merge in jb-mr2-release history after reset to jb-mr2-dev
diff --git a/dictionaries/en_GB_wordlist.combined.gz b/dictionaries/en_GB_wordlist.combined.gz
index b3e5cfc..93c5f3d 100644
--- a/dictionaries/en_GB_wordlist.combined.gz
+++ b/dictionaries/en_GB_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_US_wordlist.combined.gz b/dictionaries/en_US_wordlist.combined.gz
index 67328d8..c2421dc 100644
--- a/dictionaries/en_US_wordlist.combined.gz
+++ b/dictionaries/en_US_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_wordlist.combined.gz b/dictionaries/en_wordlist.combined.gz
index 7fc6cff..3732993 100644
--- a/dictionaries/en_wordlist.combined.gz
+++ b/dictionaries/en_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/fr_wordlist.combined.gz b/dictionaries/fr_wordlist.combined.gz
index c7c6977..7de4625 100644
--- a/dictionaries/fr_wordlist.combined.gz
+++ b/dictionaries/fr_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/ru_wordlist.combined.gz b/dictionaries/ru_wordlist.combined.gz
index 4f92805..1cfab4e 100644
--- a/dictionaries/ru_wordlist.combined.gz
+++ b/dictionaries/ru_wordlist.combined.gz
Binary files differ
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index 17d11c0..fb973f3 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -20,22 +20,24 @@
 
     <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17" />
 
-    <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.READ_USER_DICTIONARY" />
-    <uses-permission android:name="android.permission.WRITE_USER_DICTIONARY" />
-    <uses-permission android:name="android.permission.READ_CONTACTS" />
-    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
+    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
+    <uses-permission android:name="android.permission.READ_PROFILE" />
+    <uses-permission android:name="android.permission.READ_USER_DICTIONARY" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_USER_DICTIONARY" />
 
-    <application android:label="@string/aosp_android_keyboard_ime_name"
-            android:icon="@mipmap/ic_ime_settings"
+    <application android:label="@string/english_ime_name"
+            android:icon="@mipmap/ic_launcher_keyboard"
             android:killAfterRestore="false"
             android:supportsRtl="true">
 
         <service android:name="LatinIME"
-                android:label="@string/aosp_android_keyboard_ime_name"
+                android:label="@string/english_ime_name"
                 android:permission="android.permission.BIND_INPUT_METHOD">
             <intent-filter>
                 <action android:name="android.view.InputMethod" />
@@ -44,7 +46,7 @@
         </service>
 
         <service android:name=".spellcheck.AndroidSpellCheckerService"
-                 android:label="@string/aosp_spell_checker_service_name"
+                 android:label="@string/spell_checker_service_name"
                  android:permission="android.permission.BIND_TEXT_SERVICE">
             <intent-filter>
                 <action android:name="android.service.textservice.SpellCheckerService" />
@@ -53,14 +55,24 @@
         </service>
 
         <activity android:name=".setup.SetupActivity"
-                android:label="@string/aosp_android_keyboard_ime_name"
-                android:icon="@drawable/ic_setup_wizard">
+                android:label="@string/english_ime_name"
+                android:icon="@mipmap/ic_launcher_keyboard"
+                android:launchMode="singleTask"
+                android:noHistory="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
 
+        <activity android:name=".setup.SetupWizardActivity"
+                android:label="@string/english_ime_name"
+                android:clearTaskOnLaunch="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+
         <receiver android:name=".setup.LauncherIconVisibilityManager">
             <intent-filter>
                 <action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
@@ -69,7 +81,7 @@
             </intent-filter>
         </receiver>
 
-        <activity android:name="SettingsActivity" android:label="@string/aosp_android_keyboard_ime_settings"
+        <activity android:name="SettingsActivity" android:label="@string/english_ime_settings"
                   android:uiOptions="splitActionBarWhenNarrow">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -77,7 +89,7 @@
         </activity>
 
         <activity android:name="com.android.inputmethod.latin.spellcheck.SpellCheckerSettingsActivity"
-                  android:label="@string/aosp_android_spell_checker_service_settings">
+                  android:label="@string/android_spell_checker_settings">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
             </intent-filter>
@@ -123,7 +135,6 @@
 
         <activity android:name="com.android.inputmethod.dictionarypack.DictionarySettingsActivity"
                   android:label="@string/dictionary_settings_title"
-                  android:icon="@mipmap/ic_ime_settings"
                   android:theme="@android:style/Theme.Holo"
                   android:uiOptions="splitActionBarWhenNarrow">
             <intent-filter>
@@ -133,7 +144,6 @@
 
         <activity android:name="com.android.inputmethod.dictionarypack.DownloadOverMeteredDialog"
                   android:label="@string/dictionary_install_over_metered_network_prompt"
-                  android:icon="@mipmap/ic_ime_settings"
                   android:theme="@android:style/Theme.Holo">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
diff --git a/java/res/color/setup_step_action_background.xml b/java/res/color/setup_step_action_background.xml
new file mode 100644
index 0000000..79120b4
--- /dev/null
+++ b/java/res/color/setup_step_action_background.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:color="@color/setup_text_action" />
+    <item
+        android:state_pressed="true"
+        android:color="@color/setup_text_action" />
+    <item
+        android:color="@color/setup_step_background" />
+</selector>
diff --git a/java/res/color/setup_step_action_color.xml b/java/res/color/setup_step_action_color.xml
new file mode 100644
index 0000000..c53e026
--- /dev/null
+++ b/java/res/color/setup_step_action_color.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:color="@color/setup_step_background" />
+    <item
+        android:state_pressed="true"
+        android:color="@color/setup_step_background" />
+    <item
+        android:color="@color/setup_text_action" />
+</selector>
diff --git a/java/res/drawable-hdpi/ic_menu_add.png b/java/res/drawable-hdpi/ic_menu_add.png
new file mode 100644
index 0000000..4b68f52
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_menu_add.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_setup_wizard.png b/java/res/drawable-hdpi/ic_setup_wizard.png
deleted file mode 100644
index 38fca6d..0000000
--- a/java/res/drawable-hdpi/ic_setup_wizard.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/unbundled_check_01.png b/java/res/drawable-hdpi/unbundled_check_01.png
new file mode 100644
index 0000000..42cce2f
--- /dev/null
+++ b/java/res/drawable-hdpi/unbundled_check_01.png
Binary files differ
diff --git a/java/res/drawable-hdpi/unbundled_check_02.png b/java/res/drawable-hdpi/unbundled_check_02.png
new file mode 100644
index 0000000..dcd120e
--- /dev/null
+++ b/java/res/drawable-hdpi/unbundled_check_02.png
Binary files differ
diff --git a/java/res/drawable-hdpi/unbundled_earth_01.png b/java/res/drawable-hdpi/unbundled_earth_01.png
new file mode 100644
index 0000000..4a0f087
--- /dev/null
+++ b/java/res/drawable-hdpi/unbundled_earth_01.png
Binary files differ
diff --git a/java/res/drawable-hdpi/unbundled_earth_02.png b/java/res/drawable-hdpi/unbundled_earth_02.png
new file mode 100644
index 0000000..f4bd421
--- /dev/null
+++ b/java/res/drawable-hdpi/unbundled_earth_02.png
Binary files differ
diff --git a/java/res/drawable-hdpi/unbundled_key_01.png b/java/res/drawable-hdpi/unbundled_key_01.png
new file mode 100644
index 0000000..87c9e2c
--- /dev/null
+++ b/java/res/drawable-hdpi/unbundled_key_01.png
Binary files differ
diff --git a/java/res/drawable-hdpi/unbundled_key_02.png b/java/res/drawable-hdpi/unbundled_key_02.png
new file mode 100644
index 0000000..0747384
--- /dev/null
+++ b/java/res/drawable-hdpi/unbundled_key_02.png
Binary files differ
diff --git a/java/res/drawable-hdpi/unbundled_select_01.png b/java/res/drawable-hdpi/unbundled_select_01.png
new file mode 100644
index 0000000..bb3de2f
--- /dev/null
+++ b/java/res/drawable-hdpi/unbundled_select_01.png
Binary files differ
diff --git a/java/res/drawable-hdpi/unbundled_select_02.png b/java/res/drawable-hdpi/unbundled_select_02.png
new file mode 100644
index 0000000..e56fdab
--- /dev/null
+++ b/java/res/drawable-hdpi/unbundled_select_02.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_menu_add.png b/java/res/drawable-mdpi/ic_menu_add.png
new file mode 100644
index 0000000..15ffadd
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_menu_add.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_setup_wizard.png b/java/res/drawable-mdpi/ic_setup_wizard.png
deleted file mode 100644
index 66e62b8..0000000
--- a/java/res/drawable-mdpi/ic_setup_wizard.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/unbundled_check_01.png b/java/res/drawable-mdpi/unbundled_check_01.png
new file mode 100644
index 0000000..d0d02a3
--- /dev/null
+++ b/java/res/drawable-mdpi/unbundled_check_01.png
Binary files differ
diff --git a/java/res/drawable-mdpi/unbundled_check_02.png b/java/res/drawable-mdpi/unbundled_check_02.png
new file mode 100644
index 0000000..d34fa22
--- /dev/null
+++ b/java/res/drawable-mdpi/unbundled_check_02.png
Binary files differ
diff --git a/java/res/drawable-mdpi/unbundled_earth_01.png b/java/res/drawable-mdpi/unbundled_earth_01.png
new file mode 100644
index 0000000..ba60181
--- /dev/null
+++ b/java/res/drawable-mdpi/unbundled_earth_01.png
Binary files differ
diff --git a/java/res/drawable-mdpi/unbundled_earth_02.png b/java/res/drawable-mdpi/unbundled_earth_02.png
new file mode 100644
index 0000000..9c52638
--- /dev/null
+++ b/java/res/drawable-mdpi/unbundled_earth_02.png
Binary files differ
diff --git a/java/res/drawable-mdpi/unbundled_key_01.png b/java/res/drawable-mdpi/unbundled_key_01.png
new file mode 100644
index 0000000..8cd72db
--- /dev/null
+++ b/java/res/drawable-mdpi/unbundled_key_01.png
Binary files differ
diff --git a/java/res/drawable-mdpi/unbundled_key_02.png b/java/res/drawable-mdpi/unbundled_key_02.png
new file mode 100644
index 0000000..4ef6e0c
--- /dev/null
+++ b/java/res/drawable-mdpi/unbundled_key_02.png
Binary files differ
diff --git a/java/res/drawable-mdpi/unbundled_select_01.png b/java/res/drawable-mdpi/unbundled_select_01.png
new file mode 100644
index 0000000..9ab008a
--- /dev/null
+++ b/java/res/drawable-mdpi/unbundled_select_01.png
Binary files differ
diff --git a/java/res/drawable-mdpi/unbundled_select_02.png b/java/res/drawable-mdpi/unbundled_select_02.png
new file mode 100644
index 0000000..8af807b
--- /dev/null
+++ b/java/res/drawable-mdpi/unbundled_select_02.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_menu_add.png b/java/res/drawable-xhdpi/ic_menu_add.png
new file mode 100644
index 0000000..420510e
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_menu_add.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_setup_wizard.png b/java/res/drawable-xhdpi/ic_setup_wizard.png
deleted file mode 100644
index 53f70a6..0000000
--- a/java/res/drawable-xhdpi/ic_setup_wizard.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/unbundled_check_01.png b/java/res/drawable-xhdpi/unbundled_check_01.png
new file mode 100644
index 0000000..1300a7f
--- /dev/null
+++ b/java/res/drawable-xhdpi/unbundled_check_01.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/unbundled_check_02.png b/java/res/drawable-xhdpi/unbundled_check_02.png
new file mode 100644
index 0000000..7151886
--- /dev/null
+++ b/java/res/drawable-xhdpi/unbundled_check_02.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/unbundled_earth_01.png b/java/res/drawable-xhdpi/unbundled_earth_01.png
new file mode 100644
index 0000000..24b6634
--- /dev/null
+++ b/java/res/drawable-xhdpi/unbundled_earth_01.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/unbundled_earth_02.png b/java/res/drawable-xhdpi/unbundled_earth_02.png
new file mode 100644
index 0000000..59aa031
--- /dev/null
+++ b/java/res/drawable-xhdpi/unbundled_earth_02.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/unbundled_key_01.png b/java/res/drawable-xhdpi/unbundled_key_01.png
new file mode 100644
index 0000000..c8b117b
--- /dev/null
+++ b/java/res/drawable-xhdpi/unbundled_key_01.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/unbundled_key_02.png b/java/res/drawable-xhdpi/unbundled_key_02.png
new file mode 100644
index 0000000..dc2da56
--- /dev/null
+++ b/java/res/drawable-xhdpi/unbundled_key_02.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/unbundled_select_01.png b/java/res/drawable-xhdpi/unbundled_select_01.png
new file mode 100644
index 0000000..baf1e55
--- /dev/null
+++ b/java/res/drawable-xhdpi/unbundled_select_01.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/unbundled_select_02.png b/java/res/drawable-xhdpi/unbundled_select_02.png
new file mode 100644
index 0000000..ad1058e
--- /dev/null
+++ b/java/res/drawable-xhdpi/unbundled_select_02.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/ic_setup_wizard.png b/java/res/drawable-xxhdpi/ic_setup_wizard.png
deleted file mode 100644
index 6414b4f..0000000
--- a/java/res/drawable-xxhdpi/ic_setup_wizard.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable/ic_setup_finish.xml b/java/res/drawable/ic_setup_finish.xml
new file mode 100644
index 0000000..8ac8a86
--- /dev/null
+++ b/java/res/drawable/ic_setup_finish.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:drawable="@drawable/unbundled_check_01" />
+    <item
+        android:state_pressed="true"
+        android:drawable="@drawable/unbundled_check_01" />
+    <item
+        android:drawable="@drawable/unbundled_check_02" />
+</selector>
diff --git a/java/res/drawable/ic_setup_step1.xml b/java/res/drawable/ic_setup_step1.xml
new file mode 100644
index 0000000..e26afb3
--- /dev/null
+++ b/java/res/drawable/ic_setup_step1.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:drawable="@drawable/unbundled_key_01" />
+    <item
+        android:state_pressed="true"
+        android:drawable="@drawable/unbundled_key_01" />
+    <item
+        android:drawable="@drawable/unbundled_key_02" />
+</selector>
diff --git a/java/res/drawable/ic_setup_step2.xml b/java/res/drawable/ic_setup_step2.xml
new file mode 100644
index 0000000..46db293
--- /dev/null
+++ b/java/res/drawable/ic_setup_step2.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:drawable="@drawable/unbundled_select_01" />
+    <item
+        android:state_pressed="true"
+        android:drawable="@drawable/unbundled_select_01" />
+    <item
+        android:drawable="@drawable/unbundled_select_02" />
+</selector>
diff --git a/java/res/drawable/ic_setup_step3.xml b/java/res/drawable/ic_setup_step3.xml
new file mode 100644
index 0000000..4ff9fd9
--- /dev/null
+++ b/java/res/drawable/ic_setup_step3.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:drawable="@drawable/unbundled_earth_01" />
+    <item
+        android:state_pressed="true"
+        android:drawable="@drawable/unbundled_earth_01" />
+    <item
+        android:drawable="@drawable/unbundled_earth_02" />
+</selector>
diff --git a/java/res/drawable/setup_step_action_background.xml b/java/res/drawable/setup_step_action_background.xml
new file mode 100644
index 0000000..25738e3
--- /dev/null
+++ b/java/res/drawable/setup_step_action_background.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:drawable="@color/setup_text_action" />
+    <item
+        android:state_pressed="true"
+        android:drawable="@color/setup_text_action" />
+    <item
+        android:drawable="@color/setup_step_background" />
+</selector>
diff --git a/java/res/layout-land/setup_steps_screen.xml b/java/res/layout-land/setup_steps_screen.xml
new file mode 100644
index 0000000..cf8c424
--- /dev/null
+++ b/java/res/layout-land/setup_steps_screen.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:baselineAligned="false">
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="@integer/setup_title_weight_in_screen"
+        android:orientation="vertical"
+        android:layout_marginEnd="@dimen/setup_title_end_margin"
+        android:layout_marginRight="@dimen/setup_title_end_margin">
+        <include layout="@layout/setup_steps_title" />
+    </LinearLayout>
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="@integer/setup_body_weight_in_screen"
+        android:orientation="vertical">
+        <include layout="@layout/setup_steps_cards" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/java/res/layout-land/setup_welcome_screen.xml b/java/res/layout-land/setup_welcome_screen.xml
new file mode 100644
index 0000000..38aea2c
--- /dev/null
+++ b/java/res/layout-land/setup_welcome_screen.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:baselineAligned="false">
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="@integer/setup_title_weight_in_screen"
+        android:orientation="vertical"
+        android:layout_marginEnd="@dimen/setup_title_end_margin"
+        android:layout_marginRight="@dimen/setup_title_end_margin">
+        <include layout="@layout/setup_welcome_title" />
+    </LinearLayout>
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="@integer/setup_body_weight_in_screen"
+        android:orientation="vertical">
+        <include layout="@layout/setup_welcome_video" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/java/res/layout/dictionary_line.xml b/java/res/layout/dictionary_line.xml
index a8d15ab..7268cd4 100644
--- a/java/res/layout/dictionary_line.xml
+++ b/java/res/layout/dictionary_line.xml
@@ -52,33 +52,60 @@
         android:ellipsize="marquee"
         android:fadingEdge="horizontal" />
 
-    <TextView
-        android:id="@+android:id/summary"
-        android:layout_marginLeft="5dip"
-        android:layout_marginStart="5dip"
+    <FrameLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textColor="?android:attr/textColorSecondary"
-        android:maxLines="1" />
+        android:layout_marginStart="5dip"
+        android:layout_marginLeft="5dip">
 
-    <!-- <ProgressBar -->
-    <!--     android:id="@+id/dictionary_line_progress_bar" -->
-    <!--     style="@android:style/Widget.Holo.ProgressBar.Horizontal" -->
-    <!--     android:layout_width="match_parent" -->
-    <!--     android:layout_height="match_parent" -->
-    <!--     android:gravity="center" /> -->
+      <TextView
+          android:id="@+android:id/summary"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:textAppearance="?android:attr/textAppearanceSmall"
+          android:textColor="?android:attr/textColorSecondary"
+          android:maxLines="1" />
 
+      <com.android.inputmethod.dictionarypack.DictionaryDownloadProgressBar
+          android:id="@+id/dictionary_line_progress_bar"
+          style="@android:style/Widget.Holo.ProgressBar.Horizontal"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:gravity="center" />
+
+    </FrameLayout>
   </LinearLayout>
 
-  <Button
-      android:id="@+android:id/wordlist_button"
+  <com.android.inputmethod.dictionarypack.ButtonSwitcher
+      android:id="@+android:id/wordlist_button_switcher"
       android:layout_weight="0"
+      android:layout_marginStart="13dip"
+      android:layout_marginLeft="13dip"
       android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:layout_gravity="right|center_vertical"
-      android:singleLine="true"
-      android:textAppearance="?android:attr/textAppearanceMedium"
-      android:text="@string/install_dict" />
-
+      android:layout_height="wrap_content">
+    <Button
+        android:id="@+android:id/dict_install_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="right|center_vertical"
+        android:singleLine="true"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:text="@string/install_dict" />
+    <Button
+        android:id="@+android:id/dict_cancel_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="right|center_vertical"
+        android:singleLine="true"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:text="@string/cancel_download_dict" />
+    <Button
+        android:id="@+android:id/dict_delete_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="right|center_vertical"
+        android:singleLine="true"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:text="@string/delete_dict" />
+  </com.android.inputmethod.dictionarypack.ButtonSwitcher>
 </LinearLayout>
diff --git a/java/res/layout/setup_start_indicator_label.xml b/java/res/layout/setup_start_indicator_label.xml
new file mode 100644
index 0000000..33854bb
--- /dev/null
+++ b/java/res/layout/setup_start_indicator_label.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <view class="com.android.inputmethod.latin.setup.SetupStartIndicatorView$LabelView"
+        android:id="@+id/setup_start_label"
+        android:text="@string/setup_start_action"
+        android:gravity="end|center_vertical"
+        android:layout_width="0dp"
+        android:layout_weight="1.0"
+        style="@style/setupStepActionLabelStyleCommon" />
+    <view class="com.android.inputmethod.latin.setup.SetupStartIndicatorView$IndicatorView"
+        android:id="@+id/setup_start_indicator"
+        style="@style/setupStepStartIndicatorStyle" />
+</merge>
diff --git a/java/res/layout/setup_step.xml b/java/res/layout/setup_step.xml
index 26d7fe7..fa84902 100644
--- a/java/res/layout/setup_step.xml
+++ b/java/res/layout/setup_step.xml
@@ -25,34 +25,14 @@
     <TextView
         android:id="@+id/setup_step_title"
         style="@style/setupStepTitleStyle"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingTop="16dp"
-        android:paddingBottom="16dp"
-        android:paddingLeft="24dp"
-        android:paddingRight="24dp" />
+        android:paddingTop="@dimen/setup_step_vertical_padding"
+        android:paddingBottom="@dimen/setup_step_vertical_padding" />
     <TextView
         android:id="@+id/setup_step_instruction"
         style="@style/setupStepInstructionStyle"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingBottom="16dp"
-        android:paddingLeft="24dp"
-        android:paddingRight="24dp" />
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="2dp" />
+        android:paddingBottom="@dimen/setup_step_vertical_padding" />
     <TextView
         android:id="@+id/setup_step_action_label"
         style="@style/setupStepActionLabelStyle"
-        android:gravity="center_vertical"
-        android:drawablePadding="12dp"
-        android:layout_width="match_parent"
-        android:layout_height="48dp"
-        android:clickable="true"
-        android:focusable="true"
-        android:paddingLeft="12dp"
-        android:paddingStart="12dp"
-        android:paddingRight="24dp"
-        android:paddingEnd="24dp" />
+        android:layout_marginTop="@dimen/setup_step_horizontal_line_height" />
 </LinearLayout>
diff --git a/java/res/layout/setup_steps_cards.xml b/java/res/layout/setup_steps_cards.xml
new file mode 100644
index 0000000..9b03a3e
--- /dev/null
+++ b/java/res/layout/setup_steps_cards.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <LinearLayout
+        android:id="@+id/setup_step_bullets"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="@dimen/setup_step_vertical_padding"
+        android:orientation="horizontal">
+        <TextView
+            android:id="@+id/setup_step1_bullet"
+            style="@style/setupStepBulletStyle"
+            android:text="@string/setup_step1_bullet" />
+        <TextView
+            android:id="@+id/setup_step2_bullet"
+            style="@style/setupStepBulletStyle"
+            android:text="@string/setup_step2_bullet" />
+        <TextView
+            android:id="@+id/setup_step3_bullet"
+            style="@style/setupStepBulletStyle"
+            android:text="@string/setup_step3_bullet" />
+    </LinearLayout>
+    <com.android.inputmethod.latin.setup.SetupStepIndicatorView
+        android:id="@+id/setup_step_indicator"
+        style="@style/setupStepIndicatorStyle" />
+    <FrameLayout
+        android:id="@+id/setup_steps_pane"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <include
+            android:id="@+id/setup_step1"
+            layout="@layout/setup_step" />
+        <include
+            android:id="@+id/setup_step2"
+            layout="@layout/setup_step" />
+        <include
+            android:id="@+id/setup_step3"
+            layout="@layout/setup_step" />
+    </FrameLayout>
+    <TextView
+        android:id="@+id/setup_next"
+        android:text="@string/setup_next_action"
+        android:gravity="end|center_vertical"
+        style="@style/setupStepActionLabelStyleCommon"
+        android:layout_marginTop="@dimen/setup_step_horizontal_line_height" />
+    <TextView
+        android:id="@+id/setup_finish"
+        android:text="@string/setup_finish_action"
+        style="@style/setupStepActionLabelStyle"
+        android:layout_marginTop="@dimen/setup_step_horizontal_line_height" />
+</merge>
diff --git a/java/res/layout/setup_steps_screen.xml b/java/res/layout/setup_steps_screen.xml
new file mode 100644
index 0000000..1159c0a
--- /dev/null
+++ b/java/res/layout/setup_steps_screen.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+    <include layout="@layout/setup_steps_title" />
+    <include layout="@layout/setup_steps_cards" />
+</LinearLayout>
diff --git a/java/res/layout/setup_steps_title.xml b/java/res/layout/setup_steps_title.xml
new file mode 100644
index 0000000..e3694bf
--- /dev/null
+++ b/java/res/layout/setup_steps_title.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<merge xmlns: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" />
+</merge>
diff --git a/java/res/layout/setup_welcome_screen.xml b/java/res/layout/setup_welcome_screen.xml
new file mode 100644
index 0000000..44e98e2
--- /dev/null
+++ b/java/res/layout/setup_welcome_screen.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+    <include layout="@layout/setup_welcome_title" />
+    <include layout="@layout/setup_welcome_video" />
+</LinearLayout>
diff --git a/java/res/layout/setup_welcome_title.xml b/java/res/layout/setup_welcome_title.xml
new file mode 100644
index 0000000..af7053a
--- /dev/null
+++ b/java/res/layout/setup_welcome_title.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<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" />
+    <TextView
+        android:id="@+id/setup_welcome_description"
+        android:text="@string/setup_welcome_additional_description"
+        android:layout_marginTop="@dimen/setup_welcome_description_top_margin"
+        style="@style/setupWelcomeDescritpionStyle" />
+</merge>
diff --git a/java/res/layout/setup_welcome_video.xml b/java/res/layout/setup_welcome_video.xml
new file mode 100644
index 0000000..7517732
--- /dev/null
+++ b/java/res/layout/setup_welcome_video.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:paddingTop="@dimen/setup_welcome_video_top_padding"
+        android:paddingBottom="@dimen/setup_welcome_video_bottom_padding">
+        <LinearLayout
+            android:layout_weight="@integer/setup_welcome_video_weight_in_screen"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:padding="1dp"
+            android:background="@color/setup_welcome_video_margin_color" >
+            <VideoView
+                android:id="@+id/setup_welcome_video"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:background="@color/setup_background" />
+            <ImageView
+                android:id="@+id/setup_welcome_image"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:adjustViewBounds="true"
+                android:visibility="gone" />
+        </LinearLayout>
+        <View
+            android:layout_weight="@integer/setup_welcome_video_end_padding_weight_in_screen"
+            android:layout_width="0dp"
+            android:layout_height="0dp" />
+    </LinearLayout>
+    <com.android.inputmethod.latin.setup.SetupStartIndicatorView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+</merge>
diff --git a/java/res/layout/setup_wizard.xml b/java/res/layout/setup_wizard.xml
index acbbe30..87db4d0 100644
--- a/java/res/layout/setup_wizard.xml
+++ b/java/res/layout/setup_wizard.xml
@@ -18,61 +18,19 @@
 */
 -->
 
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/setup_wizard"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:fillViewport="true">
-    <RelativeLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="@color/setup_background"
-        android:paddingLeft="@dimen/setup_horizontal_padding"
-        android:paddingRight="@dimen/setup_horizontal_padding"
-        android:paddingTop="16dp"
-        android:paddingBottom="16dp">
-        <TextView
-            android:id="@+id/setup_title"
-            style="@style/setupTitleStyle"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_alignParentLeft="true"
-            android:layout_alignParentStart="true"
-            android:layout_alignParentTop="true" />
-        <LinearLayout
-            android:id="@+id/setup_step_bullets"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_below="@id/setup_title"
-            android:paddingTop="16dp"
-            android:orientation="horizontal">
-            <TextView
-                style="@style/setupStepBulletStyle"
-                android:text="@string/setup_step1_bullet" />
-            <TextView
-                style="@style/setupStepBulletStyle"
-                android:text="@string/setup_step2_bullet" />
-            <TextView
-                style="@style/setupStepBulletStyle"
-                android:text="@string/setup_step3_bullet" />
-        </LinearLayout>
-        <com.android.inputmethod.latin.setup.SetupStepIndicatorView
-            android:id="@+id/setup_step_indicator"
-            android:layout_width="match_parent"
-            android:layout_height="24dp"
-            android:layout_below="@id/setup_step_bullets" />
-        <FrameLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_below="@id/setup_step_indicator">
-            <include
-                android:id="@+id/setup_step1"
-                layout="@layout/setup_step" />
-            <include
-                android:id="@+id/setup_step2"
-                layout="@layout/setup_step" />
-            <include
-                android:id="@+id/setup_step3"
-                layout="@layout/setup_step" />
-        </FrameLayout>
-    </RelativeLayout>
-</ScrollView>
+    android:background="@color/setup_background"
+    android:paddingLeft="@dimen/setup_horizontal_padding"
+    android:paddingRight="@dimen/setup_horizontal_padding"
+    android:paddingTop="@dimen/setup_vertical_padding"
+    android:paddingBottom="@dimen/setup_vertical_padding">
+    <include
+        android:id="@+id/setup_welcome_screen"
+        layout="@layout/setup_welcome_screen" />
+    <include
+        android:id="@+id/setup_steps_screen"
+        layout="@layout/setup_steps_screen" />
+</FrameLayout>
diff --git a/java/res/layout/user_dictionary_add_word.xml b/java/res/layout/user_dictionary_add_word.xml
new file mode 100644
index 0000000..bbf9b1b
--- /dev/null
+++ b/java/res/layout/user_dictionary_add_word.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/user_dict_settings_add_dialog_top"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical" >
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" >
+
+        <com.android.internal.widget.DialogTitle
+            style="?android:attr/windowTitleStyle"
+            android:layout_width="match_parent"
+            android:layout_height="64dip"
+            android:layout_marginEnd="16dip"
+            android:layout_marginStart="16dip"
+            android:ellipsize="end"
+            android:gravity="center_vertical|start"
+            android:singleLine="true"
+            android:text="@string/user_dict_settings_add_dialog_title" />
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="2dip"
+            android:background="@android:color/holo_blue_light" />
+    </LinearLayout>
+
+    <EditText
+        android:id="@+id/user_dictionary_add_word_text"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_gravity="fill_horizontal|center_vertical"
+        android:layout_marginBottom="8dip"
+        android:layout_marginStart="8dip"
+        android:layout_marginTop="8dip"
+        android:hint="@string/user_dict_settings_add_word_hint"
+        android:imeOptions="flagNoFullscreen"
+        android:inputType="textNoSuggestions"
+        android:maxLength="@integer/user_dictionary_max_word_length" >
+
+        <requestFocus />
+    </EditText>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:divider="?android:attr/dividerHorizontal"
+        android:dividerPadding="0dip"
+        android:orientation="vertical"
+        android:showDividers="beginning" >
+
+        <LinearLayout
+            style="?android:attr/buttonBarStyle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:measureWithLargestChild="true"
+            android:orientation="horizontal" >
+
+            <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>
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/java/res/layout/user_dictionary_add_word_fullscreen.xml b/java/res/layout/user_dictionary_add_word_fullscreen.xml
new file mode 100644
index 0000000..219485b
--- /dev/null
+++ b/java/res/layout/user_dictionary_add_word_fullscreen.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/user_dict_settings_add_dialog_top"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical" >
+
+    <EditText
+        android:id="@+id/user_dictionary_add_word_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="fill_horizontal|center_vertical"
+        android:layout_marginBottom="8dip"
+        android:layout_marginStart="8dip"
+        android:layout_marginTop="8dip"
+        android:hint="@string/user_dict_settings_add_word_hint"
+        android:imeOptions="flagNoFullscreen"
+        android:inputType="textNoSuggestions"
+        android:maxLength="@integer/user_dictionary_max_word_length" >
+
+        <requestFocus />
+    </EditText>
+
+    <GridLayout
+        android:id="@+id/user_dictionary_add_word_grid"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="8dip"
+        android:layout_marginStart="8dip"
+        android:columnCount="2" >
+
+        <TextView
+            android:id="@+id/user_dictionary_add_shortcut_label"
+            style="?android:attr/textAppearanceSmall"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="start|center_vertical"
+            android:text="@string/user_dict_settings_add_shortcut_option_name" />
+
+        <EditText
+            android:id="@+id/user_dictionary_add_shortcut"
+            android:layout_width="wrap_content"
+            android:layout_gravity="fill_horizontal|center_vertical"
+            android:layout_marginBottom="8dip"
+            android:layout_marginStart="8dip"
+            android:layout_marginTop="8dip"
+            android:hint="@string/user_dict_settings_add_shortcut_hint"
+            android:imeOptions="flagNoFullscreen"
+            android:inputType="textNoSuggestions"
+            android:maxLength="@integer/user_dictionary_max_word_length" />
+
+        <TextView
+            android:id="@+id/user_dictionary_add_locale_label"
+            style="?android:attr/textAppearanceSmall"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="start|center_vertical"
+            android:text="@string/user_dict_settings_add_locale_option_name"
+            android:visibility="gone" />
+
+        <Spinner
+            android:id="@+id/user_dictionary_add_locale"
+            android:layout_width="wrap_content"
+            android:layout_gravity="fill_horizontal|center_vertical"
+            android:layout_marginBottom="8dip"
+            android:layout_marginStart="8dip"
+            android:layout_marginTop="8dip"
+            android:visibility="gone" />
+    </GridLayout>
+
+</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
new file mode 100644
index 0000000..56bad77
--- /dev/null
+++ b/java/res/layout/user_dictionary_item.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/selectableItemBackground"
+    android:gravity="center_vertical"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:paddingEnd="?android:attr/scrollbarSize" >
+
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:padding="6dip"
+        android:layout_weight="1" >
+
+        <TextView
+            android:id="@+android:id/text1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceMedium" />
+
+        <TextView
+            android:id="@+android:id/text2"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignStart="@android:id/text1"
+            android:layout_below="@android:id/text1"
+            android:maxLines="1"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorSecondary"
+            android:visibility="gone" />
+    </RelativeLayout>
+
+</LinearLayout>
diff --git a/java/res/layout/user_dictionary_preference_list_fragment.xml b/java/res/layout/user_dictionary_preference_list_fragment.xml
new file mode 100644
index 0000000..40e562c
--- /dev/null
+++ b/java/res/layout/user_dictionary_preference_list_fragment.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/transparent"
+    android:orientation="vertical" >
+
+    <ListView
+        android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="0px"
+        android:layout_weight="1"
+        android:cacheColorHint="@android:color/transparent"
+        android:clipToPadding="false"
+        android:drawSelectorOnTop="false"
+        android:paddingTop="0dip"
+        android:scrollbarAlwaysDrawVerticalTrack="true" />
+
+    <TextView
+        android:id="@android:id/empty"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:padding="5dip"
+        android:visibility="gone" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/java/res/mipmap-hdpi/ic_ime_settings.png b/java/res/mipmap-hdpi/ic_ime_settings.png
deleted file mode 100644
index 486c70d..0000000
--- a/java/res/mipmap-hdpi/ic_ime_settings.png
+++ /dev/null
Binary files differ
diff --git a/java/res/mipmap-hdpi/ic_launcher_keyboard.png b/java/res/mipmap-hdpi/ic_launcher_keyboard.png
new file mode 100644
index 0000000..36b1cca
--- /dev/null
+++ b/java/res/mipmap-hdpi/ic_launcher_keyboard.png
Binary files differ
diff --git a/java/res/mipmap-mdpi/ic_ime_settings.png b/java/res/mipmap-mdpi/ic_ime_settings.png
deleted file mode 100644
index 75f4afb..0000000
--- a/java/res/mipmap-mdpi/ic_ime_settings.png
+++ /dev/null
Binary files differ
diff --git a/java/res/mipmap-mdpi/ic_launcher_keyboard.png b/java/res/mipmap-mdpi/ic_launcher_keyboard.png
new file mode 100644
index 0000000..67ef189
--- /dev/null
+++ b/java/res/mipmap-mdpi/ic_launcher_keyboard.png
Binary files differ
diff --git a/java/res/mipmap-xhdpi/ic_ime_settings.png b/java/res/mipmap-xhdpi/ic_ime_settings.png
deleted file mode 100644
index bbf1919..0000000
--- a/java/res/mipmap-xhdpi/ic_ime_settings.png
+++ /dev/null
Binary files differ
diff --git a/java/res/mipmap-xhdpi/ic_launcher_keyboard.png b/java/res/mipmap-xhdpi/ic_launcher_keyboard.png
new file mode 100644
index 0000000..b332083
--- /dev/null
+++ b/java/res/mipmap-xhdpi/ic_launcher_keyboard.png
Binary files differ
diff --git a/java/res/mipmap-xxhdpi/ic_ime_settings.png b/java/res/mipmap-xxhdpi/ic_ime_settings.png
deleted file mode 100644
index 16fc693..0000000
--- a/java/res/mipmap-xxhdpi/ic_ime_settings.png
+++ /dev/null
Binary files differ
diff --git a/java/res/mipmap-xxhdpi/ic_launcher_keyboard.png b/java/res/mipmap-xxhdpi/ic_launcher_keyboard.png
new file mode 100644
index 0000000..acc424f
--- /dev/null
+++ b/java/res/mipmap-xxhdpi/ic_launcher_keyboard.png
Binary files differ
diff --git a/java/res/raw/empty.dict b/java/res/raw/empty.dict
index da1bf96..80ce066 100644
--- a/java/res/raw/empty.dict
+++ b/java/res/raw/empty.dict
@@ -1 +1 @@
-x±
\ No newline at end of file
+›Á:þ
\ No newline at end of file
diff --git a/java/res/raw/main_en.dict b/java/res/raw/main_en.dict
index 526761c..086874d 100644
--- a/java/res/raw/main_en.dict
+++ b/java/res/raw/main_en.dict
Binary files differ
diff --git a/java/res/raw/main_fr.dict b/java/res/raw/main_fr.dict
index 7520898..9044c7e 100644
--- a/java/res/raw/main_fr.dict
+++ b/java/res/raw/main_fr.dict
Binary files differ
diff --git a/java/res/raw/main_ru.dict b/java/res/raw/main_ru.dict
index 216ff09..7074416 100644
--- a/java/res/raw/main_ru.dict
+++ b/java/res/raw/main_ru.dict
Binary files differ
diff --git a/java/res/raw/setup_welcome_image.png b/java/res/raw/setup_welcome_image.png
new file mode 100644
index 0000000..2445915
--- /dev/null
+++ b/java/res/raw/setup_welcome_image.png
Binary files differ
diff --git a/java/res/raw/setup_welcome_video.mp4 b/java/res/raw/setup_welcome_video.mp4
new file mode 100644
index 0000000..8208525
--- /dev/null
+++ b/java/res/raw/setup_welcome_video.mp4
Binary files differ
diff --git a/java/res/values-af/strings-appname.xml b/java/res/values-af/strings-appname.xml
new file mode 100644
index 0000000..c481690
--- /dev/null
+++ b/java/res/values-af/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Android-sleutelbord (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android-speltoetser (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Android-sleutelbordinstellings (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Android-speltoetserinstellings (AOSP)"</string>
+</resources>
diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml
index 2892940..77a76b5 100644
--- a/java/res/values-af/strings.xml
+++ b/java/res/values-af/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android-sleutelbord (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android-sleutelbord-instellings (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android-speltoetser (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Android-speltoetserinstellings (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Invoeropsies"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Navorsing-loglêerbevele"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Soek kontakname op"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dubbeltik op spasiebalk voeg \'n punt in, gevolg deur \'n spasie"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Outohoofletters"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Die eerste woord van elke sin moet met \'n hoofletter begin"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Persoonlike woordeboek"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Voeg woordeboeke by"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Hoofwoordeboek"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Wys voorstelle vir korrigering"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Wys altyd"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Wys in portretmodus"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Versteek altyd"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Blokkeer aanstootlike woorde"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Moenie potensieel aanstootlike woorde voorstel nie"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Outokorrigering"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Spasiebalk en leestekens korrigeer outomaties woorde wat verkeerd gespel is"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Af"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Moet hierdie lêer regtig vir <xliff:g id="LOCALE_NAME">%s</xliff:g> geïnstalleer word?"</string>
     <string name="error" msgid="8940763624668513648">"Daar was \'n fout"</string>
     <string name="button_default" msgid="3988017840431881491">"Verstek"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Taal en invoer"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Kies invoermetode"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Welkom by <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"met Gebaar-tik"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Kom aan die gang"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Volgende stap"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Stel <xliff:g id="APPLICATION_NAME">%s</xliff:g> op"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Aktiveer <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Verifieer asseblief \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" in jou Taal- en invoerinstellings. Dit sal dit magtig om op jou toestel te loop."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> is reeds geaktiveer in jou Taal- en invoer-instellings - hierdie stap is dus klaar. Aan na die volgende een!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Aktiveer in instellings"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Skakel oor na <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Volgende, kies \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" as jou aktiewe teks-invoermetode."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Wissel invoermetodes"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Veels geluk, jy\'s gereed!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Nou kan jy in al jou gunsteling programme tik met <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Stel bykomende tale op"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Klaar"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Wys program-ikoon"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Wys program-ikoon in die lanseerpoort"</string>
     <string name="app_name" msgid="6320102637491234792">"Woordeboekverskaffer"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Woordeboekverskaffer"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Woordeboek-diens"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"\'n Woordeboek is vir <xliff:g id="LANGUAGE">%1$s</xliff:g> beskikbaar"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Druk om te hersien en af te laai"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Laai tans af: voorstelle vir <xliff:g id="LANGUAGE">%1$s</xliff:g> sal binnekort gereed wees."</string>
+    <string name="version_text" msgid="2715354215568469385">"Weergawe <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Voeg by"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Voeg by woordeboek"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Frase"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Nog opsies"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Minder opsies"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Woord:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Kortpad:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Taal:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Tik \'n woord in"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Opsionele kortpad"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Redigeer woord"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Redigeer"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Vee uit"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Jy het geen woorde in die gebruikerwoordeboek nie. Voeg \'n woord by deur die Byvoegknoppie (+) te raak."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Vir alle tale"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Nog tale…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Vee uit"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-am/strings-appname.xml b/java/res/values-am/strings-appname.xml
new file mode 100644
index 0000000..aee2e24
--- /dev/null
+++ b/java/res/values-am/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"የAndroid ቁልፍ ሰሌዳ (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android ፊደል አራሚ (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"የAndroid ቁልፍ ሰሌዳ ቅንብሮች (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"የAndroid ፊደል አራሚ ቅንብሮች (AOSP)"</string>
+</resources>
diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml
index 03a8b93..d388827 100644
--- a/java/res/values-am/strings.xml
+++ b/java/res/values-am/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"የAndroid ቁልፍ ሰሌዳ (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"የAndroid ቁልፍ ሰሌዳ ቅንብሮች (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android ፊደል አራሚ (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"የAndroid ፊደል አራሚ ቅንብሮች (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"ግቤት አማራጮች"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"የጥናት የምዝግብ ማስታወሻ ትዕዛዞች"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"የእውቅያ ስሞችን ተመልከት"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"የክፍተት አሞሌው ላይ ሁለቴ መታ ማድረግ አንድ ነጥብ እና ክፍተት አስከትሎ ያስገባል"</string>
     <string name="auto_cap" msgid="1719746674854628252">"ራስ-ሰር አቢይ ማድረግ"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"የእያንዳንዱ ዓረፍተ ነገር የመጀመሪያ ቃል በአቢይ ሆሄ ያስቀምጡ"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"የግል መዝገበ-ቃላት"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"መዝገበ ቃላቶች ጨምር"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"ዋና መዝገበ ቃላት"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"የማስተካከያ ጥቆማዎች አሳይ"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"ሁልጊዜ አሳይ"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"በቁም አቀማመጥ ሁነታ አሳይ"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"ሁልጊዜ ደብቅ"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"አፀያፊ ቃላትን አግድ"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"አጸያፊ ሊሆኑ የሚችሉ ቃላትን አትጠቁም"</string>
     <string name="auto_correction" msgid="7630720885194996950">"በራስ-ማስተካከል"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"የቦታ ቁልፍ እና ሥርዓተ ነጥብ በስህተት የተተየቡ ቃላትን  በራስሰር ያስተካክላሉ ።"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"ውጪ"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"እውን ይሄ ፋይል ለ<xliff:g id="LOCALE_NAME">%s</xliff:g> ይጫን?"</string>
     <string name="error" msgid="8940763624668513648">"ስህተት ተከስቶ ነበር"</string>
     <string name="button_default" msgid="3988017840431881491">"ነባሪ"</string>
-    <string name="language_settings" msgid="1671153053201809031">"ቋንቋ እና ግቤት"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"የግቤት ስልት ይምረጡ"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"እንኳን ወደ <xliff:g id="APPLICATION_NAME">%s</xliff:g> በደህና መጡ"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"በጣት ምልክት መተየብ"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"ጀምር"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"ቀጣይ ደረጃ"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>ን በማዋቀር ላይ"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>ን ያንቁ"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"እባክዎ «<xliff:g id="APPLICATION_NAME">%s</xliff:g>»ን በቋንቋ እና ግቤት ቅንብሮችዎ ውስጥ ያረጋግጡት። ይሄ እሱ በመሣሪያዎ ላይ እንዲሄድ ይፈቅድለታል።"</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> አስቀድሞ በእርስዎ ቋንቋ እና ግቤት ቅንብሮች ውስጥ ነቅቷል፣ ስለዚህ ይህ ደረጃ ተከናውኗል። ቀጣዩ ላይ!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"በቅንብሮች ውስጥ ያንቁ"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"ወደ <xliff:g id="APPLICATION_NAME">%s</xliff:g> ይቀይሩ"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"በመቀጠል «<xliff:g id="APPLICATION_NAME">%s</xliff:g>»ን እንደ የጽሑፍ ግቤት ስልትዎ ይምረጡት።"</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"የግቤት ስልቶችን ቀያይር"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"እንኳን ደስ አለዎት፣ በቃ ጨርሰዋል!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"አሁን በሁሉም ተወዳጅ መተግበሪያዎችዎ ላይ በ<xliff:g id="APPLICATION_NAME">%s</xliff:g> መተየብ ይችላሉ።"</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"ተጨማሪ ቋንቋዎችን ያዋቅሩ"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"ጨርሷል"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"የመተግበሪያ አዶ አሳይ"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"የመተግበሪያ አዶውን በማስጀመሪያው ውስጥ አሳይ"</string>
     <string name="app_name" msgid="6320102637491234792">"የመዝገበ-ቃላት አቅራቢ"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"የመዝገበ-ቃላት አቅራቢ"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"የመዝገበ-ቃላት አገልግሎት"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"መዝገበ-ቃላት ለ<xliff:g id="LANGUAGE">%1$s</xliff:g> ይገኛል"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"ለመገምገምና ለማውረድ ይጫኑ"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"በማውረድ ላይ፦ የ<xliff:g id="LANGUAGE">%1$s</xliff:g> ጥቆማ አስተያየቶች በቅርቡ ዝግጁ ይሆናሉ።"</string>
+    <string name="version_text" msgid="2715354215568469385">"ሥሪት <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"አክል"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"ወደ መዝገበ-ቃላት አክል"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"ሐረግ"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"ተጨማሪ አማራጮች"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"ያነሱ አማራጮች"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"እሺ"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"ቃል፦"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"አቋራጭ፦"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"ቋንቋ፦"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"አንድ ቃል ይተይቡ"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"አማራጭ አቋራጭ"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"ቃሉን አርትዕ"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"አርትዕ"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"ሰርዝ"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"በተጠቃሚ መዝገበ-ቃላት ውስጥ ምንም ቃላቶች የሉዎትም። የአክል (+) አዝራሩን በመንካት ቃል ማከል ይችላሉ።"</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"ለሁሉም ቋንቋዎች"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"ተጨማሪ ቋንቋዎች…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"ሰርዝ"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ሀለሐመሠረሰሸቀበቨተቸኀነኘአከኸወዐዘዠየደጀገጠጨጰጸፀፈፐ"</string>
 </resources>
diff --git a/java/res/values-ar/strings-appname.xml b/java/res/values-ar/strings-appname.xml
new file mode 100644
index 0000000..d5176d0
--- /dev/null
+++ b/java/res/values-ar/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"لوحة مفاتيح Android ‏(AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"المدقق الإملائي في Android‏ (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"إعدادات لوحة مفاتيح Android‏ (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"إعدادات المدقق الإملائي في Android‏ (AOSP)"</string>
+</resources>
diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml
index c16f224..3f0add8 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"لوحة مفاتيح Android ‏(AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"إعدادات لوحة مفاتيح Android‏ (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"المدقق الإملائي في Android‏ (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"إعدادات المدقق الإملائي في Android‏ (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"خيارات الإرسال"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"أوامر سجلات البحث"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"بحث في أسماء جهات الاتصال"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"يؤدي النقر نقرًا مزدوجًا على مفتاح المسافة إلى إدخال نقطة متبوعة بمسافة"</string>
     <string name="auto_cap" msgid="1719746674854628252">"أحرف كبيرة تلقائيًا"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"كتابة الحرف الأول من كل جملة بحرف كبير."</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"القاموس الشخصي"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"القواميس الإضافية"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"القاموس الرئيسي"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"عرض اقتراحات التصحيح"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"عرض دومًا"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"العرض في وضع رأسي"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"إخفاء دومًا"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"حظر الكلمات المسيئة"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"عدم اقتراح كلمات محتمل أن تكون مسيئة"</string>
     <string name="auto_correction" msgid="7630720885194996950">"التصحيح التلقائي"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"تؤدي المسافة والترقيم إلى تصحيح الكلمات المكتوبة بشكل غير صحيح"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"إيقاف"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"هل تريد حقًا تثبيت هذا الملف للغة <xliff:g id="LOCALE_NAME">%s</xliff:g>؟"</string>
     <string name="error" msgid="8940763624668513648">"حدث خطأ"</string>
     <string name="button_default" msgid="3988017840431881491">"الافتراضية"</string>
-    <string name="language_settings" msgid="1671153053201809031">"اللغة والإدخال"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"اختيار أسلوب الإدخال"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"مرحبا بكم في <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"مع الكتابة بالإشارة"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"بدء الاستخدام"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"الخطوة التالية"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"إعداد <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"تمكين <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"يُرجى تحديد \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" في إعدادات اللغة والإدخال حيث يسمح هذا الإعداد بتشغيله على جهازك."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"تم تمكين <xliff:g id="APPLICATION_NAME">%s</xliff:g> من قبل في إعدادات اللغة والإدخال، وبالتالي هذه الخطوة مكتملة. انتقل إلى الخطوة التالية."</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"تمكين في الإعدادات"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"التبديل إلى <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"بعد ذلك، حدد \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" باعتباره أسلوب إدخال النص النشط."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"تبديل أساليب الإدخال"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"تهانينا، بهذا تكون قد انتهيت من الإعداد"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"الآن أصبح بإمكانك الكتابة في جميع تطبيقاتك المفضلة باستخدام <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"تهيئة اللغات الإضافية"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"إنهاء العملية"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"عرض رمز التطبيق"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"عرض رمز التطبيق في المشغل"</string>
     <string name="app_name" msgid="6320102637491234792">"مقدم القاموس"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"مقدم القاموس"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"خدمة القاموس"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"هناك قاموس متوفر للغة <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"اضغط للمراجعة والتنزيل"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"جارٍ التنزيل: ستتوفر اقتراحات للغة <xliff:g id="LANGUAGE">%1$s</xliff:g> بعد قليل."</string>
+    <string name="version_text" msgid="2715354215568469385">"الإصدار <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"إضافة"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"إضافة إلى القاموس"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"عبارة"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"خيارات أكثر"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"خيارات أقل"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"موافق"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"الكلمة:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"الاختصار:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"اللغة:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"اكتب كلمة"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"اختصار اختياري"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"تعديل كلمة"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"تعديل"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"حذف"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"ليست لديك أية كلمات في قاموس المستخدم. يمكنك إضافة كلمة من خلال لمس الزر \"إضافة\" (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"لجميع اللغات"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"مزيد من اللغات..."</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"حذف"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" أ ب ت ث ج ح خ د ذ ر ز س ش ص ض ط ظ ع غ ف ق ك ل م ن ه و ي"</string>
 </resources>
diff --git a/java/res/values-be/strings-appname.xml b/java/res/values-be/strings-appname.xml
new file mode 100644
index 0000000..2f9593b
--- /dev/null
+++ b/java/res/values-be/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Клавіятура Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Iнструмент праверкi правапiсу для Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Налады клавіятуры Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Налады інструмента праверкі правапісу для Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-be/strings.xml b/java/res/values-be/strings.xml
index eca24eb..6aa48ef 100644
--- a/java/res/values-be/strings.xml
+++ b/java/res/values-be/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Клавіятура Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Налады клавіятуры Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Iнструмент праверкi правапiсу для Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Налады інструмента праверкі правапісу для Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Параметры ўводу"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Каманды гiсторыя даследаванняў"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Шукаць імёны кантактаў"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Падвойнае нацiсканне на прабел ўстаўляе iнтэрвал з наступным прабелам"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Аўтаматычна рабіць вялікія літары"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Пісаць з загалоўнай літары першае слова ў кожным сказе"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Персанальны слоўнік"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Дадатковыя слоўнікі"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Асноўны слоўнік"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Паказаць прапановы на выпраўленне"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Заўсёды паказваць"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Паказаць у партрэтным рэжыме"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Заўседы хаваць"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Блакіраваць абразлівыя словы"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Не прапануйце патэнцыяльна абразлівых слоў"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Аўтавыпраўленне"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Прабелы і пунктуацыйныя знакі дазваляюць аўтаматычна выпраўляць памылкова ўведзеныя словы"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Адключаны"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Сапраўды ўсталяваць гэты файл на мове: <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Была памылка"</string>
     <string name="button_default" msgid="3988017840431881491">"Па змаўчанні"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Мова і ўвод"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Выберыце метад уводу"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Вітаем у прыкладанні <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"з уводам жэстамі"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Пачаць"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Далей"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Наладка прыкладання <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Уключыць прыкладанне <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Праверце прыкладанне \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" на сваёй мове і параметры ўводу. Гэта дасць магчымасць дазволіць яму працаваць на вашай прыладзе."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"Прыкладанне <xliff:g id="APPLICATION_NAME">%s</xliff:g> ужо ўключана для вашай мовы і параметраў уводу, так што гэты крок зроблены. Пераходзім да наступнага!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Уключыць у наладах"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Пераключыцца на прыкладанне <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Выберыце \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" як актыўны метад уводу тэксту."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Пераключэнне метадаў уводу"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Усё гатова!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Цяпер вы можаце ўводзіць ўсе свае любімыя прыкладанні з iмем <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Наладка дадатковых моў"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Гатова"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Паказаць значок прыкладання"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Паказаць значок прыкладання ў панэлi запуску"</string>
     <string name="app_name" msgid="6320102637491234792">"Пастаўшчык слоўніка"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Пастаўшчык слоўніка"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Слоўнік"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Слоўнік для мовы \"<xliff:g id="LANGUAGE">%1$s</xliff:g>\""</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Нацiснiце, каб прагледзець i спампаваць"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Загрузка: прапановы для мовы \"<xliff:g id="LANGUAGE">%1$s</xliff:g>\" хутка з\'явяцца."</string>
+    <string name="version_text" msgid="2715354215568469385">"Версія <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Дадаць"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Дадаць у слоўнік"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Выраз"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Дадатковыя параметры"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Асн. параметры"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OК"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Слова:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Шлях хуткага доступу:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Мова:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Увядзіце слова"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Дадатковы цэтлiк"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Рэдагаваць слова"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Рэдагаваць"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Выдаліць"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"У вашым карыстальніцкім слоўніку няма ніводнага слова. Вы можаце дадаваць словы, дакранаючыся да кнопкі \"+\" у пункце меню \"Дадаць\"."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Для ўсіх моў"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Іншыя мовы..."</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Выдаліць"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-bg/strings-appname.xml b/java/res/values-bg/strings-appname.xml
new file mode 100644
index 0000000..f82163e
--- /dev/null
+++ b/java/res/values-bg/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Клавиатура на Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Програма за правописна проверка за Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Настройки на клавиатурата на Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Настройки на програмата за правописна проверка за Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml
index 3a9b579..2fb2cd6 100644
--- a/java/res/values-bg/strings.xml
+++ b/java/res/values-bg/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Клавиатура на Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Настройки на клавиатурата на Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Програма за правописна проверка за Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Настройки на програмата за правописна проверка за Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Опции за въвеждане"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Команди за рег. файл за проучвания"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Търсене на имена"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Двукр. докосване на клав. за интервал вмъква точка, следвана от интервал"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Автоматично поставяне на главни букви"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Правене на първата дума от всяко изречение главна"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Личен речник"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Добавени речници"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Основен речник"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Показване на предложения за поправка"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Винаги да се показва"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Показване във вертикална ориентация"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Винаги да се скрива"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Блокиране на обидни думи"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Без потенциално обидни думи"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Автоматична поправка"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Клавишът за интервал и пунктуация авт. поправя сгрешени думи"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Изкл."</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Наистина ли да се инсталира този файл за <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Възникна грешка"</string>
     <string name="button_default" msgid="3988017840431881491">"Стандартни"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Език и въвеждане"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Избор на метод на въвеждане"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Добре дошли в/ъв <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"с въвеждане чрез жест"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Първи стъпки"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Следваща стъпка"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Настройване на <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Активирайте <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Поставете отметка за <xliff:g id="APPLICATION_NAME">%s</xliff:g> в „Език и въвеждане“. Така ще упълномощите приложението да се изпълнява на устройството."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"Вече активирахте <xliff:g id="APPLICATION_NAME">%s</xliff:g> в настройките си за език и въвеждане, така че тази стъпка е изпълнена. Преминете към следващата!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Активиране в настройките"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Превключете към <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"След това изберете <xliff:g id="APPLICATION_NAME">%s</xliff:g> като активен метод на въвеждане на текст."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Превключване на методите на въвеждане"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Поздравления! Вече сте готови!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Вече можете да пишете във всичките си любими приложения посредством <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Конфигуриране на допълнителни езици"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Край"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Показв. на иконата на прилож."</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Показване на иконата на приложението в стартовия панел"</string>
     <string name="app_name" msgid="6320102637491234792">"Доставчик на речника"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Доставчик на речника"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Услуга за речник"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"За <xliff:g id="LANGUAGE">%1$s</xliff:g> е налице речник"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Натиснете, за да прегледате и изтеглите"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Изтегля се: Предложенията за <xliff:g id="LANGUAGE">%1$s</xliff:g> ще бъдат готови скоро."</string>
+    <string name="version_text" msgid="2715354215568469385">"Версия <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Добавяне"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Добавяне в речника"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Фраза"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Още опции"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"По-малко опции"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Дума:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Пряк път:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Език:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Напишете дума"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Незадължителен пряк път"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Редактиране на дума"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Редактиране"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Изтриване"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Нямате думи в потребителския речник. Добавете, като докоснете бутона за добавяне (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"За всички езици"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Още езици…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Изтриване"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЪЮЯ"</string>
 </resources>
diff --git a/java/res/values-ca/strings-appname.xml b/java/res/values-ca/strings-appname.xml
new file mode 100644
index 0000000..4abf513
--- /dev/null
+++ b/java/res/values-ca/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Teclat d\'Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Corrector ortogràfic d\'Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Configuració del teclat d\'Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Configuració del corrector ortogràfic d\'Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index 3670a98..beb5e56 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Teclat d\'Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Configuració del teclat d\'Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Corrector ortogràfic d\'Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Configuració del corrector ortogràfic d\'Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opcions d\'entrada"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Recerca d\'ordres de reg."</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cerca noms de contactes"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Picar dues vegades la barra d\'espai insereix punt i espai blanc"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Majúscules automàtiques"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Posa en majúscula la primera paraula de cada frase"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Diccionari personal"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Diccionaris complementaris"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Diccionari principal"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Suggeriments de correcció"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostra sempre"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Mostra en mode vertical"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Amaga sempre"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Bloqueja paraules ofensives"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"No suggereixis paraules potencialment ofensives"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Correcció automàtica"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Prémer tecla d\'espai o punt. per corregir errors"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desactiva"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Realment vols instal·lar aquest fitxer per a <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"S\'ha produït un error"</string>
     <string name="button_default" msgid="3988017840431881491">"Predeterminat"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Idioma i introducció de text"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Selecció de mètodes d\'introducció"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Et donem la benvinguda a <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"amb Escriptura gestual"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Comença"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Pas següent"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"S\'està configurant <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Activa <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Selecciona \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" a la configuració d\'Idioma i introducció de text perquè es pugui executar al teu dispositiu."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"L\'aplicació <xliff:g id="APPLICATION_NAME">%s</xliff:g> ja està activada per a la teva Configuració d\'idioma i d\'introducció de text. Pots passar al següent."</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Activa a la configuració"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Canvi a <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"A continuació, selecciona \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" com a mètode d\'introducció de text actiu."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Canvia els mètodes d\'introducció"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Enhorabona, ja has acabat!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Ara ja pots escriure en totes les teves aplicacions preferides amb <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Configura altres idiomes"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Finalitzat"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Mostra la icona de l\'aplicació"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Mostra la icona de l\'aplicació al menú d\'aplicacions"</string>
     <string name="app_name" msgid="6320102637491234792">"Proveïdor de diccionaris"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Proveïdor de diccionaris"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Servei de diccionari"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Hi ha un diccionari disponible per a l\'idioma: <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Prem per revisar-lo i per baixar-lo"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Baixada: els suggeriments per a <xliff:g id="LANGUAGE">%1$s</xliff:g> estaran disponibles ben aviat."</string>
+    <string name="version_text" msgid="2715354215568469385">"Versió <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Afegeix"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Afegeix al diccionari"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Frase"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Més opcions"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Menys opcions"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"D\'acord"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Paraula:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Drecera:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Idioma:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Escriu una paraula"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Drecera opcional"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Edició de la paraula"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Edita"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Suprimeix"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"No tens cap paraula al diccionari de l\'usuari. Per afegir una paraula, toca el botó Afegeix (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Per a tots els idiomes"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Més idiomes..."</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Suprimeix"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-cs/strings-appname.xml b/java/res/values-cs/strings-appname.xml
new file mode 100644
index 0000000..bea9740
--- /dev/null
+++ b/java/res/values-cs/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Klávesnice Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Kontrola pravopisu Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Nastavení klávesnice Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Nastavení kontroly pravopisu Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index bd64761..f2a5c21 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Klávesnice Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Nastavení klávesnice Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Kontrola pravopisu Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Nastavení kontroly pravopisu Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti zadávání textu a dat"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Příkazy vývoj. protokolu"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Vyhledat kontakty"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dvojím klepnutím na mezerník vložíte tečku následovanou mezerou."</string>
     <string name="auto_cap" msgid="1719746674854628252">"Velká písmena automaticky"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Kapitalizace prvního slova každé věty"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Osobní slovník"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Doplňkové slovníky"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Hlavní slovník"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Zobrazit návrhy oprav"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vždy zobrazovat"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Zobrazovat v režimu na výšku"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Vždy skrývat"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Blokovat nevhodná slova"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Nenavrhovat potenciálně nevhodná slova"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Automatické opravy"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Stisknutím mezerníku a interpunkce se automaticky opravují chybně napsaná slova"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Vypnuto"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Chcete nainstalovat tento soubor pro jazyk <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Došlo k chybě"</string>
     <string name="button_default" msgid="3988017840431881491">"Výchozí"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Jazyk a zadávání"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Výběr metody zadávání dat"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Vítá vás <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"s psaním gesty"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Začínáme"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Další krok"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Nastavení aplikace <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Zapnutí aplikace <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"V nastavení Jazyk a zadávání zaškrtněte aplikaci <xliff:g id="APPLICATION_NAME">%s</xliff:g>, povolíte tak její spuštění."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"Aplikace <xliff:g id="APPLICATION_NAME">%s</xliff:g> je již v nastaveních jazyka a vstupu zapnuta, a tento krok je proto již proveden. Pokračujme dalším."</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Aktivovat v nastavení"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Přepnutí na aplikaci <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Poté vyberte jako aktivní metodu zadávání textu možnost <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Přepnout metody zadávání"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Gratulujeme, vše je připraveno."</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Nyní můžete ve všech svých oblíbených aplikacích psát pomocí aplikace <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Nakonfigurovat další jazyky"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Hotovo"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Zobrazit ikonu aplikace"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Zobrazí ikonu aplikace ve spouštěči"</string>
     <string name="app_name" msgid="6320102637491234792">"Poskytovatel slovníku"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Poskytovatel slovníku"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Služba slovníku"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Je k dispozici slovník pro jazyk <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Stisknutím zkontrolujete a stáhnete"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Stahování: návrhy pro jazyk <xliff:g id="LANGUAGE">%1$s</xliff:g> budou brzy k dispozici."</string>
+    <string name="version_text" msgid="2715354215568469385">"Verze <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Přidat"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Přidat do slovníku"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Fráze"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Více možností"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Méně možností"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Slovo:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Zkratka:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Jazyk:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Napište slovo."</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Volitelná zkratka"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Upravit slovo"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Upravit"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Smazat"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"V uživatelském slovníku nejsou žádná slova. Slovo můžete přidat stisknutím tlačítka Přidat (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Pro všechny jazyky"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Další jazyky…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Smazat"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCČDEFGHChIJKLMNOPQRŘSŠTUVWXYZŽ"</string>
 </resources>
diff --git a/java/res/values-da/strings-appname.xml b/java/res/values-da/strings-appname.xml
new file mode 100644
index 0000000..4db49c7
--- /dev/null
+++ b/java/res/values-da/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Android-tastatur (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android-stavekontrol (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Indstillinger for Android-tastatur (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Indstillinger for Android-stavekontrol (AOSP)"</string>
+</resources>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index 5642917..5838592 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android-tastatur (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Indstillinger for Android-tastatur (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android-stavekontrol (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Indstillinger for Android-stavekontrol (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Indstillinger for input"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Forskningslogkommandoer"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Slå kontaktnavne op"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"To tryk på mellemrumstasten indsætter et punktum og et mellemrum"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Skriv aut. med stort"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Skriv det første ord i hver sætning med stort"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Personlig ordbog"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Tillægsordbøger"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Hovedordbog"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Vis rettelsesforslag"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vis altid"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Vis i portræt"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Skjul altid"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Bloker stødende ord"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Foreslå ikke potentielt stødende ord"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Automatisk rettelse"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Mellemrumstast og tegnsætning retter automatisk forkerte ord"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Fra"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Er du klar til at installere denne fil til <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Der opstod en fejl"</string>
     <string name="button_default" msgid="3988017840431881491">"Standard"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Sprog og input"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Vælg inputmetode"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Velkommen til <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"med Berøringsinput"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Kom godt i gang"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Næste trin"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> konfigureres"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Aktivér <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Markér \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" i Sprog og inputindstillinger. Dermed får appen tilladelse til at køre på din enhed."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> er allerede aktiveret i indstillingerne for dit sprog og dine input, så dette skridt er udført. Videre til det næste!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Aktivér i Indstillinger"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Skift til <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Dernæst skal du vælge \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" som din aktive sms-indtastningsmetode."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Skift indtastningsmetode"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Så er du klar."</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Nu kan du skrive i alle dine favoritapps med <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Konfigurer flere sprog"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Afslut"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Vis appikon"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Vis appikon på applikationsliste"</string>
     <string name="app_name" msgid="6320102637491234792">"Dictionary Provider"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Dictionary Provider"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Ordbogstjeneste"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Der er en tilgængelig ordbog for <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Tryk for at gennemgå og downloade"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Downloader: Der vil snart være forslag klar på <xliff:g id="LANGUAGE">%1$s</xliff:g>."</string>
+    <string name="version_text" msgid="2715354215568469385">"Version <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Tilføj"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Føj til ordbog"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Sætning"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Flere muligheder"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Færre muligh."</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Ord:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Genvej:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Sprog:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Skriv et ord"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Valgfri genvej"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Rediger ord"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Rediger"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Slet"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Du har ikke nogen ord i brugerordbogen. Du kan tilføje et ord ved at trykke på knappen Tilføj (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"For alle sprog"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Flere sprog..."</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Slet"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-de/strings-appname.xml b/java/res/values-de/strings-appname.xml
new file mode 100644
index 0000000..2073f55
--- /dev/null
+++ b/java/res/values-de/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Android-Tastatur (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android-Rechtschreibprüfung (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Android-Tastatureinstellungen (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Einstellungen für die Android-Rechtschreibprüfung (AOSP)"</string>
+</resources>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index 1c9bec2..4e7bbe0 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android-Tastatur (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android-Tastatureinstellungen (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android-Rechtschreibprüfung (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Einstellungen für die Android-Rechtschreibprüfung (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Eingabeoptionen"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Forschungsprotokollbefehle"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontaktnamen prüfen"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Für Punkt plus Leerzeichen zweimal auf die Leertaste tippen"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Autom. Groß-/Kleinschreibung"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Das erste Wort jedes Satzes großschreiben"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Mein Wörterbuch"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Erweiterte Wörterbücher"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Allgemeines Wörterbuch"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Änderungsvorschläge"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Immer anzeigen"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Im Hochformat anzeigen"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Nie anzeigen"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Anstößige Wörter sperren"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Keine potenziell anstößigen Wörter vervollständigen"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Autokorrektur"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Korrektur fehlerhafter Wörter durch Leertaste und Satzzeichen"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Aus"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Möchten Sie diese Datei für <xliff:g id="LOCALE_NAME">%s</xliff:g> installieren?"</string>
     <string name="error" msgid="8940763624668513648">"Es ist ein Fehler aufgetreten"</string>
     <string name="button_default" msgid="3988017840431881491">"Standard"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Sprache &amp; Eingabe"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Eingabemethode wählen"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Willkommen bei <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"mit Bewegungseingabe"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Jetzt starten"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Nächster Schritt"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> einrichten"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> aktivieren"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Aktivieren Sie \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" unter \"Sprache &amp; Eingabe\". Damit wird die App auf Ihrem Gerät autorisiert."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> ist bereits in Ihren Sprach- und Eingabeeinstellungen aktiviert. Fahren Sie mit dem nächsten Schritt fort."</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"In den Einstellungen aktivieren"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Zu <xliff:g id="APPLICATION_NAME">%s</xliff:g> wechseln"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Wählen Sie dann \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" als Ihre aktive Texteingabemethode."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Eingabemethode wechseln"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Die Einrichtung ist abgeschlossen"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Jetzt können Sie in allen Ihren Lieblings-Apps über <xliff:g id="APPLICATION_NAME">%s</xliff:g> Text eingeben."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Weitere Sprachen konfigurieren"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Fertig"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"App-Symbol anzeigen"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"App-Symbol in der Übersicht anzeigen"</string>
     <string name="app_name" msgid="6320102637491234792">"Wörterbuchbereitstellung"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Wörterbuchbereitstellung"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Wörterbuch"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Es ist ein Wörterbuch für <xliff:g id="LANGUAGE">%1$s</xliff:g> verfügbar."</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Zum Lesen und Herunterladen drücken"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Download wurde gestartet: Vorschläge für <xliff:g id="LANGUAGE">%1$s</xliff:g> sind in Kürze bereit."</string>
+    <string name="version_text" msgid="2715354215568469385">"Version <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Hinzufügen"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Zum Wörterbuch hinzufügen"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Wortgruppe"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Weitere Optionen"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Weniger Optionen"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Wort:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Tastaturkürzel:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Sprache:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Wort eingeben"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Optionales Tastaturkürzel"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Wort bearbeiten"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Bearbeiten"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Löschen"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Es sind noch keine Wörter in Ihrem Wörterbuch vorhanden. Sie können Wörter hinzufügen, indem Sie das \"+\"-Symbol berühren."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Für alle Sprachen"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Weitere Sprachen..."</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Löschen"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-el/strings-appname.xml b/java/res/values-el/strings-appname.xml
new file mode 100644
index 0000000..4fe7276
--- /dev/null
+++ b/java/res/values-el/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Πληκτρολόγιο Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Ορθογραφικός έλεγχος Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Ρυθμίσεις πληκτρολογίου Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Ρυθμίσεις ορθογραφικού ελέγχου Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index fad1cf5..700e1fc 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Πληκτρολόγιο Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Ρυθμίσεις πληκτρολογίου Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Ορθογραφικός έλεγχος Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Ρυθμίσεις ορθογραφικού ελέγχου Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Επιλογές εισόδου"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Έρευνα εντολών καταγραφής"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Αναζήτηση ονομάτων επαφών"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Το διπλό πάτημα του πλήκτρ.διαστ. εισάγει μια τελεία και ένα κενό"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Αυτόματη χρήση κεφαλαίων"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Χρήση κεφαλαίου στην πρώτη λέξη κάθε πρότασης"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Προσωπικό λεξικό"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Πρόσθετα λεξικά"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Κύριο λεξικό"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Εμφάνιση προτάσεων διόρθωσης"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Να εμφανίζεται πάντα"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Εμφάνιση σε κατακόρυφο προσανατολισμό"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Πάντα απόκρυψη"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Αποκλεισμός υβριστικών λέξεων"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Να μην προτείνονται πιθανώς προσβλητικές λέξεις"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Αυτόματη διόρθωση"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Τα πλήκτρα διαστήματος και στίξης διορθ. αυτόμ. λάθος λέξεις"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Απενεργοποίηση"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Να εγκατασταθεί όντως αυτό το αρχείο για <xliff:g id="LOCALE_NAME">%s</xliff:g>;"</string>
     <string name="error" msgid="8940763624668513648">"Παρουσιάστηκε σφάλμα."</string>
     <string name="button_default" msgid="3988017840431881491">"Προεπιλογή"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Γλώσσα και εισαγωγή"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Επιλογή μεθόδου εισαγωγής"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Καλώς ορίσατε στο <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"με Πληκτρολόγηση με κίνηση"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Έναρξη"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Επόμενο βήμα"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Ρύθμιση της εφαρμογής <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Ενεργοποιήστε την εφαρμογή <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Επιλέξτε \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" στις ρυθμίσεις Γλώσσα και εισαγωγή, για να εκτελεστεί στη συσκευή."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"Η εφαρμογή <xliff:g id="APPLICATION_NAME">%s</xliff:g> έχει ενεργοποιηθεί στις Ρυθμίσεις γλώσσας και εισαγωγής, συνεπώς το βήμα έχει ολοκληρωθεί. Πάμε στο επόμενο!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Ενεργοποίηση στις Ρυθμίσεις"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Μετάβαση στην εφαρμογή <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Στη συνέχεια, επιλέξτε \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" ως την ενεργή μέθοδο εισαγωγής κειμένου."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Εναλλαγή μεθόδων εισαγωγής"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Συγχαρητήρια, είστε έτοιμοι!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Πλέον μπορείτε να πληκτρολογήσετε όλες τις αγαπημένες σας εφαρμογές με το <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Διαμόρφωση πρόσθετων γλωσσών"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Ολοκληρώθηκε"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Εμφάνιση εικονιδίου εφαρμογής"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Εμφάνιση εικονιδίου εφαρμογής στο πρόγραμμα εκκίνησης"</string>
     <string name="app_name" msgid="6320102637491234792">"Παροχέας λεξικού"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Παροχέας λεξικού"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Υπηρεσία λεξικού"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Υπάρχει διαθέσιμο λεξικό για τα <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Πατήστε για έλεγχο και λήψη"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Λήψη: Οι προτάσεις για τα <xliff:g id="LANGUAGE">%1$s</xliff:g> θα είναι έτοιμες σύντομα."</string>
+    <string name="version_text" msgid="2715354215568469385">"Έκδοση <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Προσθήκη"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Προσθήκη στο λεξικό"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Φράση"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Περισσ. επιλογές"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Λιγότ. επιλογές"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Λέξη:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Συντόμευση:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Γλώσσα:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Πληκτρολογήστε μια λέξη"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Προαιρετική συντόμευση"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Επεξεργασία λέξης"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Επεξεργασία"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Διαγραφή"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Δεν υπάρχουν λέξεις καταχωρισμένες στο λεξικό χρήστη. Προσθέστε μια λέξη πατώντας το κουμπί Προσθήκη (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Για όλες τις γλώσσες"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Περισσότερες γλώσσες…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Διαγραφή"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-en-rGB/strings-appname.xml b/java/res/values-en-rGB/strings-appname.xml
new file mode 100644
index 0000000..5ad5eae
--- /dev/null
+++ b/java/res/values-en-rGB/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Android Keyboard (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android Spell Checker (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Android Keyboard Settings (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Android Spell Checker Settings (AOSP)"</string>
+</resources>
diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml
index c0b9ede..4bc6d9c 100644
--- a/java/res/values-en-rGB/strings.xml
+++ b/java/res/values-en-rGB/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android Keyboard (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android Keyboard Settings (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android Spell Checker (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Android Spell Checker Settings (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Input options"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Research Log Commands"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Look up contact names"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Double tap on spacebar inserts a full stop followed by a space"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Auto-capitalisation"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Capitalise the first word of each sentence"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Personal dictionary"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Add-on dictionaries"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Main dictionary"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Show correction suggestions"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Always show"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Show in portrait mode"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Always hide"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Block offensive words"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Do not suggest potentially offensive words"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Auto-correction"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Correct mistyped words automatically with spacebar and punctuation"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Off"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Really install this file for <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"There was an error"</string>
     <string name="button_default" msgid="3988017840431881491">"Default"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Language &amp; input"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Choose input method"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Welcome to <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"with Gesture Typing"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Get started"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Next step"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Setting up <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Enable <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Please tick \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" in your Language &amp; input settings. This will authorise it to run on your device."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> is already enabled in your Language &amp; input settings, so this step is done. On to the next one!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Enable in Settings"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Switch to <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Next, select \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" as your active text-input method."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Switch input methods"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Congratulations, you\'re all set!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Now you can type in all your favourite apps with <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Configure additional languages"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Finished"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Show app icon"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Display application icon in the launcher"</string>
     <string name="app_name" msgid="6320102637491234792">"Dictionary Provider"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Dictionary Provider"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Dictionary Service"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"A dictionary is available for <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Press to review and download"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Downloading: suggestions for <xliff:g id="LANGUAGE">%1$s</xliff:g> will be ready soon."</string>
+    <string name="version_text" msgid="2715354215568469385">"Version <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Add"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Add to dictionary"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Phrase"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"More options"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Fewer options"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Word:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Shortcut:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Language:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Type a word"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Optional shortcut"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Edit word"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Edit"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Delete"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"You don\'t have any words in the user dictionary. Add a word by touching the Add (+) button."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"For all languages"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"More languages…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Delete"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-es-rUS/strings-appname.xml b/java/res/values-es-rUS/strings-appname.xml
new file mode 100644
index 0000000..f0560d9
--- /dev/null
+++ b/java/res/values-es-rUS/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Teclado de Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Corrector ortográfico de Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Configuración del teclado de Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Config. del corrector ortográfico de Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index e1024e5..ee429fc 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Teclado de Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Configuración del teclado de Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Corrector ortográfico de Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Configuración del corrector ortográfico de Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opciones de entrada"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Comandos registro invest."</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Buscar nombres contactos"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Tocar dos veces la barra espaciadora inserta un punto y espacio."</string>
     <string name="auto_cap" msgid="1719746674854628252">"Mayúsculas automáticas"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Escribe con mayúscula la primera palabra de cada frase"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Diccionario personal"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Diccionarios complementarios"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Diccionario principal"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Mostrar sugerencias de correcciones"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar siempre"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Mostrar en modo de retrato"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ocultar siempre"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Bloquear palabras ofensivas"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"No sugerir posibles palabras ofensivas"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Corrección automática"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"La barra espaciadora y las teclas de puntuación insertan automáticamente la palabra corregida"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desactivado"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"¿Realmente quieres instalar este archivo para <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Se produjo un error."</string>
     <string name="button_default" msgid="3988017840431881491">"Predeterminado"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Teclado e idioma"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Seleccionar método de entrada"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Te damos la bienvenida a <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"con escritura gestual"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Comenzar"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Siguiente paso"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Configurando <xliff:g id="APPLICATION_NAME">%s</xliff:g>…"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Habilitar <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Marca \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" en Teclado e idioma para permitir que se ejecute en el dispositivo."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"La aplicación <xliff:g id="APPLICATION_NAME">%s</xliff:g> ya está habilitada en Teclado e idioma, por lo que este paso está finalizado. Pasemos al siguiente."</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Habilitar en Configuración"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Cambiar a <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"A continuación, selecciona \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" como tu método de entrada de texto activo."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Cambiar métodos de entrada"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"¡Felicitaciones, ya terminaste!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Ahora puedes escribir en todas las aplicaciones que quieras con <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Configurar otros idiomas"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Listo"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Mostrar ícono de aplicación"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Mostrar ícono de aplicación en el selector"</string>
     <string name="app_name" msgid="6320102637491234792">"Proveedor de diccionarios"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Proveedor de diccionarios"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Servicio de diccionarios"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Hay un diccionario disponible de <xliff:g id="LANGUAGE">%1$s</xliff:g>."</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Pulsar para opinar y descargar"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Descargando: las sugerencias de <xliff:g id="LANGUAGE">%1$s</xliff:g> estarán disponibles en breve."</string>
+    <string name="version_text" msgid="2715354215568469385">"Versión <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Agregar"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Agregar al diccionario"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Frase"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Más opciones"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Menos opciones"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"Aceptar"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Palabra:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Acceso directo:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Idioma:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Escribe una palabra."</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Acceso directo opcional"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Editar palabra"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Editar"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Eliminar"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"El diccionario del usuario no contiene ninguna palabra. Para agregar una palabra, toca el botón Agregar (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Para todos los idiomas"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Más idiomas"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Eliminar"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-es/strings-appname.xml b/java/res/values-es/strings-appname.xml
new file mode 100644
index 0000000..e0059f9
--- /dev/null
+++ b/java/res/values-es/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Teclado Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Corrector de Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Ajustes del teclado de Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Ajustes del corrector de Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index 44d264d..c4e7268 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Teclado Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Ajustes del teclado de Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Corrector de Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Ajustes del corrector de Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opciones entrada texto"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Comandos registro investigación"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Nombres de contactos"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Si tocas dos veces el espacio, se inserta un punto seguido de un espacio"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Mayúsculas automáticas"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Escribir la primera letra de cada palabra en mayúscula"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Diccionario personal"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Diccionarios complementarios"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Diccionario principal"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Sugerencias de correcciones"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar siempre"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Mostrar en modo vertical"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ocultar siempre"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Bloquear palabras ofensivas"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"No sugerir palabras potencialmente ofensivas"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Autocorrección"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Pulsar la tecla de espacio o punto para corregir errores"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desactivada"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"¿Seguro que quieres instalar este archivo para <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Se ha producido un error"</string>
     <string name="button_default" msgid="3988017840431881491">"Predeterminado"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Idioma e introducción de texto"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Selecciona un método de entrada"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Te damos la bienvenida a <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"con escritura gestual"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Empezar"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Siguiente paso"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Configurando <xliff:g id="APPLICATION_NAME">%s</xliff:g>..."</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Habilitar <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Selecciona <xliff:g id="APPLICATION_NAME">%s</xliff:g> en Idioma e introducción de texto para que pueda usarse en tu dispositivo."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"La aplicación <xliff:g id="APPLICATION_NAME">%s</xliff:g> ya está habilitada en los ajustes de idioma e introducción de texto. Ahora pasemos al siguiente paso."</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Habilitar en Ajustes"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Cambiar a <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"A continuación, selecciona <xliff:g id="APPLICATION_NAME">%s</xliff:g> como método de introducción de texto activo."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Alternar métodos de entrada"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"¡Enhorabuena, has terminado!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Ahora puedes escribir en todas tus aplicaciones favoritas con <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Configura otros idiomas"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Listo"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Mostrar icono de aplicación"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Mostrar icono de aplicación en menú de aplicaciones"</string>
     <string name="app_name" msgid="6320102637491234792">"Proveedor del diccionario"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Proveedor del diccionario"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Servicio de diccionario"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Hay un diccionario disponible de <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Pulsa para comprobar y descargar"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Descargando: las sugerencias de <xliff:g id="LANGUAGE">%1$s</xliff:g> estarán disponibles en breve."</string>
+    <string name="version_text" msgid="2715354215568469385">"Versión <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Añadir"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Añadir al diccionario"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Frase"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Más opciones"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Menos opciones"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"Aceptar"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Palabra:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Acceso directo:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Idioma:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Escribe una palabra"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Acceso directo opcional"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Editar palabra"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Editar"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Eliminar"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"No tienes ninguna palabra en el diccionario del usuario. Toca el botón Añadir (+) para añadir una palabra."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Para todos los idiomas"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Más idiomas…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Eliminar"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNÑOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-et/strings-appname.xml b/java/res/values-et/strings-appname.xml
new file mode 100644
index 0000000..bbc13d2
--- /dev/null
+++ b/java/res/values-et/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Androidi klaviatuur (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Androidi õigekirjakontroll (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Androidi klaviatuuri seaded (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Androidi õigekirjakontrolli seaded (AOSP)"</string>
+</resources>
diff --git a/java/res/values-et/strings.xml b/java/res/values-et/strings.xml
index fc85472..5e92d1c 100644
--- a/java/res/values-et/strings.xml
+++ b/java/res/values-et/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Androidi klaviatuur (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Androidi klaviatuuri seaded (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Androidi õigekirjakontroll (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Androidi õigekirjakontrolli seaded (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Sisestusvalikud"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Uuringulogi käsud"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontakti nimede kontroll."</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Tühikuklahvi kaks korda puudutades sisestatakse punkt ja tühik"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Automaatne suurtähtede kasutamine"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Iga lause esimese sõna kirjutamine suure algustähega"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Isiklik sõnastik"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Pistiksõnaraamatud"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Peamine sõnaraamat"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Kuva parandussoovitusi"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Kuva alati"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Kuva vertikaalrežiimis"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Peida alati"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Blokeeri solvavad sõnad"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Ära soovita potentsiaals. solvavaid sõnu"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Automaatparandus"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Tühik ja kirjavahemärgid parand. autom. kirjavigadega sõnad"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Väljas"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Kas soovite tõesti installida faili lokaadile <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Ilmnes viga"</string>
     <string name="button_default" msgid="3988017840431881491">"Vaikeväärtus"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Keeled ja sisestamine"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Valige sisestusmeetod"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Tere tulemast rakendusse <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"joonistusega sisestamisega"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Alustamine"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Järgmine toiming"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Rakenduse <xliff:g id="APPLICATION_NAME">%s</xliff:g> seadistamine"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Lubage <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Märkige oma keele ja sisestamise seadetes rakendus „<xliff:g id="APPLICATION_NAME">%s</xliff:g>”. See lubab rakenduse käitamise teie seadmes."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> on teie keele- ja sisestusseadetes juba lubatud, seega on see toiming tehtud. Asuge järgmise toimingu juurde."</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Luba seadetes"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Minge üle rakendusele <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Järgmisena valige aktiivseks tekstisisestusmeetodiks rakendus „<xliff:g id="APPLICATION_NAME">%s</xliff:g>”."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Vaheta sisestusmeetodeid"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Õnnitleme. Kõik on valmis!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Nüüd saate rakendusega <xliff:g id="APPLICATION_NAME">%s</xliff:g> sisestada kõikides oma lemmikrakendustes."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Seadista lisakeeled"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Lõpetatud"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Kuva rakenduse ikoon"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Rakenduse ikooni kuvamine käivitajas"</string>
     <string name="app_name" msgid="6320102637491234792">"Sõnastikupakkuja"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Sõnastikupakkuja"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Sõnastikuteenus"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Sõnastik on <xliff:g id="LANGUAGE">%1$s</xliff:g> keele jaoks saadaval"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Vajutage ülevaatamiseks ja allalaadimiseks"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Allalaadimine: <xliff:g id="LANGUAGE">%1$s</xliff:g> keele soovitused on varsti saadaval."</string>
+    <string name="version_text" msgid="2715354215568469385">"Versioon <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Lisa"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Sõnaraamatusse lisamine"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Fraas"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Rohkem valikuid"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Vähem valikuid"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Sõna:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Otsetee:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Keel:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Sisestage sõna"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Valikuline otsetee"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Sõna muutmine"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Muuda"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Kustuta"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Teie kasutaja sõnaraamatus ei ole ühtegi sõna. Sõna saate lisada, puudutades nuppu Lisa (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Kõikides keeltes"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Rohkem keeli ..."</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Kustuta"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSŠZŽTUVWÕÄÖÜXY"</string>
 </resources>
diff --git a/java/res/values-fa/strings-appname.xml b/java/res/values-fa/strings-appname.xml
new file mode 100644
index 0000000..38234c2
--- /dev/null
+++ b/java/res/values-fa/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"صفحه کلید Android ‏(AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"غلط‌گیر Android ‏(AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"تنظیمات صفحه کلید Android ‏(AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"تنظیمات غلط‌گیر Android ‏(AOSP)"</string>
+</resources>
diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml
index e7f5c96..502355e 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"صفحه کلید Android ‏(AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"تنظیمات صفحه کلید Android ‏(AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"غلط‌گیر Android ‏(AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"تنظیمات غلط‌گیر Android ‏(AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"گزینه‌های ورودی"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"فرمان‌های گزارش‌گیری پژوهش"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"جستجوی نام مخاطبین"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"با دوبار ضربه روی دکمه فاصله نقطه با یک فاصله بعد آن درج می‌شود"</string>
     <string name="auto_cap" msgid="1719746674854628252">"بزرگ‌کردن خودکار حروف"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"بزرگ‌نویسی کلمه اول هر جمله"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"فرهنگ لغت شخصی"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"فرهنگ‌های لغت افزودنی"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"فرهنگ‌ لغت اصلی"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"نمایش پیشنهادات تصحیح"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"همیشه نمایش داده شود"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"نمایش در حالت عمودی"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"همیشه پنهان شود"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"عدم نمایش کلمات توهین‌آمیز"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"کلمات توهین‌آمیز احتمالی پیشنهاد نشود"</string>
     <string name="auto_correction" msgid="7630720885194996950">"تصحیح خودکار"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"کلید فاصله و علائم نگارشی به صورت خودکار کلماتی را که غلط تایپ شده‌اند تصحیح می‌کنند"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"خاموش"</string>
@@ -175,8 +174,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"این فایل واقعاً برای <xliff:g id="LOCALE_NAME">%s</xliff:g> نصب شود؟"</string>
     <string name="error" msgid="8940763624668513648">"خطایی روی داد"</string>
     <string name="button_default" msgid="3988017840431881491">"پیش‌فرض"</string>
-    <string name="language_settings" msgid="1671153053201809031">"زبان و ورودی"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"انتخاب روش ورودی"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"به <xliff:g id="APPLICATION_NAME">%s</xliff:g> خوش آمدید"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"با ورودی اشاره‌ای"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"شروع به کار"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"مرحله بعد"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"راه‌اندازی <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"فعال‌سازی <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"لطفاً «<xliff:g id="APPLICATION_NAME">%s</xliff:g>» را در تنظیمات زبان و ورودی خود علامت بزنید. این کار مجوز اجرای آن در دستگاه شما است."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> در حال حاضر در تنظیمات زبان و ورودی شما فعال است، بنابراین این مرحله انجام شده است. به مرحله بعد بروید!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"فعال‌سازی در تنظیمات"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"جابجایی به <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"در مرحله بعد، با انتخاب «<xliff:g id="APPLICATION_NAME">%s</xliff:g>» به عنوان روش ورودی نوشتار خود آن را فعال نمایید."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"تغییر روش‌های ورودی"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"تبریک می‌گوییم، اکنون کاملاً آماده هستید!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"اکنون می‌توانید در همه برنامه‌های دلخواه خود با <xliff:g id="APPLICATION_NAME">%s</xliff:g> تایپ کنید."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"پیکربندی زبان‌های دیگر"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"تمام شد"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"نمایش نماد برنامه"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"نمایش نماد برنامه در راه‌انداز"</string>
     <string name="app_name" msgid="6320102637491234792">"ارائه‌دهنده فرهنگ لغت"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"ارائه‌دهنده فرهنگ لغت"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"سرویس فرهنگ لغت"</string>
@@ -207,4 +222,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"یک فرهنگ لغت برای <xliff:g id="LANGUAGE">%1$s</xliff:g> موجود است"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"برای مرور و دانلود فشار دهید"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"دانلود لغات پیشنهادی برای <xliff:g id="LANGUAGE">%1$s</xliff:g> به زودی شروع می‌شود."</string>
+    <string name="version_text" msgid="2715354215568469385">"نسخه <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"افرودن"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"افزودن به فرهنگ‌ لغت"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"عبارت"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"گزینه‌های بیشتر"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"گزینه‌های کمتر"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"تأیید"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"کلمه:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"میانبر:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"زبان:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"یک کلمه تایپ کنید"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"میانبر اختیاری"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"ویرایش کلمه"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"ویرایش"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"حذف"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"کلمه‌ای در فرهنگ لغت کاربر شما موجود نیست. می‌توانید با لمس کردن دکمه افزودن (+) یک کلمه را اضافه کنید."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"برای همه زبان‌ها"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"زبان‌های بیشتر…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"حذف"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-fi/strings-appname.xml b/java/res/values-fi/strings-appname.xml
new file mode 100644
index 0000000..8ee3ae4
--- /dev/null
+++ b/java/res/values-fi/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Android-näppäimistö (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android-oikoluku (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Android-näppäimistön asetukset (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Android-oikoluvun asetukset (AOSP)"</string>
+</resources>
diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml
index 6499136..563d3ae 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android-näppäimistö (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android-näppäimistön asetukset (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android-oikoluku (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Android-oikoluvun asetukset (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Syöttövalinnat"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Tutkimuslokin komennot"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Hae kontaktien nimiä"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Välilyönnin kaksoisnapautus lisää tekstiin pisteen ja välilyönnin"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Automaattiset isot kirjaimet"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Kirjoita jokaisen lauseen ensimmäinen sana isolla alkukirjaimella"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Oma sanakirja"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Lisäsanakirjat"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Pääsanakirja"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Näytä korjausehdotukset"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Näytä aina"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Näytä pystyasennossa"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Piilota aina"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Estä loukkaavat sanat"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Älä ehdota mahdollisesti loukkaavia sanoja"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Autom. korjaus"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Välilyönnit ja välimerkit korjaavat väärinkirjoitetut sanat automaattisesti"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Älä käytä"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Haluatko asentaa tämä tiedoston kielelle <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Tapahtui virhe"</string>
     <string name="button_default" msgid="3988017840431881491">"Oletusarvot"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Kieli ja syöttötapa"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Valitse syöttötapa"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Tervetuloa käyttämään sovellusta <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"sekä piirtokirjoitus"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Aloita"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Seuraava vaihe"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Sovelluksen <xliff:g id="APPLICATION_NAME">%s</xliff:g> asetukset"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Ota <xliff:g id="APPLICATION_NAME">%s</xliff:g> käyttöön"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Valitse <xliff:g id="APPLICATION_NAME">%s</xliff:g> kieli- ja syöttötapa-asetuksissa, mikä valtuuttaa sovel. laitteellesi."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> on jo käytössä Kieli- ja syöttöasetuksissa, joten tämä vaihe on tehty. Siirrytään eteenpäin!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Ota käyttöön asetuksissa"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Siirry sovellukseen <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Valitse <xliff:g id="APPLICATION_NAME">%s</xliff:g> käytössä olevaksi tekstinsyöttötavaksi."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Vaihda syöttötapaa"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Onneksi olkoon, valmista tuli!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Nyt voit kirjoittaa kaikkiin lempisovelluksiisi sovelluksen <xliff:g id="APPLICATION_NAME">%s</xliff:g> avulla."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Määritä lisää kieliä"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Valmis"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Näytä sovelluskuvake"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Näytä sovelluskuvake käynnistysohjelmassa."</string>
     <string name="app_name" msgid="6320102637491234792">"Sanakirjan tarjoaja"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Sanakirjan tarjoaja"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Sanakirjapalvelu"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Kielen <xliff:g id="LANGUAGE">%1$s</xliff:g> sanakirja on saatavilla"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Paina tätä, jos haluat tarkastella kohdetta tai ladata sen"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Ladataan: pian ehdotuksia näytetään kielellä <xliff:g id="LANGUAGE">%1$s</xliff:g>."</string>
+    <string name="version_text" msgid="2715354215568469385">"Versio <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Lisää"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Lisää sanakirjaan"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Ilmaus"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Lisäasetukset"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Väh. vaihtoeht."</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Sana:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Pikanäppäin"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Kieli:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Kirjoita sana"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Valinnainen pikanäppäin"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Muokkaa sanaa"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Muokkaa"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Poista"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Käyttäjän sanakirjassa ei ole yhtään sanaa. Voit lisätä sanan koskettamalla Lisää (+) -painiketta."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Kaikille kielille"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Lisää kieliä…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Poista"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ"</string>
 </resources>
diff --git a/java/res/values-fr/strings-appname.xml b/java/res/values-fr/strings-appname.xml
new file mode 100644
index 0000000..d45e239
--- /dev/null
+++ b/java/res/values-fr/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Clavier Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Correcteur orthographique Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Paramètres du clavier Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Paramètres du correcteur orthographique Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index 4c6ae70..9198a7d 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Clavier Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Paramètres du clavier Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Correcteur orthographique Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Paramètres du correcteur orthographique Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Options de saisie"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Commandes journaux rech."</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Rechercher noms contacts"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Appuyez deux fois sur la barre d\'espace pour insérer un point et un espace."</string>
     <string name="auto_cap" msgid="1719746674854628252">"Majuscules auto"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Majuscule au premier mot de chaque phrase"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Dictionnaire personnel"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dictionnaires complémentaires"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Dictionnaire principal"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Suggestions de correction"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Toujours afficher"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Afficher en mode Portrait"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Toujours masquer"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Bloquer les termes choquants"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Pas de termes potentiellement choquants"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Correction auto"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Corriger autom. orthographe (pression sur barre espace/signes ponctuation)"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Désactiver"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Installer ce fichier pour la langue \"<xliff:g id="LOCALE_NAME">%s</xliff:g>\" ?"</string>
     <string name="error" msgid="8940763624668513648">"Une erreur s\'est produite"</string>
     <string name="button_default" msgid="3988017840431881491">"Par défaut"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Langue et saisie"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Sélectionnez le mode de saisie"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Bienvenue dans <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"avec la saisie gestuelle"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Commencer"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Étape suivante"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Configurer <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Activer <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Sous \"Langue et saisie\", cochez \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" pour autoriser son exécution sur l\'appareil."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"L\'application \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" est déjà activée dans vos paramètres \"Langue et saisie\". Passez à l\'étape suivante."</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Activer le clavier dans les paramètres"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Basculer vers <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Sélectionnez ensuite \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" comme mode de saisie actif."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Changer de mode de saisie"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Félicitations, l\'opération est terminée"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Avec <xliff:g id="APPLICATION_NAME">%s</xliff:g>, vous pouvez saisir du texte dans toutes vos applications préférées."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Configurer des langues supplémentaires"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"OK"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Afficher icône application"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Afficher l\'icône de l\'application dans le lanceur"</string>
     <string name="app_name" msgid="6320102637491234792">"Fournisseur de dictionnaires"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Fournisseur de dictionnaires"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Service de dictionnaires"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Un dictionnaire est disponible en <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Appuyez ici pour consulter et télécharger le dictionnaire."</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"En cours de téléchargement. Des suggestions pour la langue suivante seront bientôt disponibles : <xliff:g id="LANGUAGE">%1$s</xliff:g>."</string>
+    <string name="version_text" msgid="2715354215568469385">"Version <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Ajouter"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Ajouter au dictionnaire"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Expression"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Plus d\'options"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Moins d\'options"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Mot :"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Raccourci :"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Langue :"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Saisissez un mot"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Raccourci facultatif"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Modifier le mot"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Modifier"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Supprimer"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Votre dictionnaire personnel ne contient aucun mot. Ajoutez un mot en appuyant sur le bouton d\'ajout (\"+\")."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Pour toutes les langues"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Plus de langues…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Supprimer"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-h1200dp-port/setup-dimens-large-tablet-port.xml b/java/res/values-h1200dp-port/setup-dimens-large-tablet-port.xml
new file mode 100644
index 0000000..bc7928d
--- /dev/null
+++ b/java/res/values-h1200dp-port/setup-dimens-large-tablet-port.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Setup wizard dimensions for large-tablet/portrait. -->
+    <dimen name="setup_title_text_size">72sp</dimen>
+    <dimen name="setup_welcome_description_text_size">38sp</dimen>
+    <dimen name="setup_step_bullet_text_size">24sp</dimen>
+    <dimen name="setup_step_triangle_indicator_height">24dp</dimen>
+    <dimen name="setup_step_indicator_height">24dp</dimen>
+    <dimen name="setup_step_title_text_size">24sp</dimen>
+    <dimen name="setup_step_instruction_text_size">18sp</dimen>
+    <dimen name="setup_step_action_text_size">20sp</dimen>
+    <dimen name="setup_vertical_padding">96dp</dimen>
+    <dimen name="setup_horizontal_padding">144dp</dimen>
+    <dimen name="setup_step_action_height">48dp</dimen>
+    <dimen name="setup_step_horizontal_padding">24dp</dimen>
+    <dimen name="setup_step_horizontal_padding_half">12dp</dimen>
+    <dimen name="setup_step_vertical_padding">16dp</dimen>
+    <dimen name="setup_step_horizontal_line_height">2dp</dimen>
+    <integer name="setup_title_weight_in_screen">40</integer>
+    <integer name="setup_body_weight_in_screen">60</integer>
+    <dimen name="setup_title_end_margin">24dp</dimen>
+    <dimen name="setup_welcome_description_top_margin">12dp</dimen>
+    <dimen name="setup_welcome_video_top_padding">24dp</dimen>
+    <dimen name="setup_welcome_video_bottom_padding">24dp</dimen>
+    <integer name="setup_welcome_video_weight_in_screen">70</integer>
+    <integer name="setup_welcome_video_end_padding_weight_in_screen">30</integer>
+</resources>
diff --git a/java/res/values-h330dp-land/setup-dimens-large-phone-land.xml b/java/res/values-h330dp-land/setup-dimens-large-phone-land.xml
new file mode 100644
index 0000000..aebf6d2
--- /dev/null
+++ b/java/res/values-h330dp-land/setup-dimens-large-phone-land.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Setup wizard dimensions for large-phone/landscape. -->
+    <dimen name="setup_title_text_size">40sp</dimen>
+    <dimen name="setup_welcome_description_text_size">22sp</dimen>
+    <dimen name="setup_step_bullet_text_size">22sp</dimen>
+    <dimen name="setup_step_triangle_indicator_height">24dp</dimen>
+    <dimen name="setup_step_indicator_height">24dp</dimen>
+    <dimen name="setup_step_title_text_size">20sp</dimen>
+    <dimen name="setup_step_instruction_text_size">16sp</dimen>
+    <dimen name="setup_step_action_text_size">18sp</dimen>
+    <dimen name="setup_vertical_padding">16dp</dimen>
+    <dimen name="setup_horizontal_padding">16dp</dimen>
+    <dimen name="setup_step_action_height">48dp</dimen>
+    <dimen name="setup_step_horizontal_padding">24dp</dimen>
+    <dimen name="setup_step_horizontal_padding_half">12dp</dimen>
+    <dimen name="setup_step_vertical_padding">16dp</dimen>
+    <dimen name="setup_step_horizontal_line_height">2dp</dimen>
+    <integer name="setup_title_weight_in_screen">40</integer>
+    <integer name="setup_body_weight_in_screen">60</integer>
+    <dimen name="setup_title_end_margin">24dp</dimen>
+    <dimen name="setup_welcome_description_top_margin">10dp</dimen>
+    <dimen name="setup_welcome_video_top_padding">0dp</dimen>
+    <dimen name="setup_welcome_video_bottom_padding">12dp</dimen>
+    <integer name="setup_welcome_video_weight_in_screen">70</integer>
+    <integer name="setup_welcome_video_end_padding_weight_in_screen">30</integer>
+</resources>
diff --git a/java/res/values-h520dp-land/setup-dimens-small-tablet-land.xml b/java/res/values-h520dp-land/setup-dimens-small-tablet-land.xml
new file mode 100644
index 0000000..aedf79f
--- /dev/null
+++ b/java/res/values-h520dp-land/setup-dimens-small-tablet-land.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Setup wizard dimensions for small-tablet/landscape. -->
+    <dimen name="setup_title_text_size">60sp</dimen>
+    <dimen name="setup_welcome_description_text_size">32sp</dimen>
+    <dimen name="setup_step_bullet_text_size">24sp</dimen>
+    <dimen name="setup_step_triangle_indicator_height">24dp</dimen>
+    <dimen name="setup_step_indicator_height">24dp</dimen>
+    <dimen name="setup_step_title_text_size">24sp</dimen>
+    <dimen name="setup_step_instruction_text_size">18sp</dimen>
+    <dimen name="setup_step_action_text_size">20sp</dimen>
+    <dimen name="setup_vertical_padding">32dp</dimen>
+    <dimen name="setup_horizontal_padding">96dp</dimen>
+    <dimen name="setup_step_action_height">48dp</dimen>
+    <dimen name="setup_step_horizontal_padding">24dp</dimen>
+    <dimen name="setup_step_horizontal_padding_half">12dp</dimen>
+    <dimen name="setup_step_vertical_padding">16dp</dimen>
+    <dimen name="setup_step_horizontal_line_height">2dp</dimen>
+    <integer name="setup_title_weight_in_screen">50</integer>
+    <integer name="setup_body_weight_in_screen">50</integer>
+    <dimen name="setup_title_end_margin">24dp</dimen>
+    <dimen name="setup_welcome_description_top_margin">12dp</dimen>
+    <dimen name="setup_welcome_video_top_padding">0dp</dimen>
+    <dimen name="setup_welcome_video_bottom_padding">24dp</dimen>
+    <integer name="setup_welcome_video_weight_in_screen">80</integer>
+    <integer name="setup_welcome_video_end_padding_weight_in_screen">20</integer>
+</resources>
diff --git a/java/res/values-h540dp-port/setup-dimens-large-phone-port.xml b/java/res/values-h540dp-port/setup-dimens-large-phone-port.xml
new file mode 100644
index 0000000..6d66f46
--- /dev/null
+++ b/java/res/values-h540dp-port/setup-dimens-large-phone-port.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Setup wizard dimensions for large-phone/portrait. -->
+    <dimen name="setup_title_text_size">48sp</dimen>
+    <dimen name="setup_welcome_description_text_size">26sp</dimen>
+    <dimen name="setup_step_bullet_text_size">22sp</dimen>
+    <dimen name="setup_step_triangle_indicator_height">24dp</dimen>
+    <dimen name="setup_step_indicator_height">24dp</dimen>
+    <dimen name="setup_step_title_text_size">20sp</dimen>
+    <dimen name="setup_step_instruction_text_size">16sp</dimen>
+    <dimen name="setup_step_action_text_size">18sp</dimen>
+    <dimen name="setup_vertical_padding">8dp</dimen>
+    <dimen name="setup_horizontal_padding">16dp</dimen>
+    <dimen name="setup_step_action_height">48dp</dimen>
+    <dimen name="setup_step_horizontal_padding">24dp</dimen>
+    <dimen name="setup_step_horizontal_padding_half">12dp</dimen>
+    <dimen name="setup_step_vertical_padding">16dp</dimen>
+    <dimen name="setup_step_horizontal_line_height">2dp</dimen>
+    <integer name="setup_title_weight_in_screen">40</integer>
+    <integer name="setup_body_weight_in_screen">60</integer>
+    <dimen name="setup_title_end_margin">24dp</dimen>
+    <dimen name="setup_welcome_description_top_margin">6dp</dimen>
+    <dimen name="setup_welcome_video_top_padding">12dp</dimen>
+    <dimen name="setup_welcome_video_bottom_padding">12dp</dimen>
+    <integer name="setup_welcome_video_weight_in_screen">70</integer>
+    <integer name="setup_welcome_video_end_padding_weight_in_screen">30</integer>
+</resources>
diff --git a/java/res/values-h720dp-land/setup-dimens-large-tablet-land.xml b/java/res/values-h720dp-land/setup-dimens-large-tablet-land.xml
new file mode 100644
index 0000000..e22b741
--- /dev/null
+++ b/java/res/values-h720dp-land/setup-dimens-large-tablet-land.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Setup wizard dimensions for large-tablet/landscape. -->
+    <dimen name="setup_title_text_size">72sp</dimen>
+    <dimen name="setup_welcome_description_text_size">38sp</dimen>
+    <dimen name="setup_step_bullet_text_size">24sp</dimen>
+    <dimen name="setup_step_triangle_indicator_height">24dp</dimen>
+    <dimen name="setup_step_indicator_height">24dp</dimen>
+    <dimen name="setup_step_title_text_size">24sp</dimen>
+    <dimen name="setup_step_instruction_text_size">18sp</dimen>
+    <dimen name="setup_step_action_text_size">20sp</dimen>
+    <dimen name="setup_vertical_padding">96dp</dimen>
+    <dimen name="setup_horizontal_padding">160dp</dimen>
+    <dimen name="setup_step_action_height">48dp</dimen>
+    <dimen name="setup_step_horizontal_padding">24dp</dimen>
+    <dimen name="setup_step_horizontal_padding_half">12dp</dimen>
+    <dimen name="setup_step_vertical_padding">16dp</dimen>
+    <dimen name="setup_step_horizontal_line_height">2dp</dimen>
+    <integer name="setup_title_weight_in_screen">50</integer>
+    <integer name="setup_body_weight_in_screen">50</integer>
+    <dimen name="setup_title_end_margin">24dp</dimen>
+    <dimen name="setup_welcome_description_top_margin">12dp</dimen>
+    <dimen name="setup_welcome_video_top_padding">0dp</dimen>
+    <dimen name="setup_welcome_video_bottom_padding">24dp</dimen>
+    <integer name="setup_welcome_video_weight_in_screen">80</integer>
+    <integer name="setup_welcome_video_end_padding_weight_in_screen">20</integer>
+</resources>
diff --git a/java/res/values-h800dp-port/setup-dimens-small-tablet-port.xml b/java/res/values-h800dp-port/setup-dimens-small-tablet-port.xml
new file mode 100644
index 0000000..86cf3a0
--- /dev/null
+++ b/java/res/values-h800dp-port/setup-dimens-small-tablet-port.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Setup wizard dimensions for small-tablet/portrait. -->
+    <dimen name="setup_title_text_size">72sp</dimen>
+    <dimen name="setup_welcome_description_text_size">36sp</dimen>
+    <dimen name="setup_step_bullet_text_size">24sp</dimen>
+    <dimen name="setup_step_triangle_indicator_height">24dp</dimen>
+    <dimen name="setup_step_indicator_height">24dp</dimen>
+    <dimen name="setup_step_title_text_size">24sp</dimen>
+    <dimen name="setup_step_instruction_text_size">18sp</dimen>
+    <dimen name="setup_step_action_text_size">20sp</dimen>
+    <dimen name="setup_vertical_padding">32dp</dimen>
+    <dimen name="setup_horizontal_padding">64dp</dimen>
+    <dimen name="setup_step_action_height">48dp</dimen>
+    <dimen name="setup_step_horizontal_padding">24dp</dimen>
+    <dimen name="setup_step_horizontal_padding_half">12dp</dimen>
+    <dimen name="setup_step_vertical_padding">16dp</dimen>
+    <dimen name="setup_step_horizontal_line_height">2dp</dimen>
+    <integer name="setup_title_weight_in_screen">40</integer>
+    <integer name="setup_body_weight_in_screen">60</integer>
+    <dimen name="setup_title_end_margin">24dp</dimen>
+    <dimen name="setup_welcome_description_top_margin">12dp</dimen>
+    <dimen name="setup_welcome_video_top_padding">24dp</dimen>
+    <dimen name="setup_welcome_video_bottom_padding">24dp</dimen>
+    <integer name="setup_welcome_video_weight_in_screen">70</integer>
+    <integer name="setup_welcome_video_end_padding_weight_in_screen">30</integer>
+</resources>
diff --git a/java/res/values-hi/strings-appname.xml b/java/res/values-hi/strings-appname.xml
new file mode 100644
index 0000000..59e33e6
--- /dev/null
+++ b/java/res/values-hi/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Android कीबोर्ड (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android वर्तनी परीक्षक (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Android कीबोर्ड सेटिंग (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Android वर्तनी परीक्षक सेटिंग (AOSP)"</string>
+</resources>
diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml
index bd745cc..3b6a6c3 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android कीबोर्ड (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android कीबोर्ड सेटिंग (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android वर्तनी परीक्षक (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Android वर्तनी परीक्षक सेटिंग (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"इनपुट विकल्‍प"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"लॉग आदेशों का शोध करें"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"संपर्क नामों को खोजें"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"स्पेसबार पर डबल टैप करने से पीरियड शामिल हो जाता है जिसके बाद एक रिक्ति होती है"</string>
     <string name="auto_cap" msgid="1719746674854628252">"स्‍वत: अक्षर बड़े करना"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"प्रत्येक वाक्य के पहले शब्द को बड़ा लिखें"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"व्यक्तिगत डिक्शनरी"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"एड-ऑन डिक्शनरी"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"मुख्‍य डिक्‍शनरी"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"सुधार सुझाव दिखाएं"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"हमेशा दिखाएं"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"पोर्ट्रेट मोड में दिखाएं"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"हमेशा छुपाएं"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"आपत्तिजनक शब्द अवरुद्ध करें"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"संभावित आपत्तिजनक शब्दों का सुझाव न दें"</string>
     <string name="auto_correction" msgid="7630720885194996950">"स्‍वत: सुधार"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Spacebar और विराम चिह्न गलत लिखे गए शब्‍दों को स्‍वचालित रूप से ठीक करते हैं"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"बंद"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"<xliff:g id="LOCALE_NAME">%s</xliff:g> के लिए वास्तव में यह फ़ाइल इंस्टॉल करें?"</string>
     <string name="error" msgid="8940763624668513648">"कोई त्रुटि हुई थी"</string>
     <string name="button_default" msgid="3988017840431881491">"डिफ़ॉल्ट"</string>
-    <string name="language_settings" msgid="1671153053201809031">"भाषा और इनपुट"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"इनपुट पद्धति चुनें"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> में आपका स्वागत है"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"हावभाव लेखन के साथ"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"आरंभ करें"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"अगला चरण"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> सेट करना"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> को सक्षम करें"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"कृपया अपनी भाषा और इनपुट सेटिंग में \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" को चेक करें. इससे वह आपके उपकरण पर चलने के लिए अधिकृत हो जाएगा."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> आपकी भाषा और इनपुट सेटिंग में पहले से सक्षम है, इसलिए यह चरण पूर्ण हो गया है. अगले चरण पर जाएं!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"सेटिंग में सक्षम करें"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> पर स्विच करें"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"इसके बाद, \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" को अपनी सक्रिय पाठ-इनपुट पद्धति के रूप में चुनें."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"इनपुट पद्धतियां स्विच करें"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"बधाई हो, आप बिल्कुल तैयार हैं!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"अब आप <xliff:g id="APPLICATION_NAME">%s</xliff:g> के साथ अपने सभी पसंदीदा एप्लिकेशन में लिख सकते हैं."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"अतिरिक्त भाषाएं कॉन्फ़िगर करना"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"समाप्त"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"एप्लिकेशन आइकन दिखाएं"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"लॉन्चर में एप्लिकेशन आइकन प्रदर्शित करें"</string>
     <string name="app_name" msgid="6320102637491234792">"डिक्‍शनरी प्रदाता"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"डिक्‍शनरी प्रदाता"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"डिक्‍शनरी सेवा"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"<xliff:g id="LANGUAGE">%1$s</xliff:g> के लिए डिक्‍शनरी उपलब्‍ध है"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"समीक्षा करने और डाउनलोड करने के लिए दबाएं"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"डाउनलोड हो रहा है: <xliff:g id="LANGUAGE">%1$s</xliff:g> के लिए सुझाव जल्दी ही तैयार हो जाएंगे."</string>
+    <string name="version_text" msgid="2715354215568469385">"संस्करण <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"जोड़ें"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"शब्दकोष में जोड़ें"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"वाक्यांश"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"अधिक विकल्प"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"कम विकल्‍प"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"ठीक"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"शब्द:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"शॉर्टकट:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"भाषा:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"कोई शब्द लिखें"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"वैकल्पिक शॉर्टकट"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"शब्‍द संपादित करें"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"संपादित करें"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"हटाएं"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"आपके पास उपयोगकर्ता शब्दकोश में कोई शब्‍द नहीं है. जोड़ें (+) बटन स्‍पर्श करके कोई शब्‍द जोड़ें."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"सभी भाषाओं के लिए"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"अधिक भाषाएं…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"हटाएं"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-hr/strings-appname.xml b/java/res/values-hr/strings-appname.xml
new file mode 100644
index 0000000..e22d2a2
--- /dev/null
+++ b/java/res/values-hr/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Androidova tipkovnica (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Androidova provjera pravopisa (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Postavke Androidove tipkovnice (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Postavke Androidove provjere pravopisa (AOSP)"</string>
+</resources>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index 6b418a1..264192b 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Androidova tipkovnica (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Postavke Androidove tipkovnice (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Androidova provjera pravopisa (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Postavke Androidove provjere pravopisa (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opcije ulaza"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Istraživanje naredbi dnevnika"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Potražite imena kontakata"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dvostrukim dodirivanjem razmaknice umeću se točka i razmak"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Automatsko pisanje velikih slova"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Napiši velikim slovom prvu riječ svake rečenice"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Osobni rječnik"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Rječnici-dodaci"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Glavni rječnik"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Pokaži prijedloge ispravka"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Uvijek prikaži"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Prikaži u portretnom načinu"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Uvijek sakrij"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Blokiraj uvredljive riječi"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Ne predlaži potencijalno uvredljive riječi"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Automatski ispravak"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Razmak i interpunkcija automatski ispravljaju krive riječi"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Isključeno"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Želite li doista instalirati ovu datoteku za <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Došlo je do pogreške"</string>
     <string name="button_default" msgid="3988017840431881491">"Zadano"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Jezik i unos"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Odabir načina unosa"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Dobro došli u aplikaciju <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"s Pisanjem kretnjama"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Počnite s radom"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Sljedeći korak"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Postavljanje aplikacije <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Omogućite aplikaciju <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Potvrdite aplikaciju \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" u postavkama Jezik i unos i ovlastite je za pokretanje."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"Aplikacija <xliff:g id="APPLICATION_NAME">%s</xliff:g> već je omogućena u postavkama jezika i unosa, pa je taj korak gotov. Nastavite sa sljedećim korakom!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Omogući u postavkama"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Prijeđite na aplikaciju <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Zatim odaberite aplikaciju \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" kao aktivan način unosa teksta."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Prebaci na drugi način unosa"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Izvrsno, spremni ste!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Sada možete pisati u svim svojim omiljenim aplikacijama pomoću aplikacije <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Konfiguriraj dodatne jezike"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Završeno"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Prikaži ikonu aplikacije"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Prikazivanje ikone aplikacije u pokretaču"</string>
     <string name="app_name" msgid="6320102637491234792">"Davatelj rječnika"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Davatelj rječnika"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Usluga rječnika"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Dostupan je rječnik za <xliff:g id="LANGUAGE">%1$s</xliff:g> jezik"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Pritisnite za pregled i preuzimanje"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Preuzimanje: prijedlozi za <xliff:g id="LANGUAGE">%1$s</xliff:g> bit će spremni uskoro."</string>
+    <string name="version_text" msgid="2715354215568469385">"Verzija <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Dodavanje"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Dodaj u rječnik"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Fraza"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Više opcija"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Manje opcija"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"U redu"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Riječ:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Prečac:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Jezik:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Upišite riječ"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Neobavezni prečac"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Uređivanje riječi"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Uređivanje"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Brisanje"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Nemate nijednu riječ u korisničkom rječniku. Riječ možete dodati dodirom na gumb Dodaj (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Za sve jezike"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Više jezika…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Izbriši"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCČĆDDŽĐEFGHIJKLLJMNNJOPRSŠTUVZŽ"</string>
 </resources>
diff --git a/java/res/values-hu/strings-appname.xml b/java/res/values-hu/strings-appname.xml
new file mode 100644
index 0000000..ec99904
--- /dev/null
+++ b/java/res/values-hu/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Android-billentyűzet (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Androidos helyesírás-ellenőrző (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Android-billentyűzet beállításai (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Androidos helyesírás-ellenőrző beállításai (AOSP)"</string>
+</resources>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index b35a5f1..167b43b 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android-billentyűzet (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android-billentyűzet beállításai (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Androidos helyesírás-ellenőrző (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Androidos helyesírás-ellenőrző beállításai (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Beviteli beállítások"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Naplózási parancsok"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Névjegyek keresése"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"A szóköz kétszeri megérintése beszúr egy pontot, majd egy szóközt"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Automatikusan nagy kezdőbetű"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Minden mondat első szava nagybetűvel"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Személyes szótár"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Bővítmények: szótárak"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Fő szótár"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Javítási ajánlások megjelenítése"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mindig látszik"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Megjelenítés álló tájolásban"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Mindig rejtve"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Sértő szavak kizárása"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Ne javasoljon esetlegesen sértő szavakat"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Automatikus javítás"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Szóköz és központozás automatikusan javítja az elgépelést"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Ki"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Valóban telepíti ezt a fájlt <xliff:g id="LOCALE_NAME">%s</xliff:g> nyelvhez?"</string>
     <string name="error" msgid="8940763624668513648">"Hiba történt."</string>
     <string name="button_default" msgid="3988017840431881491">"Alapértelmezett"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Nyelv és bevitel"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Beviteli mód kiválasztása"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Üdvözli a(z) <xliff:g id="APPLICATION_NAME">%s</xliff:g>!"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"kézmozdulatokkal történő bevitellel"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Első lépések"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Következő lépés"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"A(z) <xliff:g id="APPLICATION_NAME">%s</xliff:g> beállítása"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"A(z) <xliff:g id="APPLICATION_NAME">%s</xliff:g> engedélyezése"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Jelölje be a(z) „<xliff:g id="APPLICATION_NAME">%s</xliff:g>” alkalmazást a „Nyelv és bevitel” alatt a futtatás érdekében."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> alkalmazást már engedélyezte a Nyelv és bevitel beállításainál, tehát ez a lépés már kész. Folytassa a következővel!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Engedélyezés a Beállítások között"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Váltás a(z) <xliff:g id="APPLICATION_NAME">%s</xliff:g> alkalmazásra"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Ezután válassza a(z) „<xliff:g id="APPLICATION_NAME">%s</xliff:g>”  alkalmazást aktív szövegbeviteli módszerként."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Váltás a beviteli módok között"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Gratulálunk, máris elkészült!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Immár minden kedvenc alkalmazásában gépelhet a(z) <xliff:g id="APPLICATION_NAME">%s</xliff:g> segítségével."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Állítson be további nyelveket"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Befejeződött"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Alkalmazásikon megjelenítése"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Alkalmazásikon megjelenítése az indítóban"</string>
     <string name="app_name" msgid="6320102637491234792">"Szótárszolgáltató"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Szótárszolgáltató"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Szótárszolgáltatás"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"<xliff:g id="LANGUAGE">%1$s</xliff:g> nyelvhez van rendelkezésre álló szótár"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Nyomja meg az áttekintéshez és letöltéshez"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Letöltés: a(z) <xliff:g id="LANGUAGE">%1$s</xliff:g> nyelvvel kapcsolatos javaslatok hamarosan elérhetők lesznek."</string>
+    <string name="version_text" msgid="2715354215568469385">"Verzió: <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Hozzáadás"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Hozzáadás a szótárhoz"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Kifejezés"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"További opciók"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Kevesebb opció"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Szó:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Gyorsparancs:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Nyelv:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Írjon be egy szót"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Választható gyorsparancs"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Szó szerkesztése"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Szerkesztés"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Törlés"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Nincsenek szavak a felhasználói szótárban. Új szavakat a Hozzáadás (+) gomb megérintésével vehet fel."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Minden nyelven"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"További nyelvek…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Törlés"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-in/strings-appname.xml b/java/res/values-in/strings-appname.xml
new file mode 100644
index 0000000..c9da0db
--- /dev/null
+++ b/java/res/values-in/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Keyboard Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Pemeriksa Ejaan Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Setelan Keyboard Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Setelan Pemeriksa Ejaan Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index 5072b9a..7550b9d 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Keyboard Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Setelan Keyboard Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Pemeriksa Ejaan Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Setelan Pemeriksa Ejaan Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opsi masukan"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Riset Perintah Log"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cari nama kontak"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Mengetuk tombol spasi dua kali akan memasukkan titik diikuti satu spasi"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Kapitalisasi otomatis"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Kapitalisasi kata pertama di setiap kalimat"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Kamus pribadi"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Kamus pengaya"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Kamus utama"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Tampilkan saran koreksi"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Selalu tampilkan"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Tampilkan dalam mode potret"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Selalu sembunyikan"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Blokir kata tak pantas"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Jangan sarankan kata yang berpotensi menyinggung"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Koreksi otomatis"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Bilah spasi dan tanda baca secara otomatis dikoreksi pada kata yang salah ketik"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Mati"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Yakin ingin memasang file ini untuk <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Terjadi kesalahan"</string>
     <string name="button_default" msgid="3988017840431881491">"Default"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Bahasa &amp; masukan"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Pilih metode masukan"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Selamat datang di <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"dengan Ketikan Isyarat"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Memulai"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Langkah berikutnya"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Menyiapkan <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Aktifkan <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Centang \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" di setelan Bahasa &amp; masukan Anda. Tindakan ini akan mengizinkannya berjalan di perangkat Anda."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> telah diaktifkan di setelan Bahasa &amp; masukan Anda, jadi langkah ini sudah diselesaikan. Lanjut langkah selanjutnya!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Aktifkan dalam Setelan"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Beralih ke <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Lalu, pilih \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" sebagai metode masukan teks aktif Anda."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Alihkan metode masukan"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Selamat, Anda sudah siap!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Sekarang Anda dapat mengetik di semua aplikasi favorit Anda dengan <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Konfigurasikan bahasa tambahan"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Selesai"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Tampilkan ikon aplikasi"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Menampillkan ikon aplikasi di peluncur"</string>
     <string name="app_name" msgid="6320102637491234792">"Penyedia Kamus"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Penyedia Kamus"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Layanan Kamus"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Kamus tersedia untuk bahasa <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Tekan untuk meninjau dan mengunduh"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Mengunduh: saran untuk bahasa <xliff:g id="LANGUAGE">%1$s</xliff:g> akan segera tersedia."</string>
+    <string name="version_text" msgid="2715354215568469385">"Versi <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Tambahkan"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Tambahkan ke kamus"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Frasa"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Opsi lainnya"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Sedikit opsi"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"Oke"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Kata:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Pintasan:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Bahasa:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Ketik kata"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Pintasan opsional"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Edit kata"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Edit"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Hapus"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Anda tidak memiliki satu kata pun dalam kamus pengguna. Tambahkan kata dengan menyentuh tombol Tambahkan (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Untuk semua bahasa"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Bahasa lainnya..."</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Hapus"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-it/strings-appname.xml b/java/res/values-it/strings-appname.xml
new file mode 100644
index 0000000..fe5fc97
--- /dev/null
+++ b/java/res/values-it/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Tastiera Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Controllo ortografico Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Impostazioni tastiera Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Impostazioni controllo ortografico Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index c64543a..ed8bfa6 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Tastiera Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Impostazioni tastiera Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Controllo ortografico Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Impostazioni controllo ortografico Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opzioni inserimento"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Ricerca comandi di log"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cerca in nomi contatti"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Tocca due volte barra spaziatr. per inserire punto seguito da spazio"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Maiuscole automatiche"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Iniziale maiuscola per la prima parola di ogni frase"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Dizionario personale"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dizionari aggiuntivi"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Dizionario principale"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Mostra suggerimenti correzioni"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostra sempre"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Mostra in modalità verticale"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Nascondi sempre"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Blocca parole offensive"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Non suggerire parole potenzialmente offensive"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Correzione automatica"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Barra spaziatrice/punteggiatura correggono parole con errori"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Off"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Installare questo file per <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Si è verificato un errore"</string>
     <string name="button_default" msgid="3988017840431881491">"Predefinito"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Lingua e input"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Scegli il metodo di immissione"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Benvenuto in <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"con la Digitazione gestuale"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Inizia"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Passaggio successivo"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Configurazione di <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Abilita <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Seleziona \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" nelle impostazioni Lingua e immissione per autorizzarne l\'esecuzione sul dispositivo."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> è già abilitato nelle tue impostazioni di lingua e immissione, quindi questo passaggio è completato. Vai al prossimo."</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Abilita nelle impostazioni"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Passa a <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Quindi seleziona \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" come metodo di immissione testo attivo."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Cambia metodo di immissione"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Grazie, adesso sei pronto!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Ora puoi digitare in tutte le tue app preferite con <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Configura altre lingue"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Terminato"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Mostra icona app"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Mostra l\'icona dell\'app in Avvio applicazioni"</string>
     <string name="app_name" msgid="6320102637491234792">"Dictionary Provider"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Dictionary Provider"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Servizio dizionario"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"È disponibile un dizionario per <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Premi per esaminare e scaricare"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Download: i suggerimenti per <xliff:g id="LANGUAGE">%1$s</xliff:g> saranno pronti a breve."</string>
+    <string name="version_text" msgid="2715354215568469385">"Versione <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Aggiungi"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Aggiungi al dizionario"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Frase"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Più opzioni"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Meno opzioni"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Parola:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Scorciatoia:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Lingua:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Digita una parola"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Scorciatoia facoltativa"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Modifica parola"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Modifica"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Elimina"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Non sono presenti parole nel dizionario utente. Puoi aggiungere una parola toccando il pulsante Aggiungi (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Per tutte le lingue"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Altre lingue..."</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Elimina"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-iw/strings-appname.xml b/java/res/values-iw/strings-appname.xml
new file mode 100644
index 0000000..1a07c54
--- /dev/null
+++ b/java/res/values-iw/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"מקלדת Android‏ (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"בודק האיות של Android‏ (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"הגדרות מקלדת Android‏ (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"הגדרות בודק האיות של Android‏ (AOSP)"</string>
+</resources>
diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml
index d476c32..6c9ab85 100644
--- a/java/res/values-iw/strings.xml
+++ b/java/res/values-iw/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"מקלדת Android‏ (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"הגדרות מקלדת Android‏ (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"בודק האיות של Android‏ (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"הגדרות בודק האיות של Android‏ (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"אפשרויות קלט"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"פקודות יומן מחקר"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"חפש שמות של אנשי קשר"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"הקשה פעמיים על מקש הרווח מזינה נקודה ואחריה רווח"</string>
     <string name="auto_cap" msgid="1719746674854628252">"הפיכת אותיות לרישיות באופן אוטומטי"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"השתמש באות גדולה במילה הראשונה של כל משפט"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"מילון אישי"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"הוספת מילונים"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"מילון ראשי"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"הצג הצעות לתיקונים"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"הצג תמיד"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"הצג בפריסה לאורך"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"הסתר תמיד"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"חסום מילים פוגעניות"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"אל תציע מילים שעלולות להיות פוגעניות"</string>
     <string name="auto_correction" msgid="7630720885194996950">"תיקון אוטומטי"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"מקש הרווח ופיסוק מתקנים אוטומטית שגיאות הקלדה"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"כבוי"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"האם באמת להתקין את הקובץ הזה עבור <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"אירעה שגיאה"</string>
     <string name="button_default" msgid="3988017840431881491">"ברירת מחדל"</string>
-    <string name="language_settings" msgid="1671153053201809031">"שפה וקלט"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"בחירת שיטת קלט"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"ברוך הבא אל <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"עם הקלדת החלקה"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"התחל"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"השלב הבא"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"הגדרת <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"הפעל את <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"סמן את \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" בהגדרות השפה והקלט שלך. פעולה זו תאפשר לו לפעול במכשיר שלך."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> כבר פועל בשפה שלך ובהגדרות הקלט, כך שהשלב הזה הסתיים. הלאה אל השלב הבא!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"הפעל בהגדרות"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"עבור אל <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"בשלב הבא, בחר ב-\'<xliff:g id="APPLICATION_NAME">%s</xliff:g>\' כאמצעי הקלט הפעיל להזנת טקסט."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"החלף שיטות קלט"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"ברכותינו, הכל מוכן!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"כעת תוכל להקליד בכל היישומים המועדפים עליך באמצעות <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"הגדר שפות נוספות"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"סיום"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"הצג את סמל היישום"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"הצג את סמל היישום במפעיל"</string>
     <string name="app_name" msgid="6320102637491234792">"ספק המילון"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"ספק המילון"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"שירות מילון"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"יש מילון זמין עבור <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"לחץ כדי לעיין ולהוריד"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"מוריד: הצעות ב<xliff:g id="LANGUAGE">%1$s</xliff:g> יהיו מוכנות בקרוב."</string>
+    <string name="version_text" msgid="2715354215568469385">"גרסה <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"הוסף"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"הוסף למילון"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"ביטוי"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"עוד אפשרויות"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"פחות אפשרויות"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"אישור"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"מילה:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"קיצור דרך:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"שפה:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"הקלד מילה"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"קיצור דרך אופציונלי"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"עריכת מילה"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"ערוך"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"מחק"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"לא מוגדרות מילים במילון המשתמש. הוסף מילה על ידי נגיעה בלחצן \'הוסף\' (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"לכל השפות"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"עוד שפות…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"מחק"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-ja/strings-appname.xml b/java/res/values-ja/strings-appname.xml
new file mode 100644
index 0000000..e3a9303
--- /dev/null
+++ b/java/res/values-ja/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Androidキーボード(AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Androidスペルチェッカー(AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Androidキーボードの設定(AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Androidスペルチェッカーの設定(AOSP)"</string>
+</resources>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
index 267148c..e0af4b0 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Androidキーボード(AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Androidキーボードの設定(AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Androidスペルチェッカー(AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Androidスペルチェッカーの設定(AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"入力オプション"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"ログコマンドの検索"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"連絡先名の検索"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"スペースバーをダブルタップするとピリオドとスペースを挿入できます"</string>
     <string name="auto_cap" msgid="1719746674854628252">"自動大文字変換"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"英字入力で各文の最初の単語を大文字にします"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"ユーザー辞書"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"アドオン辞書"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"メイン辞書"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"修正候補を表示する"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"常に表示"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"縦向きで表示"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"常に非表示"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"不適切な語句をブロック"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"不適切な可能性がある語句を候補にしない"</string>
     <string name="auto_correction" msgid="7630720885194996950">"自動修正"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"誤入力をスペースまたは句読点キーで修正する"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"OFF"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"この<xliff:g id="LOCALE_NAME">%s</xliff:g>のファイルをインストールしてもよろしいですか?"</string>
     <string name="error" msgid="8940763624668513648">"エラーが発生しました"</string>
     <string name="button_default" msgid="3988017840431881491">"デフォルト"</string>
-    <string name="language_settings" msgid="1671153053201809031">"言語と入力"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"入力方法の選択"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>へようこそ"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"新しいジェスチャー入力をお試しください"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"開始"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"次のステップ"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>の設定"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>の有効化"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"[言語と入力]で[<xliff:g id="APPLICATION_NAME">%s</xliff:g>]のチェックボックスをオンにしてください。これで、端末でキーボードを使用できるようになります。"</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>は[言語と入力]で既に有効になっているので、このステップは完了です。次のステップに進んでください。"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"設定での有効化"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>への切り替え"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"次に、有効なテキスト入力方法として「<xliff:g id="APPLICATION_NAME">%s</xliff:g>」を選択します。"</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"入力方法を切り替える"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"設定完了"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"これで、お気に入りのすべてのアプリで<xliff:g id="APPLICATION_NAME">%s</xliff:g>を使用して入力できます。"</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"別の言語を設定する"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"完了"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"アプリアイコンを表示"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"ランチャーにアプリアイコンを表示します"</string>
     <string name="app_name" msgid="6320102637491234792">"辞書提供元"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"辞書提供元"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"辞書"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"<xliff:g id="LANGUAGE">%1$s</xliff:g>の辞書を利用できます"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"押すと確認/ダウンロードできます"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"ダウンロード中: <xliff:g id="LANGUAGE">%1$s</xliff:g>の入力候補をまもなく利用できます。"</string>
+    <string name="version_text" msgid="2715354215568469385">"バージョン<xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"追加"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"辞書に追加"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"フレーズ"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"詳細設定を表示"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"詳細設定を隠す"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"単語:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"ショートカット:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"言語:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"単語を入力します"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"オプションのショートカット"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"語句の編集"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"編集"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"削除"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"単語リストに登録がありません。追加ボタン[+]をタップすると単語を追加できます。"</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"すべての言語用"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"その他の言語…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"削除"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-ko/strings-appname.xml b/java/res/values-ko/strings-appname.xml
new file mode 100644
index 0000000..a25fdb6
--- /dev/null
+++ b/java/res/values-ko/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Android 키보드(AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android 맞춤법 검사기(AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Android 키보드 설정(AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Android 맞춤법 검사기 설정(AOSP)"</string>
+</resources>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index 7ea31a3..b481899 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android 키보드(AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android 키보드 설정(AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android 맞춤법 검사기(AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Android 맞춤법 검사기 설정(AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"입력 옵션"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"로그 명령 탐색"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"연락처 이름 조회"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"스페이스바를 두 번 탭하면 마침표와 공백 한 개가 삽입됩니다."</string>
     <string name="auto_cap" msgid="1719746674854628252">"자동 대문자화"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"문장의 첫 단어를 대문자로 표시"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"개인 사전"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"사전 추가"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"기본 사전"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"수정 제안 표시"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"항상 표시"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"세로 모드로 표시"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"항상 숨기기"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"불쾌감을 주는 단어 차단"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"불쾌감을 줄 수 있는 단어는 추천하지 않음"</string>
     <string name="auto_correction" msgid="7630720885194996950">"자동 수정"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"스페이스바와 문장부호 키를 사용하면 오타가 자동으로 교정됩니다."</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"사용 안함"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"이 파일을 <xliff:g id="LOCALE_NAME">%s</xliff:g>(으)로 설치하시겠습니까?"</string>
     <string name="error" msgid="8940763624668513648">"오류 발생"</string>
     <string name="button_default" msgid="3988017840431881491">"기본값"</string>
-    <string name="language_settings" msgid="1671153053201809031">"언어 및 키보드"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"입력 방법 선택"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>에 오신 것을 환영합니다."</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"제스처 타이핑 사용"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"시작하기"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"다음 단계"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> 설정"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> 사용 설정"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"언어 및 입력 설정에서 \'<xliff:g id="APPLICATION_NAME">%s</xliff:g>\'을(를) 확인하세요. 애플리케이션을 기기에서 실행할 수 있도록 승인합니다."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>은(는) 언어 및 입력 설정에서 이미 사용하도록 설정되어 있으므로 이 단계는 완료되었습니다. 다음 단계로 이동하세요."</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"\'설정\'에서 사용 설정"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>(으)로 전환"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"그런 다음, 텍스트 입력 방법으로 \'<xliff:g id="APPLICATION_NAME">%s</xliff:g>\'을(를) 선택합니다."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"입력 방법 전환"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"모든 설정이 완료되었습니다."</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"이제 즐겨찾는 앱을 <xliff:g id="APPLICATION_NAME">%s</xliff:g>에 입력할 수 있습니다."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"추가 언어 설정"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"완료됨"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"앱 아이콘 표시"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"실행기에 애플리케이션 아이콘 표시"</string>
     <string name="app_name" msgid="6320102637491234792">"사전 제공업체"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"사전 제공업체"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"사전 서비스"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"<xliff:g id="LANGUAGE">%1$s</xliff:g> 사전을 사용할 수 있습니다."</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"검토하고 다운로드하려면 누르세요."</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"다운로드 중: <xliff:g id="LANGUAGE">%1$s</xliff:g>에 대한 추천항목이 곧 준비됩니다."</string>
+    <string name="version_text" msgid="2715354215568469385">"버전 <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"추가"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"사전에 추가"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"구문"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"옵션 더보기"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"옵션 숨기기"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"확인"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"단어:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"단축키:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"언어:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"단어 입력"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"선택적 단축키"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"단어 수정"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"수정"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"삭제"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"사용자 사전에 단어가 없습니다. 추가(+) 버튼을 터치하여 단어를 추가할 수 있습니다."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"모든 언어"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"더보기…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"삭제"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-land/keyboard-heights.xml b/java/res/values-land/keyboard-heights.xml
index 4ebeda5..670be33 100644
--- a/java/res/values-land/keyboard-heights.xml
+++ b/java/res/values-land/keyboard-heights.xml
@@ -19,19 +19,21 @@
 -->
 
 <resources>
-    <!-- Build.HARDWARE,keyboard_height_in_dp -->
+    <!-- Build condition,keyboard_height_in_dp -->
     <string-array name="keyboard_heights" translatable="false">
     <!-- Preferable keyboard height in absolute scale: 1.100in -->
         <!-- Droid -->
-        <item>sholes,194.3333</item>
+        <item>HARDWARE=sholes,194.3333</item>
         <!-- Nexus One -->
-        <item>mahimahi,186.2667</item>
+        <item>HARDWARE=mahimahi,186.2667</item>
         <!-- Nexus S -->
-        <item>herring,171.9385</item>
+        <item>HARDWARE=herring,171.9385</item>
         <!-- Galaxy Nexus -->
-        <item>tuna,173.4207</item>
+        <item>HARDWARE=tuna,173.4207</item>
     <!-- Preferable keyboard height in absolute scale: 45.0mm -->
         <!-- Xoom -->
-        <item>stingray,265.4378</item>
+        <item>HARDWARE=stingray,265.4378</item>
+    <!-- Default value for unknown device: empty string -->
+        <item>,</item>
     </string-array>
 </resources>
diff --git a/java/res/values-land/setup-dimens-small-phone-land.xml b/java/res/values-land/setup-dimens-small-phone-land.xml
new file mode 100644
index 0000000..088e656
--- /dev/null
+++ b/java/res/values-land/setup-dimens-small-phone-land.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Setup wizard dimensions for small-phone/landscape. -->
+    <dimen name="setup_title_text_size">32sp</dimen>
+    <dimen name="setup_welcome_description_text_size">18sp</dimen>
+    <dimen name="setup_step_bullet_text_size">18sp</dimen>
+    <dimen name="setup_step_triangle_indicator_height">18dp</dimen>
+    <dimen name="setup_step_indicator_height">18dp</dimen>
+    <dimen name="setup_step_title_text_size">18sp</dimen>
+    <dimen name="setup_step_instruction_text_size">14sp</dimen>
+    <dimen name="setup_step_action_text_size">16sp</dimen>
+    <dimen name="setup_vertical_padding">16dp</dimen>
+    <dimen name="setup_horizontal_padding">12dp</dimen>
+    <dimen name="setup_step_action_height">42dp</dimen>
+    <dimen name="setup_step_horizontal_padding">20dp</dimen>
+    <dimen name="setup_step_horizontal_padding_half">10dp</dimen>
+    <dimen name="setup_step_vertical_padding">12dp</dimen>
+    <dimen name="setup_step_horizontal_line_height">2dp</dimen>
+    <integer name="setup_title_weight_in_screen">40</integer>
+    <integer name="setup_body_weight_in_screen">60</integer>
+    <dimen name="setup_title_end_margin">12dp</dimen>
+    <dimen name="setup_welcome_description_top_margin">12dp</dimen>
+    <dimen name="setup_welcome_video_top_padding">0dp</dimen>
+    <dimen name="setup_welcome_video_bottom_padding">12dp</dimen>
+    <integer name="setup_welcome_video_weight_in_screen">70</integer>
+    <integer name="setup_welcome_video_end_padding_weight_in_screen">30</integer>
+</resources>
diff --git a/java/res/values-lt/strings-appname.xml b/java/res/values-lt/strings-appname.xml
new file mode 100644
index 0000000..2b9d6ea
--- /dev/null
+++ b/java/res/values-lt/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"„Android“ klaviatūra (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"„Android“ rašybos tikrinimo programa (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"„Android“ klaviatūros nustatymai (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"„Android“ rašybos tikrinimo programos nustatymai (AOSP)"</string>
+</resources>
diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml
index 283013c..b877ffa 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"„Android“ klaviatūra (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"„Android“ klaviatūros nustatymai (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"„Android“ rašybos tikrinimo programa (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"„Android“ rašybos tikrinimo programos nustatymai (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Įvesties parinktys"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Tyrinėti žurnalo komandas"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontaktų vardų paieška"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dukart palietus tarpo klavišą įterpiamas taškas ir tarpas."</string>
     <string name="auto_cap" msgid="1719746674854628252">"Automatinis didžiųjų raidžių rašymas"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Kiekvieno sakinio pirmą žodį rašyti iš didžiosios raidės"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Asmeninis žodynas"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Papildomi žodynai"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Pagrindinis žodynas"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Rodyti taisymo pasiūlymus"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Visada rodyti"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Rodyti portreto režimu"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Visada slėpti"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Blokuoti įžeidžiančius žodžius"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Nesiūlyti galimai įžeidžiančių žodžių"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Automatinis taisymas"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Tarpo kl. ir skyr. ženkl. aut. išt. neteis. įv. žodž."</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Išjungta"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Ar tikrai įdiegti šį failą <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Įvyko klaida"</string>
     <string name="button_default" msgid="3988017840431881491">"Numatytieji"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Kalba ir įvestis"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Pasirinkite įvesties metodą"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Sveiki! Tai „<xliff:g id="APPLICATION_NAME">%s</xliff:g>“"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"naudojant įvestį gestais"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Pradėti"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Kitas veiksmas"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"„<xliff:g id="APPLICATION_NAME">%s</xliff:g>“ sąranka"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Įgalinkite „<xliff:g id="APPLICATION_NAME">%s</xliff:g>“"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Skiltyje „Kalbos ir įvesties nustatymai“ žr. „<xliff:g id="APPLICATION_NAME">%s</xliff:g>“ (progr. bus įgal. veikti įr.)."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"„<xliff:g id="APPLICATION_NAME">%s</xliff:g>“ jau įgalinta jūsų „Kalbos ir įvesties nustatymuose“, todėl šis veiksmas yra atliktas. Galite pereiti prie kito!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Įgalinti nustatymuose"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Perjungimas į „<xliff:g id="APPLICATION_NAME">%s</xliff:g>“"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Toliau pasirinkite „<xliff:g id="APPLICATION_NAME">%s</xliff:g>“ kaip aktyvų teksto įvesties metodą."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Perjungti įvesties metodus"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Sveikiname, viską nustatėte!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Dabar galite įvesti visas mėgstamas programas naudodami „<xliff:g id="APPLICATION_NAME">%s</xliff:g>“."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Konfigūruokite papildomas kalbas"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Baigta"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Rodyti programos piktogramą"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Pateikti programos piktogramą paleidimo priemonėje"</string>
     <string name="app_name" msgid="6320102637491234792">"Žodyno teikėjas"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Žodyno teikėjas"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Žodyno paslauga"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Galimas <xliff:g id="LANGUAGE">%1$s</xliff:g> žodynas"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Paspauskite, kad peržiūrėtumėte ir atsisiųstumėte"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Atsisiunčiama. Netrukus bus galimi <xliff:g id="LANGUAGE">%1$s</xliff:g> pasiūlymai."</string>
+    <string name="version_text" msgid="2715354215568469385">"<xliff:g id="VERSION_NUMBER">%1$s</xliff:g> versija"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Pridėti"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Pridėti prie žodyno"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Frazė"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Daugiau parink."</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Mažiau parink."</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"Gerai"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Žodis:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Spartusis klavišas:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Kalba:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Įveskite žodį"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Pasirenkamasis spartusis klavišas"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Redaguoti žodį"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Redaguoti"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Ištrinti"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Neturite jokių žodžių naudotojo žodyne. Žodį galite pridėti paliesdami mygtuką „Pridėti“ (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Visos kalbos"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Daugiau kalbų..."</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Ištrinti"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" AĄBCČDEĘĖFGHIĮYJKLMNOPRSŠTUŲŪVZŽ"</string>
 </resources>
diff --git a/java/res/values-lv/strings-appname.xml b/java/res/values-lv/strings-appname.xml
new file mode 100644
index 0000000..65b63db
--- /dev/null
+++ b/java/res/values-lv/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Android tastatūra (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android pareizrakstības pārbaudītājs (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Android tastatūras iestatījumi (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Android pareizrakstības pārbaudītāja iestatījumi (AOSP)"</string>
+</resources>
diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml
index 84b11f6..9ad5fc2 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android tastatūra (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android tastatūras iestatījumi (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android pareizrakstības pārbaudītājs (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Android pareizrakstības pārbaudītāja iestatījumi (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Ievades opcijas"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Izpētes žurnāla komandas"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Meklēt kontaktp. vārdus"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Divreiz pieskaroties atst. taustiņam, ievada punktu un atstarpi."</string>
     <string name="auto_cap" msgid="1719746674854628252">"Automātiska lielo burtu lietošana"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Katra teikuma pirmo vārdu rakstīt ar lielo burtu."</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Personiskā vārdnīca"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Papildinājumu vārdnīcas"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Galvenā vārdnīca"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Rādīt labojumu ieteikumus"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vienmēr rādīt"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Rādīt portreta režīmā"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Vienmēr slēpt"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Bloķēt aizvainojošus vārdus"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Neiesakiet vārdus, kas varētu būt aizvainojoši."</string>
     <string name="auto_correction" msgid="7630720885194996950">"Automātiska labošana"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Atstarpes taustiņš un interpunkcija; automātiska kļūdainu vārdu labošana"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Izslēgta"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Vai instalēt šo failu šādai valodai: <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Radās kļūda"</string>
     <string name="button_default" msgid="3988017840431881491">"Noklusējums"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Valoda un ievade"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Ievades metodes izvēle"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Laipni lūdzam pakalpojumā <xliff:g id="APPLICATION_NAME">%s</xliff:g>,"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"kurā varat izmantot ievadi ar žestiem"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Sākt darbu"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Nākamā darbība"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Lietojumprogrammas <xliff:g id="APPLICATION_NAME">%s</xliff:g> iestatīšana"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Lietojumprogrammas <xliff:g id="APPLICATION_NAME">%s</xliff:g> iespējošana"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Valodas un ievades iestatījumos atzīmējiet “<xliff:g id="APPLICATION_NAME">%s</xliff:g>”, autorizējot tās palaišanu ierīcē."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"Tā kā lietojumprogramma <xliff:g id="APPLICATION_NAME">%s</xliff:g> jau ir iespējota valodas un iesūtnes iestatījumos, šī darbība ir pabeigta. Veiciet nākamo darbību!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Iespējot iestatījumos"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Pārslēgšanās uz lietojumprogrammu <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Pēc tam atlasiet “<xliff:g id="APPLICATION_NAME">%s</xliff:g>” kā aktīvo ievades metodi."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Pārslēgt ievades metodes"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Gatavs!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Izmantojot lietojumprogrammu <xliff:g id="APPLICATION_NAME">%s</xliff:g>, varat rakstīt visās iecienītākajās lietotnēs."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Konfigurēt papildu valodas"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Pabeigts"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Rādīt lietotnes ikonu"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Rādīt palaidēja ekrānā lietojumprogrammas ikonu"</string>
     <string name="app_name" msgid="6320102637491234792">"Vārdnīcas nodrošinātājs"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Vārdnīcas nodrošinātājs"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Vārdnīcas pakalpojums"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Ir pieejama vārdnīca šādai valodai: <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Nospiediet, lai pārskatītu un lejupielādētu"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Notiek lejupielāde. Drīz būs pieejami ieteikumi šādai valodai: <xliff:g id="LANGUAGE">%1$s</xliff:g>."</string>
+    <string name="version_text" msgid="2715354215568469385">"Versija <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Pievienot"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Pievienot vārdnīcai"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Frāze"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Vairāk opciju"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Mazāk opciju"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"Labi"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Vārds:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Saīsne:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Valoda:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Ierakstiet vārdu."</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Izvēles saīsne"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Vārda rediģēšana"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Rediģēt"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Dzēst"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Lietotāja vārdnīcā nav neviena vārda. Lai pievienotu vārdu, pieskarieties pogai Pievienot (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Visās valodās"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Citas valodas..."</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Dzēst"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-ms/strings-appname.xml b/java/res/values-ms/strings-appname.xml
new file mode 100644
index 0000000..76d1d29
--- /dev/null
+++ b/java/res/values-ms/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Papan Kekunci Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Penyemak Ejaan Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Tetapan Papan Kekunci Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Tetapan Penyemak Ejaan Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-ms/strings.xml b/java/res/values-ms/strings.xml
index 67fdea9..bb001ef 100644
--- a/java/res/values-ms/strings.xml
+++ b/java/res/values-ms/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Papan kekunci Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Tetapan Papan Kekunci Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Penyemak Ejaan Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Tetapan Penyemak Ejaan Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Pilihan input"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Arahan Log Penyelidikan"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cari nama kenalan"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Mengetik 2X pada bar ruang memasukkan titik diikuti dengan ruang"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Huruf besar auto"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Besarkan perkataan pertama setiap ayat"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Kamus peribadi"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Kamus tambahan"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Kamus utama"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Tunjukkan cadangan pembetulan"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Sentiasa tunjukkan"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Tunjukkan dalam mod potret"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Sentiasa sembunyikan"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Sekat perkataan yg menyinggung"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Jangan cadangkan perkataan yang boleh menyinggung"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Auto pembetulan"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Bar ruang dan tanda baca secara automatik membetulkan perkataan yang ditaip salah"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Matikan"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Betul-betul pasang fail ini untuk <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Berlaku ralat"</string>
     <string name="button_default" msgid="3988017840431881491">"Lalai"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Bahasa &amp; input"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Pilih kaedah input"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Selamat datang ke <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"dengan Taipan Gerak Isyarat"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Bermula"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Langkah seterusnya"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Menyediakan <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Dayakan <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Sila semak \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" dlm ttpn Bhs &amp; input. Ini mbnarkn apl djlnkn pd pranti anda."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> sudah didayakan dalam tetapan Bahasa &amp; input anda, jadi langkah ini telah selesai. Beralih ke langkah seterusnya!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Dayakan dalam Tetapan"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Beralih ke <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Seterusnya, pilih \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" sebagai kaedah input teks aktif anda."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Tukar kaedah input"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Tahniah, anda sudah sedia!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Kini anda boleh menaip dalam semua apl kegemaran anda dengan <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Konfigurasikan bahasa tambahan"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Selesai"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Tunjukkan ikon apl"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Paparkan ikon apl dalam pelancar"</string>
     <string name="app_name" msgid="6320102637491234792">"Pembekal Kamus"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Pembekal Kamus"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Perkhidmatan Kamus"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Kamus tersedia untuk <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Tekan untuk mengulas dan memuat turun"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Memuat turun: cadangan untuk <xliff:g id="LANGUAGE">%1$s</xliff:g> akan sedia tidak lama lagi."</string>
+    <string name="version_text" msgid="2715354215568469385">"Versi <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Tambah"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Tambah ke kamus"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Frasa"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Lagi pilihan"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Kurang pilihan"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Perkataan:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Pintasan:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Bahasa:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Taip perkataan"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Pintasan pilihan"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Edit perkataan"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Edit"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Padam"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Anda tidak mempunyai sebarang perkataan dalam kamus pengguna. Tambahkan perkataan dengan menyentuh butang Tambah (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Untuk semua bahasa"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Lebih banyak bahasa..."</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Padam"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-nb/strings-appname.xml b/java/res/values-nb/strings-appname.xml
new file mode 100644
index 0000000..8a71405
--- /dev/null
+++ b/java/res/values-nb/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Android-tastatur (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android-stavekontroll (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Instillinger for Android-tastatur (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Innstillinger for Android-stavekontroll (AOSP)"</string>
+</resources>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index caa98cc..fc0c01e 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android-tastatur (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Instillinger for Android-tastatur (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android-stavekontroll (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Innstillinger for Android-stavekontroll (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Inndataalternativer"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Kommandoer for undersøkelseslogging"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Slå opp kontaktnavn"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dobbeltrykk på mellomromstasten for punktum etterfulgt av mellomrom"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Stor forbokstav"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Sett stor bokstav i det første ordet i hver setning"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Personlig ordliste"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Tilleggsordbøker"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Hovedordliste"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Vis rettingsforslag"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vis alltid"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Vis i stående modus"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Skjul alltid"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Blokkér støtende ord"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Ikke foreslå potensielt støtende ord"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Autokorrektur"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Mellomromstast og skilletegn retter automat. feilstavede ord"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Av"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Vil du virkelig installere denne filen for <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Det oppsto en feil"</string>
     <string name="button_default" msgid="3988017840431881491">"Standard"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Språk og inndata"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Velg inndatametode"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Velkommen til <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"med Ordføring"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Startveiledning"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Neste trinn"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Konfigurerer <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Aktiver <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Sjekk <xliff:g id="APPLICATION_NAME">%s</xliff:g> i Språk og inndata-innstillingene dine. Dette tillater appen å kjøre på enheten."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> er allerede aktivert i Språk og inndata-innstillingene dine, så dette trinnet er fullført. Gå til neste trinn!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Aktiver i Innstillinger"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Bytt til <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Deretter velger du  «<xliff:g id="APPLICATION_NAME">%s</xliff:g>» som den aktive inndatametoden for tekst."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Bytt inndatametode"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Gratulerer, du er klar!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Nå kan du skrive inn alle favorittappene dine med <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Konfigurer flere språk"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Fullført"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Vis app-ikonet"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Vis app-ikonet i appvelgeren"</string>
     <string name="app_name" msgid="6320102637491234792">"Ordlisteleverandør"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Ordlisteleverandør"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Ordlistetjeneste"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"En ordliste er tilgjengelig for <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Trykk for å se gjennom og laste ned"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Laster ned: forslag blir snart tilgjengelige for <xliff:g id="LANGUAGE">%1$s</xliff:g>."</string>
+    <string name="version_text" msgid="2715354215568469385">"Versjon <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Legg til"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Legg til i ordlisten"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Setning"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Flere alt."</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Færre alt."</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Ord:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Snarvei:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Språk:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Skriv inn et ord"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Valgfri snarvei"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Rediger ord"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Rediger"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Slett"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Det finnes ingen ord i brukerordlisten. Du kan legge til et ord ved å trykke på Legg til-knappen (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"For alle språk"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Flere språk"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Slett"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ"</string>
 </resources>
diff --git a/java/res/values-nl/strings-appname.xml b/java/res/values-nl/strings-appname.xml
new file mode 100644
index 0000000..41f85dc
--- /dev/null
+++ b/java/res/values-nl/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Android-toetsenbord (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Spellingcontrole van Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Instellingen voor Android-toetsenbord (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Instellingen voor spellingcontrole van Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index 5480430..64d64b5 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android-toetsenbord (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Instellingen voor het Android-toetsenbord (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Spellingcontrole van Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Instellingen voor spellingcontrole van Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Invoeropties"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Opdrachten in onderzoekslogbestand"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Contactnamen opzoeken"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dubbeltik op spatiebalk voor een punt gevolgd door een spatie"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Auto-hoofdlettergebruik"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Het eerste woord van elke zin met een hoofdletter schrijven"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Persoonlijk woordenboek"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Woordenboeken toevoegen"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Algemeen woordenboek"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Correctievoorstellen weergeven"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Altijd weergeven"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Weergeven in staande modus"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Altijd verbergen"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Aanstootgevende woorden blokk."</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Geen potentieel aanstootgevende woorden voorstellen"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Autocorrectie"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Met spatiebalk en interpunctie worden verkeerd gespelde woorden automatisch gecorrigeerd"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Uitgeschakeld"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Wilt u dit bestand voor <xliff:g id="LOCALE_NAME">%s</xliff:g> echt installeren?"</string>
     <string name="error" msgid="8940763624668513648">"Er is een fout opgetreden"</string>
     <string name="button_default" msgid="3988017840431881491">"Standaard"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Taal en invoer"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Invoermethode selecteren"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Welkom bij <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"met Invoer met bewegingen"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Aan de slag"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Volgende stap"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> instellen"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> inschakelen"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Vink \'<xliff:g id="APPLICATION_NAME">%s</xliff:g>\' aan in \'Instellingen voor taal en invoer\'. De app kan dan worden uitgevoerd op uw apparaat."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> is al ingeschakeld in \'Instellingen voor taal en invoer\', dus deze stap is voltooid. Op naar de volgende!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Inschakelen in \'Instellingen\'"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Overschakelen naar <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Selecteer vervolgens \'<xliff:g id="APPLICATION_NAME">%s</xliff:g>\' als actieve tekstinvoermethode."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Schakelen tussen invoermethoden"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Gefeliciteerd, u kunt nu aan de slag."</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"U kunt nu in al uw favoriete apps typen met <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Extra talen configureren"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Voltooid"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"App-pictogram weergeven"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"App-pictogram weergeven in het opstartprogramma"</string>
     <string name="app_name" msgid="6320102637491234792">"Woordenboekleverancier"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Woordenboekleverancier"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Woordenboekservice"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Er is een woordenboek beschikbaar voor het <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Druk om te controleren en te downloaden"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Downloaden: suggesties voor het <xliff:g id="LANGUAGE">%1$s</xliff:g> zijn straks beschikbaar."</string>
+    <string name="version_text" msgid="2715354215568469385">"Versie <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Toevoegen"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Toevoegen aan woordenboek"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Zinsdeel"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Meer opties"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Minder opties"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Woord:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Sneltoets:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Taal:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Typ een woord"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Optionele snelkoppeling"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Woord bewerken"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Bewerken"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Verwijderen"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Er staan geen woorden in het gebruikerswoordenboek. U kunt een woord toevoegen door op de knop \'Toevoegen\' (+) te tikken."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Voor alle talen"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Meer talen…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Verwijderen"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-pl/strings-appname.xml b/java/res/values-pl/strings-appname.xml
new file mode 100644
index 0000000..bcdec24
--- /dev/null
+++ b/java/res/values-pl/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Klawiatura Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Sprawdzanie pisowni w Androidzie (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Ustawienia klawiatury Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Ustawienia sprawdzania pisowni w Androidzie (AOSP)"</string>
+</resources>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index fc95671..0a8f568 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Klawiatura Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Ustawienia klawiatury Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Sprawdzanie pisowni na Androidzie (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Ustawienia sprawdzania pisowni na Androidzie (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opcje wprowadzania"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Polecenia dziennika badań"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Przeszukaj kontakty"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dwukrotne kliknięcie spacji wstawia kropkę ze spacją"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Wstawiaj wielkie litery"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Zaczynaj każde zdanie wielką literą"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Słownik osobisty"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dodatkowe słowniki"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Słownik główny"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Pokazuj propozycje poprawek"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Zawsze pokazuj"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Pokaż w trybie pionowym"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Zawsze ukrywaj"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Blokuj obraźliwe słowa"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Nie proponuj słów potencjalnie obraźliwych"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Autokorekta"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Spacja i znaki przestankowe poprawiają błędnie wpisane słowa"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Wyłącz"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Czy na pewno zainstalować ten plik dla języka: <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Wystąpił błąd"</string>
     <string name="button_default" msgid="3988017840431881491">"Domyślne"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Język, klawiatura, głos"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Wybierz metodę wprowadzania"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Witamy w aplikacji <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"z pisaniem gestami"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Rozpocznij"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Następny krok"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Konfigurowanie aplikacji <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Włącz aplikację <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Zaznacz aplikację „<xliff:g id="APPLICATION_NAME">%s</xliff:g>” w ustawieniach Język, klawiatura i głos. Umożliwi to jej uruchamianie na urządzeniu."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"Aplikacja <xliff:g id="APPLICATION_NAME">%s</xliff:g> jest już włączona w Ustawieniach języka i wprowadzania danych. Przejdź do następnego kroku."</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Włącz w Ustawieniach"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Przełącz się na aplikację <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Następnie wybierz „<xliff:g id="APPLICATION_NAME">%s</xliff:g>” jako aktywną metodę wprowadzania tekstu."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Przełącz metody wprowadzania"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Gratulacje! Wszystko gotowe."</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Teraz możesz pisać we wszystkich swoich ulubionych aplikacjach, używając aplikacji <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Skonfiguruj dodatkowe języki"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Zakończone"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Pokaż ikonę aplikacji"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Wyświetl ikonę aplikacji w programie uruchamiającym"</string>
     <string name="app_name" msgid="6320102637491234792">"Dostawca słownika"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Dostawca słownika"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Usługa słownika"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Dostępny jest słownik <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Naciśnij, by sprawdzić i pobrać"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Pobieranie – wkrótce będą dostępne sugestie w tym języku: <xliff:g id="LANGUAGE">%1$s</xliff:g>."</string>
+    <string name="version_text" msgid="2715354215568469385">"Wersja <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Dodaj"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Dodaj do słownika"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Fraza"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Więcej opcji"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Mniej opcji"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Słowo:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Skrót:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Język:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Wpisz słowo"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Opcjonalny skrót"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Edytuj słowo"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Edytuj"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Usuń"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Nie masz żadnych słów w słowniku użytkownika. Aby dodać słowo, kliknij przycisk Dodaj (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Dla wszystkich języków"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Więcej języków…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Usuń"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-port/setup-dimens-small-phone-port.xml b/java/res/values-port/setup-dimens-small-phone-port.xml
new file mode 100644
index 0000000..8ac72ea
--- /dev/null
+++ b/java/res/values-port/setup-dimens-small-phone-port.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Setup wizard dimensions for small-phone/portrait. -->
+    <dimen name="setup_title_text_size">40sp</dimen>
+    <dimen name="setup_welcome_description_text_size">20sp</dimen>
+    <dimen name="setup_step_bullet_text_size">18sp</dimen>
+    <dimen name="setup_step_triangle_indicator_height">18dp</dimen>
+    <dimen name="setup_step_indicator_height">18dp</dimen>
+    <dimen name="setup_step_title_text_size">18sp</dimen>
+    <dimen name="setup_step_instruction_text_size">14sp</dimen>
+    <dimen name="setup_step_action_text_size">16sp</dimen>
+    <dimen name="setup_vertical_padding">2dp</dimen>
+    <dimen name="setup_horizontal_padding">12dp</dimen>
+    <dimen name="setup_step_action_height">42dp</dimen>
+    <dimen name="setup_step_horizontal_padding">20dp</dimen>
+    <dimen name="setup_step_horizontal_padding_half">10dp</dimen>
+    <dimen name="setup_step_vertical_padding">12dp</dimen>
+    <dimen name="setup_step_horizontal_line_height">2dp</dimen>
+    <integer name="setup_title_weight_in_screen">40</integer>
+    <integer name="setup_body_weight_in_screen">60</integer>
+    <dimen name="setup_title_end_margin">16dp</dimen>
+    <dimen name="setup_welcome_description_top_margin">4dp</dimen>
+    <dimen name="setup_welcome_video_top_padding">12dp</dimen>
+    <dimen name="setup_welcome_video_bottom_padding">12dp</dimen>
+    <integer name="setup_welcome_video_weight_in_screen">70</integer>
+    <integer name="setup_welcome_video_end_padding_weight_in_screen">30</integer>
+</resources>
diff --git a/java/res/values-pt-rPT/strings-appname.xml b/java/res/values-pt-rPT/strings-appname.xml
new file mode 100644
index 0000000..368207c
--- /dev/null
+++ b/java/res/values-pt-rPT/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Teclado Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Verificador Ortográfico Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Definições do Teclado Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Definições do Verificador Ortográfico Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index 479ff94..afd01ea 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Teclado Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Definições do Teclado Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Verificador Ortográfico Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Definições do Verificador Ortográfico Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opções de introdução"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Comandos de Reg. Invest."</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Procurar nomes de contac."</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Tocar duas vezes na barra espaço insere ponto seguido de espaço"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Letras maiúsculas automáticas"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Maiúscula no início da frase"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Dicionário pessoal"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dicionários extras"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Dicionário principal"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Mostrar sugestões de correcção"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar sempre"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Mostrar em modo retrato"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ocultar sempre"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Bloquear palavras ofensivas"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Não sugerir palavras potencialmente ofensivas"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Correção automática"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Correcção automática de palavras mal escritas c/ barra de espaços e pontuação"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desligar"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Instalar mesmo este ficheiro para <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Ocorreu um erro"</string>
     <string name="button_default" msgid="3988017840431881491">"Predefinido"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Idioma e entrada de som"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Escolher o método de entrada"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Bem-vindo(a) a <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"com a Escrita com Gestos"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Começar"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Passo seguinte"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Configurar <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Ativar <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Marque \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" nas definições de Idioma e introdução p/ autorizar a execução no dispositivo."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> já está ativado nas Definições de idioma e introdução, por isso, este passo está concluído. Passemos ao seguinte!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Ativar nas Definições"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Mudar para <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Em seguida, selecione \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" como o seu método de introdução de texto ativo."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Mudar métodos de introdução"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Parabéns, está pronto para começar!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Agora pode escrever em todas as suas aplicações favoritas com <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Configurar idiomas adicionais"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Concluído"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Mostrar ícone da aplicação"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Mostrar ícone da aplicação no iniciador"</string>
     <string name="app_name" msgid="6320102637491234792">"Fornecedor de Dicionário"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Fornecedor de Dicionário"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Serviço de Dicionário"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Está disponível um dicionário para <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Prima para consultar e transferir"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"A transferir: as sugestões para <xliff:g id="LANGUAGE">%1$s</xliff:g> estarão prontas em breve."</string>
+    <string name="version_text" msgid="2715354215568469385">"Versão <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Adicionar"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Adicionar ao dicionário"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Expressão"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Mais opções"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Menos opções"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Palavra:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Atalho:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Idioma:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Escreva uma palavra"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Atalho opcional"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Editar palavra"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Editar"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Eliminar"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Não tem palavras no dicionário do utilizador. Para adicionar uma palavra, toque no botão Adicionar (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Em todos os idiomas"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Mais idiomas..."</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Eliminar"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-pt/strings-appname.xml b/java/res/values-pt/strings-appname.xml
new file mode 100644
index 0000000..7180a0c
--- /dev/null
+++ b/java/res/values-pt/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Teclado Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Corretor ortográfico do Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Configurações de teclado Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Configurações de corretor ortográfico do Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index dc01c5b..907eee8 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Teclado Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Configurações de teclado Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Corretor ortográfico do Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Configurações de corretor ortográfico do Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opções de entrada"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Pesq. comandos de reg."</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Buscar nomes de contatos"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Toque duplo na barra de espaço insere um ponto seguido de espaço"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Capitaliz. automática"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Iniciar a primeira palavra de cada frase com letra maiúscula"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Dicionário pessoal"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dicionários complementares"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Dicionário principal"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Exibir sugestões de correção"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar sempre"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Mostrar em modo de retrato"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Sempre ocultar"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Bloquear palavras ofensivas"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Não sugerir palavras potencialmente ofensivas"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Correção automática"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"A barra de espaço e a pontuação corrigem automaticamente palavras com erro de digitação"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desativado"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Deseja instalar este arquivo para <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Ocorreu um erro"</string>
     <string name="button_default" msgid="3988017840431881491">"Padrão"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Idioma e entrada"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Selecione o método de entrada"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Bem-vindo ao <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"com entrada por gestos"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Começar"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Próxima etapa"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Configurando o <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Ative o <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Marque \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" em \"Configurações de idioma e entrada\" para autorizar a execução."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> já está ativado em suas configurações de idioma e entrada. Esta etapa está concluída. Vamos avançar para a próxima!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Ativar em \"Configurações\""</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Abra o <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Em seguida, selecione \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" como o método de entrada de texto ativo."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Alternar métodos de entrada"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Parabéns, você terminou!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Agora você pode digitar em todos os seus aplicativos favoritos com o <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Configurar idiomas adicionais"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Concluído"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Mostrar ícone do aplicativo"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Mostrar ícone do aplicativo no iniciador"</string>
     <string name="app_name" msgid="6320102637491234792">"Provedor de dicionário"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Provedor de dicionário"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Serviço de dicionário"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Há um dicionário disponível para <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Pressione para consultar e fazer o download"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Download em andamento: as sugestões para <xliff:g id="LANGUAGE">%1$s</xliff:g> estarão prontas em breve."</string>
+    <string name="version_text" msgid="2715354215568469385">"Versão <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Adicionar"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Adicionar ao dicionário"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Frase"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Mais opções"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Menos opções"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"Ok"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Palavra:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Atalho:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Idioma:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Digite uma palavra"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Atalho opcional"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Editar palavra"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Editar"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Excluir"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Você não possui palavras no dicionário do usuário. Adicione uma palavra tocando no botão \"Adicionar\" ou no símbolo \"+\"."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Para todos os idiomas"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Mais idiomas..."</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Excluir"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-rm/strings.xml b/java/res/values-rm/strings.xml
index 15cd327..f777c52 100644
--- a/java/res/values-rm/strings.xml
+++ b/java/res/values-rm/strings.xml
@@ -20,14 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
     <!-- no translation found for english_ime_input_options (3909945612939668554) -->
     <skip />
     <!-- no translation found for english_ime_research_log (8492602295696577851) -->
@@ -82,6 +74,8 @@
     <string name="auto_cap" msgid="1719746674854628252">"Maiusclas automaticas"</string>
     <!-- no translation found for auto_cap_summary (7934452761022946874) -->
     <skip />
+    <!-- no translation found for edit_personal_dictionary (3996910038952940420) -->
+    <skip />
     <!-- no translation found for configure_dictionaries_title (4238652338556902049) -->
     <skip />
     <!-- no translation found for main_dictionary (4798763781818361168) -->
@@ -96,6 +90,10 @@
     <skip />
     <!-- no translation found for prefs_suggestion_visibility_hide_name (6309143926422234673) -->
     <skip />
+    <!-- no translation found for prefs_block_potentially_offensive_title (5078480071057408934) -->
+    <skip />
+    <!-- no translation found for prefs_block_potentially_offensive_summary (2371835479734991364) -->
+    <skip />
     <!-- no translation found for auto_correction (7630720885194996950) -->
     <skip />
     <!-- no translation found for auto_correction_summary (5625751551134658006) -->
@@ -310,9 +308,41 @@
     <skip />
     <!-- no translation found for button_default (3988017840431881491) -->
     <skip />
-    <!-- no translation found for language_settings (1671153053201809031) -->
+    <!-- no translation found for setup_welcome_title (6112821709832031715) -->
     <skip />
-    <!-- no translation found for select_input_method (4301602374609275003) -->
+    <!-- no translation found for setup_welcome_additional_description (8150252008545768953) -->
+    <skip />
+    <!-- no translation found for setup_start_action (8936036460897347708) -->
+    <skip />
+    <!-- no translation found for setup_next_action (371821437915144603) -->
+    <skip />
+    <!-- no translation found for setup_steps_title (6400373034871816182) -->
+    <skip />
+    <!-- no translation found for setup_step1_title (3147967630253462315) -->
+    <skip />
+    <!-- no translation found for setup_step1_instruction (2578631936624637241) -->
+    <skip />
+    <!-- no translation found for setup_step1_finished_instruction (10761482004957994) -->
+    <skip />
+    <!-- no translation found for setup_step1_action (4366513534999901728) -->
+    <skip />
+    <!-- no translation found for setup_step2_title (6860725447906690594) -->
+    <skip />
+    <!-- no translation found for setup_step2_instruction (9141481964870023336) -->
+    <skip />
+    <!-- no translation found for setup_step2_action (1660330307159824337) -->
+    <skip />
+    <!-- no translation found for setup_step3_title (3154757183631490281) -->
+    <skip />
+    <!-- no translation found for setup_step3_instruction (8025981829605426000) -->
+    <skip />
+    <!-- no translation found for setup_step3_action (600879797256942259) -->
+    <skip />
+    <!-- no translation found for setup_finish_action (276559243409465389) -->
+    <skip />
+    <!-- no translation found for show_setup_wizard_icon (5008028590593710830) -->
+    <skip />
+    <!-- no translation found for show_setup_wizard_icon_summary (4119998322536880213) -->
     <skip />
     <!-- no translation found for app_name (6320102637491234792) -->
     <skip />
@@ -373,4 +403,44 @@
     <skip />
     <!-- no translation found for toast_downloading_suggestions (1313027353588566660) -->
     <skip />
+    <!-- no translation found for version_text (2715354215568469385) -->
+    <skip />
+    <!-- no translation found for user_dict_settings_add_menu_title (1254195365689387076) -->
+    <skip />
+    <!-- no translation found for user_dict_settings_add_dialog_title (4096700390211748168) -->
+    <skip />
+    <!-- no translation found for user_dict_settings_add_screen_title (5818914331629278758) -->
+    <skip />
+    <!-- no translation found for user_dict_settings_add_dialog_more_options (5671682004887093112) -->
+    <skip />
+    <!-- no translation found for user_dict_settings_add_dialog_less_options (2716586567241724126) -->
+    <skip />
+    <!-- no translation found for user_dict_settings_add_dialog_confirm (4703129507388332950) -->
+    <skip />
+    <!-- no translation found for user_dict_settings_add_word_option_name (6665558053408962865) -->
+    <skip />
+    <!-- no translation found for user_dict_settings_add_shortcut_option_name (3094731590655523777) -->
+    <skip />
+    <!-- no translation found for user_dict_settings_add_locale_option_name (4738643440987277705) -->
+    <skip />
+    <!-- no translation found for user_dict_settings_add_word_hint (4902434148985906707) -->
+    <skip />
+    <!-- no translation found for user_dict_settings_add_shortcut_hint (2265453012555060178) -->
+    <skip />
+    <!-- no translation found for user_dict_settings_edit_dialog_title (3765774633869590352) -->
+    <skip />
+    <!-- no translation found for user_dict_settings_context_menu_edit_title (6812255903472456302) -->
+    <skip />
+    <!-- no translation found for user_dict_settings_context_menu_delete_title (8142932447689461181) -->
+    <skip />
+    <!-- no translation found for user_dict_settings_empty_text (558499587532668203) -->
+    <skip />
+    <!-- no translation found for user_dict_settings_all_languages (8276126583216298886) -->
+    <skip />
+    <!-- no translation found for user_dict_settings_more_languages (7131268499685180461) -->
+    <skip />
+    <!-- no translation found for user_dict_settings_delete (110413335187193859) -->
+    <skip />
+    <!-- no translation found for user_dict_fast_scroll_alphabet (5431919401558285473) -->
+    <skip />
 </resources>
diff --git a/java/res/values-ro/strings-appname.xml b/java/res/values-ro/strings-appname.xml
new file mode 100644
index 0000000..3b0ca4f
--- /dev/null
+++ b/java/res/values-ro/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Tastatură Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Verificator ortografic Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Setări tastatură Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Setări verificator ortografic Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index 9081f03..0c81a62 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Tastatură Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Setări tastatură Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Verificator ortografic Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Setări verificator ortografic Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opţiuni de introducere text"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Comenzi jurnal cercetare"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Verificare nume în agendă"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dubla atingere a barei de spațiu inserează punct urmat de spațiu"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Auto-capitalizare"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Scrie cu majusculă primul cuvânt din fiecare propoziţie"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Dicționar personal"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dicţionare suplimentare"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Dicţionar principal"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Afişaţi sugestii de corectare"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Afişaţi întotdeauna"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Afişaţi în modul Portret"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ascundeţi întotdeauna"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Blocați cuvintele jignitoare"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Nu se sugerează cuvinte potențial jignitoare"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Autocorectare"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Corectare automată cuvinte prin bară spaţiu/semne punctuaţie"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Dezactivată"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Doriți să instalați acest fișier pentru <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"A apărut o eroare"</string>
     <string name="button_default" msgid="3988017840431881491">"Prestabilit"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Limbă și introducere de text"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Alegeți metoda de introducere de text"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Bun venit la <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"cu Tastarea gestuală"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Începeți"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Pasul următor"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Configurarea <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Activați <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Bifați „<xliff:g id="APPLICATION_NAME">%s</xliff:g>” din setările Limbă și introducere de text. Astfel, o autorizați să ruleze pe dispozitiv."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> este activată deja în setările Limbă și introducere de text, deci ați completat acest pas. Treceți acum la următorul!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Activați în Setări"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Comutați la <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Apoi, selectați „<xliff:g id="APPLICATION_NAME">%s</xliff:g>” ca metodă de introducere a textului activă."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Comutați între metodele de introducere a textului"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Felicitări, sunteți gata!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Acum, puteți introduce text în toate aplicațiile preferate, utilizând <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Configurați limbi suplimentare"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Finalizat"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Afișați pictograma aplicației"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Afișați pictograma aplicației în lansator"</string>
     <string name="app_name" msgid="6320102637491234792">"Furnizor dicționar"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Furnizor dicționar"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Serviciu dicționar"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Este disponibil un dicționar pentru <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Apăsați pentru examinare și descărcare"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Se descarcă: sugestiile pentru <xliff:g id="LANGUAGE">%1$s</xliff:g> vor fi gata în curând."</string>
+    <string name="version_text" msgid="2715354215568469385">"Versiunea <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Adăugați"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Adăugați în dicționar"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Expresie"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Alte opțiuni"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Puține opțiuni"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Cuvânt:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Comandă rapidă:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Limbă:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Introduceți un cuvânt"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Comandă rapidă opțională"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Editați cuvântul"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Editați"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Ștergeți"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Nu există cuvinte în dicționarul utilizatorului. Puteți adăuga un cuvânt atingând butonul Adăugați (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Pentru toate limbile"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Mai multe limbi…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Ștergeți"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" AĂÂBCDEFGHIÎJKLMNOPQRSȘTȚUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-ru/strings-appname.xml b/java/res/values-ru/strings-appname.xml
new file mode 100644
index 0000000..d364d44
--- /dev/null
+++ b/java/res/values-ru/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Клавиатура Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Проверка правописания Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Настройки клавиатуры Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Настройки проверки правописания Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index 7bf10ae..3ee9869 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Клавиатура Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Настройки клавиатуры Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Проверка правописания Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Настройки проверки правописания Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Настройки"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Все команды"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Поиск контактов"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Вводить точку с пробелом двойным нажатием кнопки \"Пробел\"."</string>
     <string name="auto_cap" msgid="1719746674854628252">"Заглавные автоматически"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Писать первое слово предложения с прописной буквы"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Пользовательский словарь"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Дополнительные словари"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Основной словарь"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Варианты исправлений"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Всегда предлагать"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Только в вертикальном режиме"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Никогда не предлагать"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Блокировка нецензурных слов"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Не предлагать слова, которые могут быть сочтены оскорбительными"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Автоисправление"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Автоматическое исправление опечаток при вводе знака препинания или пробела"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Откл."</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Установить этот файл для следующего языка: <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Ошибка"</string>
     <string name="button_default" msgid="3988017840431881491">"По умолчанию"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Язык и ввод"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Выберите способ ввода"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Представляем приложение \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\""</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"с непрерывным вводом"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Начать работу"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Далее"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>: настройка"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>: включение"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Установите флажок <xliff:g id="APPLICATION_NAME">%s</xliff:g> в меню \"Язык и ввод\", чтобы использовать этот способ ввода на устройстве."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"Приложение <xliff:g id="APPLICATION_NAME">%s</xliff:g> уже включено в настройках языка и ввода, поэтому можно перейти к следующему шагу."</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Включить в настройках"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>: активация"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Выберите приложение \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" как текущий способ ввода."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Другой способ ввода"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Настройте дополнительные языки."</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Теперь вы можете использовать приложение \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" для набора текста."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Настроить дополнительные языки"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Готово"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Показывать значок приложения"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Отображать значок приложения на панели запуска"</string>
     <string name="app_name" msgid="6320102637491234792">"Поставщик словарей"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Поставщик словарей"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Служба словарей"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Доступен словарь: <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Нажмите, чтобы просмотреть и загрузить"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Загрузка словаря: <xliff:g id="LANGUAGE">%1$s</xliff:g>…"</string>
+    <string name="version_text" msgid="2715354215568469385">"Версия <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Добавить"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Добавление в словарь"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Фраза"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Больше настроек"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Меньше настроек"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"ОК"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Слово:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Быстрые клавиши:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Язык:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Введите слово"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Ярлык для фразы (необязательно)"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Изменение слова"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Изменить"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Удалить"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"В словаре пользователя пока ничего нет. Добавлять слова можно с помощью кнопки \"Добавить ( + )\"."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Для всех языков"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Другой язык"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Удалить"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ"</string>
 </resources>
diff --git a/java/res/values-sk/strings-appname.xml b/java/res/values-sk/strings-appname.xml
new file mode 100644
index 0000000..1a7a43a
--- /dev/null
+++ b/java/res/values-sk/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Klávesnica Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Kontrola pravopisu (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Nastavenia klávesnice Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Nastavenia kontroly pravopisu Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml
index b01e325..a52a5b9 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Klávesnica Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Nastavenia klávesnice Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Kontrola pravopisu (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Nastavenia kontroly pravopisu Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti zadávania textu a údajov"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Príkazy denníka výskumu"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Vyhľadať kontakty"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dvojitým klepnutím na medzerník vložíte bodku a medzeru."</string>
     <string name="auto_cap" msgid="1719746674854628252">"Veľké písmená automaticky"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Písanie prvého slova v každej vete veľkým písmenom"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Osobný slovník"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Doplnkové slovníky"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Hlavný slovník"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Zobraziť návrhy opráv"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vždy zobrazovať"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Zobraziť v režime na výšku"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Vždy skrývať"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Blokovať urážlivé slová"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Nenavrhovať potenciálne urážlivé slová"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Automatické opravy"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Stlačením medzerníka a interpunkcie sa aut. opravia chybné slová"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Vypnuté"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Chcete nainštalovať tento súbor pre jazyk <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Vyskytla sa chyba"</string>
     <string name="button_default" msgid="3988017840431881491">"Predvolené"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Jazyk &amp; vstup"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Zvoliť metódu vstupu"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Vitajte v aplikácii <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"s funkciou Písanie gestami"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Začať"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Ďalší krok"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Nastavenie aplikácie <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Povoľte aplikáciu <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"V nastaveniach vstupu a jazyka začiarknite políčko <xliff:g id="APPLICATION_NAME">%s</xliff:g>. Týmto aplikácii povolíte spustenie v zariadení."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"Aplikácia <xliff:g id="APPLICATION_NAME">%s</xliff:g> je už povolená v Nastaveniach jazyka a vstupu. Prejdite na ďalší krok."</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Povoliť v Nastaveniach"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Prepnite na aplikáciu <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Potom vyberte aplikáciu <xliff:g id="APPLICATION_NAME">%s</xliff:g> ako aktívnu metódu textového vstupu."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Prepnúť metódu vstupu"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Blahoželáme, všetko je nastavené!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Pomocou aplikácie <xliff:g id="APPLICATION_NAME">%s</xliff:g> teraz môžete zadávať text vo všetkých obľúbených aplikáciách."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Nakonfigurujte ďalšie jazyky"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Hotovo"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Zobraziť ikonu aplikácie"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Zobrazenie ikony aplikácie v spúšťači"</string>
     <string name="app_name" msgid="6320102637491234792">"Poskytovateľ slovníka"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Poskytovateľ slovníka"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Služba slovníka"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"K dispozícii je slovník pre jazyk <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Stlačením skontrolujete a prevezmete"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Preberanie: návrhy pre jazyk <xliff:g id="LANGUAGE">%1$s</xliff:g> budú čoskoro k dispozícii."</string>
+    <string name="version_text" msgid="2715354215568469385">"Verzia <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Pridať"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Pridať do slovníka"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Fráza"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Ďalšie možnosti"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Menej možností"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Slovo:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Klávesová skratka"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Jazyk:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Zadajte slovo"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Voliteľná skratka"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Upraviť slovo"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Upraviť"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Odstrániť"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"V používateľskom slovníku nie sú žiadne slová. Slovo pridáte dotknutím sa tlačidla Pridať (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Pre všetky jazyky"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Ďalšie jazyky…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Odstrániť"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-sl/strings-appname.xml b/java/res/values-sl/strings-appname.xml
new file mode 100644
index 0000000..5a9c017
--- /dev/null
+++ b/java/res/values-sl/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Tipkovnica za Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Črkovalnik za Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Nastavitve tipkovnice za Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Nastavitve črkovalnika za Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index 9243b12..c09e2e0 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Tipkovnica za Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Nastavitve tipkovnice za Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Črkovalnik za Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Nastavitve črkovalnika za Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti vnosa"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Ukazi za dnevnik raziskav"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Iskanje imen stikov"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Z dvojnim dotikom preslednice vstavite piko in za njo presledek"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Samod. velike začetnice"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Prvo besedo stavka piši z veliko začetnico"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Osebni slovar"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dodatni slovarji"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Glavni slovar"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Pokaži predloge popravkov"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vedno pokaži"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Prikaži v pokončnem načinu"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Vedno skrij"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Blokiraj žaljive besede"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Ne predlagaj potencialno žaljivih besed"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Samodejni popravek"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Preslednica in ločila samodejno popravijo napačno vtipkane besede"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Izklopljeno"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Zares želite namestiti to datoteko za jezik <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Prišlo je do napake"</string>
     <string name="button_default" msgid="3988017840431881491">"Privzeto"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Jezik in vnos"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Izbira načina vnosa"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Pozdravljeni v aplikaciji <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"s pisanjem s kretnjami"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Začnite"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Naslednji korak"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Nastavitev aplikacije <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Omogočanje aplikacije <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"V nastavitvah za jezik in vnos izberite aplikacijo »<xliff:g id="APPLICATION_NAME">%s</xliff:g>«. S tem ji omogočite izvajanje v napravi."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"Aplikacija <xliff:g id="APPLICATION_NAME">%s</xliff:g> je že omogočena v nastavitvah jezika in vnosa, zato je to že opravljeno. Nadaljujte."</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Omogoči v nastavitvah"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Preklop na aplikacijo <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Nato izberite aplikacijo »<xliff:g id="APPLICATION_NAME">%s</xliff:g>« kot aktivni način vnosa besedila."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Preklopi način vnosa"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Čestitamo, pripravljeni ste."</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Zdaj lahko z aplikacijo <xliff:g id="APPLICATION_NAME">%s</xliff:g> tipkate v vseh svojih priljubljenih aplikacijah."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Konfiguracija dodatnih jezikov"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Končano"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Pokaži ikono aplikacije"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Prikaz ikone aplikacije v zaganjalniku"</string>
     <string name="app_name" msgid="6320102637491234792">"Ponudnik slovarja"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Ponudnik slovarja"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Storitev slovarja"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Slovar je na voljo za jezik <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Pritisnite za pregled in prenos"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Predlogi za prenos za jezik <xliff:g id="LANGUAGE">%1$s</xliff:g> bodo kmalu pripravljeni."</string>
+    <string name="version_text" msgid="2715354215568469385">"Različica <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Dodaj"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Dodaj v slovar"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Besedna zveza"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Več možnosti"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Manj možnosti"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"V redu"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Beseda:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Bližnjica:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Jezik:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Vnesite besedo"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Izbirna bližnjica"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Uredi besedo"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Uredi"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Izbriši"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"V uporabniškem slovarju ni besed. Besede lahko dodate z dotikom gumba »Dodaj« (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Za vse jezike"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Več jezikov ..."</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Izbriši"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-sr/strings-appname.xml b/java/res/values-sr/strings-appname.xml
new file mode 100644
index 0000000..a8c610b
--- /dev/null
+++ b/java/res/values-sr/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Android тастатура (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android провера правописа (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Подешавања Android тастатуре (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Подешавања Android провере правописа (AOSP)"</string>
+</resources>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index cede67c..c44671a 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android тастатура (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Подешавања Android тастатуре (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android провера правописа (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Подешавања Android провере правописа (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Опције уноса"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Команде евиденције истраживања"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Потражи имена контаката"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Двоструким додиром размака умеће се тачка праћена размаком"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Аутоматски унос великих слова"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Писање великог слова на почетку сваке реченице"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Лични речник"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Помоћни речници"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Главни речник"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Прикажи предлоге за исправку"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Увек прикажи"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Прикажи у усправном режиму"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Увек сакриј"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Блокирај увредљиве речи"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Нема предлагања потенцијално увредљивих речи"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Аутом. исправљање"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Размак и интерпункција аутоматски исправљају грешке у куцању"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Искључи"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Желите ли стварно да инсталирате ову датотеку за <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Дошло је до грешке"</string>
     <string name="button_default" msgid="3988017840431881491">"Подразумевано"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Језик и унос"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Избор метода уноса"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Добро дошли у <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"помоћу Куцања покретима"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Започнимо"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Следећи корак"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Подешавање апликације <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Омогућите апликацију <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Потврдите апликацију „<xliff:g id="APPLICATION_NAME">%s</xliff:g>“ у Подешавањима језика и уноса. Тако јој одобравате покретање на уређају."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"Апликација <xliff:g id="APPLICATION_NAME">%s</xliff:g> је већ омогућена у Подешавањима језика и уноса тако да је овај корак готов. Пређимо на следећи!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Омогући у Подешавањима"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Пребаците на <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Затим изаберите „<xliff:g id="APPLICATION_NAME">%s</xliff:g>“ као активни метод уноса текста."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Пребаци методе уноса"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Честитамо, све је спремно!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Сада можете да куцате у свим омиљеним апликацијама помоћу <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Конфигуриши додатне језике"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Завршено"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Прикажи икону апликације"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Икона апликације се приказује у покретачу"</string>
     <string name="app_name" msgid="6320102637491234792">"Добављач речника"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Добављач речника"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Услуга речника"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Речник је доступан за <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Притисните за преглед и преузимање"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Преузимање: Предлози за <xliff:g id="LANGUAGE">%1$s</xliff:g> ће ускоро бити спремни."</string>
+    <string name="version_text" msgid="2715354215568469385">"Верзија <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Додај"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Додавање у речник"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Фраза"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Више опција"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Мање опција"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"Потврди"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Реч:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Пречица:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Језик:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Унесите реч"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Опционална пречица"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Измена речи"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Измени"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Избриши"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Немате ниједну реч у корисничком речнику. Додајте реч додиром на дугме Додај (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"За све језике"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Још језика..."</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Избриши"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" АБВГДЂЕЖЗИЈКЛЉМНЊОПРСТЋУФХЦЧЏШ"</string>
 </resources>
diff --git a/java/res/values-sv/strings-appname.xml b/java/res/values-sv/strings-appname.xml
new file mode 100644
index 0000000..5d750cc
--- /dev/null
+++ b/java/res/values-sv/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Androids tangentbord (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Stavningskontroll i Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Inställningar för Androids tangentbord (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Inställningar för Androids stavningskontroll (AOSP)"</string>
+</resources>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index 8ac6c2c..1b0bd01 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Androids tangentbord (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Inställningar för Androids tangentbord (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Stavningskontroll i Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Inställningar för Androids stavningskontroll (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Inmatningsalternativ"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Loggkommandon"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Sök namn på kontakter"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dubbelt blanksteg ger en punkt följt av mellanslag"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Automatiska versaler"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Automatisk stor bokstav först i varje mening"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Egen ordlista"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Tilläggsordlistor"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Huvudordlistan"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Visa rättningsförslag"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Visa alltid"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Visa i stående format"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Dölj alltid"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Blockera stötande ord"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Visa inte förslag på ord som kan verka stötande"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Autokorrigering"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Blanksteg/skiljetecken rättar felstavning"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Av"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Vill du verkligen installera filen för <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Ett fel uppstod"</string>
     <string name="button_default" msgid="3988017840431881491">"Standard"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Språk &amp; inmatning"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Välj inmatningsmetod"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Välkommen till <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"med svepskrivning"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Kom igång"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Nästa steg"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Konfigurera <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Aktivera <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Markera <xliff:g id="APPLICATION_NAME">%s</xliff:g> i inställningarna för språk och inmatning så att appen kan köras."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> har redan aktiverats i inställningarna för Språk och inmatning och det här steget är färdigt. Fortsätt till nästa steg."</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Aktivera i inställningarna"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Byt till <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Välj sedan <xliff:g id="APPLICATION_NAME">%s</xliff:g> som din aktiva textinmatningsmetod."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Byt inmatningsmetod"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Grattis! Nu är det klart."</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Nu kan du skriva i alla dina favoritappar med <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Konfigurera ytterligare språk"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Slutförda"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Visa appikon"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Visa appikonen i startprogrammet"</string>
     <string name="app_name" msgid="6320102637491234792">"Dictionary Provider"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Dictionary Provider"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Ordlistetjänst"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"En ordlista är tillgänglig för <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Tryck om du vill granska och hämta"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Hämtar: förslag för <xliff:g id="LANGUAGE">%1$s</xliff:g> är snart klara."</string>
+    <string name="version_text" msgid="2715354215568469385">"Version <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Lägg till"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Lägg till i ordlista"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Fras"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Fler alternativ"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Färre alternativ"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Ord:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Genväg:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Språk:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Skriv ett ord"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Valfri genväg"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Redigera ord"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Redigera"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Ta bort"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Det finns inga ord i användarordlistan. Du kan lägga till ord med knappen Lägg till (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"För alla språk"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Fler språk ..."</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Ta bort"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-sw/strings-appname.xml b/java/res/values-sw/strings-appname.xml
new file mode 100644
index 0000000..354afdd
--- /dev/null
+++ b/java/res/values-sw/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Kibodi ya Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Kikagua-tahajia cha Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Mipangilio ya Kibodi ya Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Mipangilio ya Kikagua-tahajia cha Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml
index 3ae4009..53e46aa 100644
--- a/java/res/values-sw/strings.xml
+++ b/java/res/values-sw/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Kibodi ya Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Mipangilio ya Kibodi ya Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Kikagua-tahajia cha Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Mipangilio ya Kikagua-tahajia cha Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Chaguo za uingizaji"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Amri za Kumbukumbu za Utafiti"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Angalia majina ya unaowasiliana nao"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Kugonga mara mbili kwenye upau nafasi kunaingiza kitone kikifuatiwa na nafasi"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Uwekaji wa herufi kubwa kiotomatiki"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Fanya herufi kubwa neno la kwanza la kila sentensi"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Kamusi ya kibinafsi"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Nyongeza za kamusi"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Kamusi kuu"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Onyesha mapendekezo ya marekebisho"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Onyesha kila wakati"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Onyesha katika hali wima"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ficha kila wakati"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Zuia maneno yanayokera"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Usipendekeze maneno yanayoweza kukera"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Usahihishaji otomatiki"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Kiaamba na kiakifishi hurekebisha maneno ambayo yamechapishwa vibaya"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Zima"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Isakinishe faili hii kwa <xliff:g id="LOCALE_NAME">%s</xliff:g> kweli?"</string>
     <string name="error" msgid="8940763624668513648">"Kulikuwa na hitilafu"</string>
     <string name="button_default" msgid="3988017840431881491">"Chaguo-msingi"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Lugha na uingizaji"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Chagua mbinu ya kuingiza data"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Karibu kwenye <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"kwa Kuandika kwa ishara"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Anza kutumia"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Hatua inayofuata"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Inasanidi <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Washa <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Tafadhali angalia \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" katika lugha yako na mipangilio ya kuingiza. Hii itaidhinisha ili iendeshwe kwenye kifaa chako."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> tayari imewashwa katika mipangilio yako ya Lugha, Kibodi na Sauti, kwa hivyo hatua hii imekamilika. Nenda kwenye hatua inayofuata!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Washa katika Mipangilio"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Badilisha kwenda <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Kisha, chagua \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" kama mbinu yako inayotumika ya kuingiza data ya maandishi."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Badilisha mbinu za kuingiza data"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Hongera, uko tayari!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Sasa unaweza kuchapa programu zako zote uzipendazo ukitumia <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Sanidi lugha za ziada"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Imemaliza"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Onyesha ikoni ya programu"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Onyesha ikoni ya programu kwenye kizinduzi"</string>
     <string name="app_name" msgid="6320102637491234792">"Programu ya kamusi"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Programu ya kamusi"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Huduma ya Kamusi"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Kamusi ya <xliff:g id="LANGUAGE">%1$s</xliff:g> inapatikana"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Bonyeza ili kukagua na kupakua"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Inapakua: mapendekezo ya <xliff:g id="LANGUAGE">%1$s</xliff:g> yatakuwa tayari hivi karibuni."</string>
+    <string name="version_text" msgid="2715354215568469385">"Toleo la <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Ongeza"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Ongeza kwenye kamusi"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Fungu la maneno"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Hiari zingine"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Hiari chache"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"Sawa"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Neno:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Njia ya mkato:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Lugha:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Chapa neno"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Njia ya mkato ya hiari"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Badilisha neno"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Hariri"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Futa"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Huna maneno yoyote katika kamusi ya mtumiaji. Ongeza neno kwa kugusa kitufe cha Ongeza (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Ya lugha zote"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Lugha zingine..."</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Futa"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-sw600dp-land/setup-dimens.xml b/java/res/values-sw600dp-land/setup-dimens.xml
deleted file mode 100644
index 9aea214..0000000
--- a/java/res/values-sw600dp-land/setup-dimens.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <dimen name="setup_title_text_size">64sp</dimen>
-    <dimen name="setup_horizontal_padding">96dp</dimen>
-</resources>
diff --git a/java/res/values-sw600dp/config.xml b/java/res/values-sw600dp/config.xml
index 9527fd6..8265651 100644
--- a/java/res/values-sw600dp/config.xml
+++ b/java/res/values-sw600dp/config.xml
@@ -19,8 +19,6 @@
 -->
 
 <resources>
-    <!-- Device form factor. This value must be aligned with {@link KeyboardId.FORM_FACTOR_TABLET7} -->
-    <integer name="config_device_form_factor">1</integer>
     <bool name="config_enable_show_voice_key_option">false</bool>
     <bool name="config_enable_show_option_of_key_preview_popup">false</bool>
     <bool name="config_enable_bigram_suggestions_option">false</bool>
diff --git a/java/res/values-sw600dp/dimens.xml b/java/res/values-sw600dp/dimens.xml
index 586fbe6..75b476c 100644
--- a/java/res/values-sw600dp/dimens.xml
+++ b/java/res/values-sw600dp/dimens.xml
@@ -85,8 +85,8 @@
     <dimen name="suggestion_text_size">22dp</dimen>
     <dimen name="more_suggestions_hint_text_size">33dp</dimen>
 
-    <!-- Gesture preview trail parameters -->
-    <dimen name="gesture_preview_trail_width">2.5dp</dimen>
+    <!-- Gesture trail parameters -->
+    <dimen name="gesture_trail_width">2.5dp</dimen>
     <!-- Gesture floating preview text parameters -->
     <dimen name="gesture_floating_preview_text_size">28dp</dimen>
     <dimen name="gesture_floating_preview_text_offset">87dp</dimen>
diff --git a/java/res/values-sw768dp-land/setup-dimens.xml b/java/res/values-sw768dp-land/setup-dimens.xml
deleted file mode 100644
index 0d2af17..0000000
--- a/java/res/values-sw768dp-land/setup-dimens.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <dimen name="setup_title_text_size">64sp</dimen>
-    <dimen name="setup_horizontal_padding">192dp</dimen>
-</resources>
diff --git a/java/res/values-sw768dp/config.xml b/java/res/values-sw768dp/config.xml
index 3c2c198..97f11cb 100644
--- a/java/res/values-sw768dp/config.xml
+++ b/java/res/values-sw768dp/config.xml
@@ -19,8 +19,6 @@
 -->
 
 <resources>
-    <!-- Device form factor. This value must be aligned with {@link KeyboardId.FORM_FACTOR_TABLET10} -->
-    <integer name="config_device_form_factor">2</integer>
     <bool name="config_enable_show_voice_key_option">false</bool>
     <bool name="config_enable_show_option_of_key_preview_popup">false</bool>
     <bool name="config_enable_bigram_suggestions_option">false</bool>
diff --git a/java/res/values-sw768dp/dimens.xml b/java/res/values-sw768dp/dimens.xml
index 2fd7322..91251f5 100644
--- a/java/res/values-sw768dp/dimens.xml
+++ b/java/res/values-sw768dp/dimens.xml
@@ -86,8 +86,8 @@
     <dimen name="suggestion_text_size">22dp</dimen>
     <dimen name="more_suggestions_hint_text_size">33dp</dimen>
 
-    <!-- Gesture preview trail parameters -->
-    <dimen name="gesture_preview_trail_width">2.5dp</dimen>
+    <!-- Gesture trail parameters -->
+    <dimen name="gesture_trail_width">2.5dp</dimen>
     <!-- Gesture floating preview text parameters -->
     <dimen name="gesture_floating_preview_text_size">26dp</dimen>
     <dimen name="gesture_floating_preview_text_offset">86dp</dimen>
diff --git a/java/res/values-th/strings-appname.xml b/java/res/values-th/strings-appname.xml
new file mode 100644
index 0000000..af6d1a9
--- /dev/null
+++ b/java/res/values-th/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"แป้นพิมพ์แอนดรอยด์ (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"เครื่องตรวจตัวสะกดแอนดรอยด์ (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"การตั้งค่าแป้นพิมพ์แอนดรอยด์ (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"การตั้งค่าเครื่องตรวจตัวสะกดแอนดรอยด์ (AOSP)"</string>
+</resources>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index b6a0d66..0501936 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"แป้นพิมพ์แอนดรอยด์ (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"การตั้งค่าแป้นพิมพ์แอนดรอยด์ (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"เครื่องตรวจตัวสะกดแอนดรอยด์ (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"การตั้งค่าเครื่องตรวจตัวสะกดแอนดรอยด์ (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"ตัวเลือกการป้อนข้อมูล"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"คำสั่งบันทึกการวิจัย"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"ค้นหารายชื่อติดต่อ"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"แตะ Spacebar สองครั้งจะแทรกจุดตามด้วยช่องว่างหนึ่งช่อง"</string>
     <string name="auto_cap" msgid="1719746674854628252">"ปรับเป็นตัวพิมพ์ใหญ่อัตโนมัติ"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"ทำให้คำแรกของทุกประโยคเป็นตัวพิมพ์ใหญ่"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"พจนานุกรมส่วนตัว"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"พจนานุกรม Add-On"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"พจนานุกรมหลัก"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"แสดงคำแนะนำการแก้ไข"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"แสดงทุกครั้ง"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"แสดงในโหมดแนวตั้ง"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"ซ่อนทุกครั้ง"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"บล็อกคำที่ไม่เหมาะสม"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"ไม่แนะนำคำที่อาจไม่เหมาะสม"</string>
     <string name="auto_correction" msgid="7630720885194996950">"การแก้ไขอัตโนมัติ"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"กดเว้นวรรคและเครื่องหมายจะแก้คำผิดอัตโนมัติ"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"ปิด"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"ติดตั้งไฟล์นี้สำหรับ <xliff:g id="LOCALE_NAME">%s</xliff:g> จริงๆ หรือ"</string>
     <string name="error" msgid="8940763624668513648">"เกิดข้อผิดพลาด"</string>
     <string name="button_default" msgid="3988017840431881491">"ค่าเริ่มต้น"</string>
-    <string name="language_settings" msgid="1671153053201809031">"ภาษาและการป้อนข้อมูล"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"เลือกวิธีการป้อนข้อมูล"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"ยินดีต้อนรับสู่ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"พร้อมการป้อนข้อมูลด้วยท่าทาง"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"เริ่มต้นใช้งาน"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"ขั้นตอนถัดไป"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"การตั้งค่า <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"เปิดใช้งาน <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"โปรดตรวจสอบ \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" ในการตั้งค่าภาษาและการป้อนข้อมูลของคุณ ซึ่งจะอนุญาตให้แอปทำงานบนอุปกรณ์ของคุณ"</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> เปิดใช้งานในภาษาและการตั้งค่าการป้อนข้อมูลของคุณอยู่แล้ว ดังนั้น ขั้นตอนนี้จึงเสร็จสิ้นแล้ว ไปยังขั้นตอนต่อไป!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"เปิดใช้งานในการตั้งค่า"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"สลับไปใช้ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"ถัดไป เลือก \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" เป็นวิธีการป้อนข้อความที่ใช้งานของคุณ"</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"สลับวิธีการป้อนข้อมูล"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"ยินดีด้วย คุณพร้อมใช้งานแล้ว!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"ตอนนี้คุณสามารถพิมพ์ข้อมูลลงในแอปที่ชื่นชอบทั้งหมดด้วย <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"กำหนดค่าภาษาเพิ่มเติม"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"เสร็จสิ้น"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"แสดงไอคอนแอป"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"แสดงไอคอนแอปพลิเคชันในตัวเรียกใช้งาน"</string>
     <string name="app_name" msgid="6320102637491234792">"ผู้ให้บริการพจนานุกรม"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"ผู้ให้บริการพจนานุกรม"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"บริการพจนานุกรม"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"มีพจนานุกรมให้ใช้งานในภาษา <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"กดเพื่อตรวจสอบและดาวน์โหลด"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"กำลังดาวน์โหลด: คำแนะนำสำหรับ <xliff:g id="LANGUAGE">%1$s</xliff:g> จะพร้อมใช้งานเร็วๆ นี้"</string>
+    <string name="version_text" msgid="2715354215568469385">"เวอร์ชัน <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"เพิ่ม"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"เพิ่มในพจนานุกรม"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"ข้อความ"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"ตัวเลือกอื่น"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"ลดตัวเลือก"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"ตกลง"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"คำ:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"ทางลัด:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"ภาษา:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"พิมพ์คำ"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"ทางลัดที่ไม่บังคับ"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"แก้ไขคำ"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"แก้ไข"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"ลบ"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"คุณไม่มีคำในพจนานุกรมผู้ใช้เลย เพิ่มคำโดยแตะปุ่มเพิ่ม (+)"</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"สำหรับทุกภาษา"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"ภาษาเพิ่มเติม…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"ลบ"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-tl/strings-appname.xml b/java/res/values-tl/strings-appname.xml
new file mode 100644
index 0000000..200ee5f
--- /dev/null
+++ b/java/res/values-tl/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Android Keyboard (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Spell Checker ng Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Mga Setting ng Android Keyboard (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Mga Setting ng Spell Checker ng Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index 6f8add7..6437345 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android Keyboard (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Mga Setting ng Android Keyboard (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Spell Checker ng Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Mga Setting ng Spell Checker ng Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Mga pagpipilian sa input"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Cmmnd sa Log ng Pnnliksik"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Maghanap pangalan contact"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Naglalagay ng tuldok na may puwang ang pag-double tap sa spacebar"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Auto-capitalization"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"I-capitalize ang unang salita ng bawat pangungusap"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Personal na diksyunaryo"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Mga diksyunaryo na add-on"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Pangunahing diksyunaryo"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Magpakita ng mga suhestiyon ng pagwawasto"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Palaging ipakita"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Ipakita sa portrait na mode"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Palaging itago"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"I-block nakakapanakit na salita"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Huwag magmungkahi ng mga maaaring nakakapanakit na salita"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Awtomatiko pagwasto"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Awto tinatama ng spacebar at bantas ang maling na-type"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Naka-off"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"I-install talaga ang file na ito para sa <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Nagkaroon ng error"</string>
     <string name="button_default" msgid="3988017840431881491">"Default"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Wika at input"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Pumili ng pamamaraan ng pag-input"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Maligayang pagdating sa <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"gamit ang Gesture na Pag-type"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Magsimula"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Susunod na hakbang"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Sine-set up ang <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Paganahin ang <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Paki-check ang \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" sa mga setting mo ng Wika at input. Mapapahintulutan itong tumakbo sa device mo."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"Naka-enable na ang <xliff:g id="APPLICATION_NAME">%s</xliff:g> sa iyong Wika at mga setting ng pag-input, kaya tapos na ang hakbang na ito. Magpatuloy sa susunod!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"I-enable sa Mga Setting"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Lumipat sa <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Susunod, piliin ang \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" bilang iyong aktibong pamamaraan ng pag-input ng teksto."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Magpalit ng pamamaraan ng pag-input"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Binabati kita, handa ka na!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Ngayon, mata-type mo na ang lahat ng paborito mong apps gamit ang <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Mag-configure ng mga karagdagang wika"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Tapos na"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Ipakita ang icon ng app"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Ipakita ang icon ng application sa launcher"</string>
     <string name="app_name" msgid="6320102637491234792">"Provider ng Diksyunaryo"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Provider ng Diksyunaryo"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Serbisyo ng Diksyunaryo"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"May available na diksyunaryo para sa <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Pindutin upang suriin at i-download"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Dina-download: malapit nang maging handa ang mga suhestiyon para sa <xliff:g id="LANGUAGE">%1$s</xliff:g>."</string>
+    <string name="version_text" msgid="2715354215568469385">"Bersyon <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Idagdag"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Idagdag sa diksyunaryo"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Parirala"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Higit pa"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Mas kaunti"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Salita:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Shortcut:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Wika:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Mag-type ng salita"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Opsyonal na shortcut"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"I-edit ang salita"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"I-edit"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Tanggalin"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Wala kang anumang mga salita sa diksyunaryo ng user. Magdagdag ng salita sa pamamagitan ng pagpindot sa button na Magdagdag (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Para sa lahat ng wika"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Higit pang mga wika..."</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Tanggalin"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-tr/strings-appname.xml b/java/res/values-tr/strings-appname.xml
new file mode 100644
index 0000000..4eb8ab7
--- /dev/null
+++ b/java/res/values-tr/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Android Klavye (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android Yazım Denetleyici (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Android Klavye Ayarları (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Android Yazım Denetleyici Ayarları (AOSP)"</string>
+</resources>
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
index a0a6a67..e85372e 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android klavye (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android Klavye Ayarları (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android Yazım Denetleyici (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Android Yazım Denetleyici Ayarları (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Giriş seçenekleri"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Araştırma Günlüğü Komutları"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kişi adlarını denetle"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Boşluk çubuğuna iki kez vurmak nokta ve ardından bir boşluk ekler"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Otomatik olarak büyük harf yap"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Her cümlenin ilk kelimesini büyük harf yap"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Kişisel sözlük"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Ek sözlükler"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Ana sözlük"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Düzeltme önerilerini göster"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Her zaman göster"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Dikey modda göster"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Her zaman gizle"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Rahatsız edici kelimeleri engelle"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Rahatsız edici olabilecek kelimeleri önerme"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Otomatik düzeltme"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Boşluk tuşu ve noktalama işaretleri yanlış yazılan kelimeleri otomatikman düzeltir"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Kapalı"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"<xliff:g id="LOCALE_NAME">%s</xliff:g> için bu dosya gerçekten yüklensin mi?"</string>
     <string name="error" msgid="8940763624668513648">"Bir hata oluştu"</string>
     <string name="button_default" msgid="3988017840431881491">"Varsayılan"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Dil ve giriş"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Giriş yöntemini seçin"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> uygulamasına hoş geldiniz"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"Hareketle Yazmayı içerir"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Başlayın"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Sonraki adım"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> uygulamasını kurma"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> uygulamasını etkinleştirin"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Lütfen \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" uygulamasını kendi Dil ve giriş ayarlarınızda işaretleyin. Bu işlem, uygulamaya cihazınızda çalışma yetkisi verecektir."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> zaten Diliniz ve giriş ayarlarınızda etkinleştirilmiş durumda, dolayısıyla bu adım tamamlanmıştır. Bir sonrakine geçin!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Ayarlarda etkinleştir"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> uygulamasına geçin"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Sonra, \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" uygulamasını etkin metin giriş yönteminiz olarak seçin."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Giriş yöntemini değiştir"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Tebrikler, bitirdiniz!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Artık <xliff:g id="APPLICATION_NAME">%s</xliff:g> ile tüm favori uygulamalarınızda yazabilirsiniz."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Ek dilleri yapılandırın"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Tamamlandı"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Uygulama simgesini göster"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Uygulama simgesini başlatıcıda göster"</string>
     <string name="app_name" msgid="6320102637491234792">"Sözlük Sağlayıcı"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Sözlük Sağlayıcı"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Sözlük Hizmeti"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"<xliff:g id="LANGUAGE">%1$s</xliff:g> için kullanılabilecek bir sözlük mevcut"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"İncelemek ve indirmek için basın"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"<xliff:g id="LANGUAGE">%1$s</xliff:g> için önerilerin indirilmesine kısa süre içinde başlanacak."</string>
+    <string name="version_text" msgid="2715354215568469385">"Sürüm <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Ekle"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Sözlüğe ekle"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Kelime öbeği"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Daha çok seçenek"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Daha az seçenek"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"Tamam"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Kelime:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Kısayol:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Dil:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Bir kelime yazın"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"İsteğe bağlı kısayol"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Kelimeyi düzenle"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Düzenle"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Sil"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Kullanıcı sözlüğünde hiç kelimeniz yok. Ekle (+) düğmesini kullanarak kelime ekleyin."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Tüm diller için"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Diğer diller…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Sil"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCÇDEFGĞHIİJKLMNOÖPQRSŞTUÜVWXYZ"</string>
 </resources>
diff --git a/java/res/values-uk/strings-appname.xml b/java/res/values-uk/strings-appname.xml
new file mode 100644
index 0000000..44dd5b7
--- /dev/null
+++ b/java/res/values-uk/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Клавіатура Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Перевірка орфографії Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Налаштування клавіатури Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Налаштування перевірки орфографії Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index 876ec0f..47f2132 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Клавіатура Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Налаштування клавіатури Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Перевірка орфографії Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Налаштування перевірки орфографії Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Парам. введення"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Команди журналу дослідж."</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Шукати імена контактів"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Подвійне натискання пробілу вставляє крапку з пробілом після неї"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Авто викор. вел. літер"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Писати перше слово в кожному реченні з великої літери"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Особистий словник"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Додані словники"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Основний словник"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Показувати пропозиції виправлень"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Завжди показувати"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Показувати в книжковій орієнтації"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Завжди ховати"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Блокувати образливі слова"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Не пропонувати потенційно образливі слова"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Автовиправлення"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Пробіл і пунктуація автоматично виправляють слова з помилками"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Вимк."</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Справді встановити цей файл для такої мови: <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Сталася помилка"</string>
     <string name="button_default" msgid="3988017840431881491">"За умовчанням"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Мова та введення"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Вибрати метод введення"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Вітаємо в програмі <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"з функцією Ввід жестами"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Розпочати"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Наступний крок"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Налаштування програми <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Увімкніть програму <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Виберіть \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" у налаштуваннях \"Мова та введення\", щоб дозволити запуск цієї програми на пристрої."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"Програму <xliff:g id="APPLICATION_NAME">%s</xliff:g> уже ввімкнено в налаштуваннях мови та введення. Перейдіть до наступного кроку."</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Увімкнути в налаштуваннях"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Перейдіть до програми <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Далі виберіть \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" як поточний метод введення тексту."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Змінити метод введення"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Вітаємо! Налаштування завершено."</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Тепер ви можете вводити текст у всіх своїх улюблених програмах за допомогою <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Налаштувати додаткові мови"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Завершено"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Показувати піктограму програми"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Відображати піктограму програми на панелі запуску"</string>
     <string name="app_name" msgid="6320102637491234792">"Постачальник словника"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Постачальник словника"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Служба словника"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Доступний словник для такої мови: <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Натисніть, щоб переглянути та завантажити"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Скоро почнеться завантаження пропозицій для такої мови: <xliff:g id="LANGUAGE">%1$s</xliff:g>."</string>
+    <string name="version_text" msgid="2715354215568469385">"Версія <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Додати"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Додати в словник"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Фраза"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Інші варіанти"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Менше опцій"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"ОК"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Слово:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Ярлик:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Мова:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Введіть слово"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Необов’язковий ярлик"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Редагувати слово"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Редагувати"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Видалити"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"У словнику користувача немає жодного слова. Додайте слово, торкнувшись кнопки \"Додати\" (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Для всіх мов"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Інші мови…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Видалити"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" АБВГҐДЕЄЖЗИІЇЙКЛМНОПРСТУФХЦЧШЩЬЮЯ"</string>
 </resources>
diff --git a/java/res/values/setup-dimens.xml b/java/res/values-v17/setup-styles.xml
similarity index 68%
rename from java/res/values/setup-dimens.xml
rename to java/res/values-v17/setup-styles.xml
index 007906d..8a9d664 100644
--- a/java/res/values/setup-dimens.xml
+++ b/java/res/values-v17/setup-styles.xml
@@ -15,6 +15,11 @@
 -->
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <dimen name="setup_title_text_size">46sp</dimen>
-    <dimen name="setup_horizontal_padding">16dp</dimen>
+    <style name="setupTitleStyle" parent="setupTitleStyleCommon">
+        <item name="android:layout_alignParentStart">true</item>
+    </style>
+    <style name="setupStepActionLabelStyle" parent="setupStepActionLabelStyleCommon">
+        <item name="android:paddingStart">12dp</item>
+        <item name="android:paddingEnd">24dp</item>
+    </style>
 </resources>
diff --git a/java/res/values-vi/strings-appname.xml b/java/res/values-vi/strings-appname.xml
new file mode 100644
index 0000000..e4b0f65
--- /dev/null
+++ b/java/res/values-vi/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Bàn phím Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Trình kiểm tra chính tả Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Cài đặt bàn phím Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Cài đặt trình kiểm tra chính tả Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index d062630..c4bfaa4 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Bàn phím Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Cài đặt bàn phím Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Trình kiểm tra chính tả Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Cài đặt trình kiểm tra chính tả Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Tùy chọn nhập"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Lệnh ghi nhật ký cho nghiên cứu"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Tra cứu tên liên hệ"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Nhấn đúp vào phím cách sẽ chèn thêm một dấu sau dấu cách"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Tự động viết hoa"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Viết hoa chữ đầu tiên của mỗi câu"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Từ điển cá nhân"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Thêm từ điển"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Từ điển chính"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Hiển thị gợi ý sửa"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Luôn hiển thị"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Hiển thị ở chế độ dọc"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Luôn ẩn"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Chặn các từ xúc phạm"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Không đề xuất các từ có thể gây xúc phạm"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Tự động sửa"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Phím cách và dấu câu tự động sửa từ nhập sai"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Tắt"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Thực sự cài đặt tệp này cho <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Đã xảy ra lỗi"</string>
     <string name="button_default" msgid="3988017840431881491">"Mặc định"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Ngôn ngữ và phương thức nhập"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Chọn phương thức nhập"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Chào mừng bạn đến với <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"với Nhập bằng cử chỉ"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Bắt đầu"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Bước tiếp theo"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Thiết lập <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Bật <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Vui lòng kiểm tra \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" trong cài đặt ngôn ngữ và phương thức nhập của bạn. Điều này sẽ ủy quyền cho ứng dụng chạy trên thiết bị của bạn."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> đã được bật trong cài đặt Ngôn ngữ và phương thức nhập, do đó bước này đã hoàn tất. Hãy chuyển sang bước tiếp theo!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Bật trong Cài đặt"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Chuyển sang <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Tiếp theo, chọn \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" làm phương thức nhập văn bản hoạt động của bạn."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Chuyển phương thức nhập"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Xin chúc mừng, bạn đã cài đặt xong!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Bây giờ bạn có thể nhập vào tất cả ứng dụng yêu thích của mình với <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Định cấu hình các ngôn ngữ khác"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Đã xong"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Hiển thị biểu tượng ứng dụng"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Hiển thị biểu tượng ứng dụng trong trình khởi chạy"</string>
     <string name="app_name" msgid="6320102637491234792">"Nhà cung cấp từ điển"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Nhà cung cấp từ điển"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Dịch vụ từ điển"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Có sẵn từ điển cho <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Nhấn để xem lại và tải xuống"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Tải xuống: đề xuất đối với <xliff:g id="LANGUAGE">%1$s</xliff:g> sẽ sớm sẵn sàng."</string>
+    <string name="version_text" msgid="2715354215568469385">"Phiên bản <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Thêm"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Thêm vào từ điển"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Cụm từ"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Thêm tùy chọn"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Bớt tùy chọn"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Từ:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Phím tắt:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Ngôn ngữ:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Nhập một từ"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Phím tắt tùy chọn"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Chỉnh sửa từ"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Chỉnh sửa"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Xóa"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Bạn không có bất kỳ từ nào trong từ điển người dùng. Bạn có thể thêm từ bằng cách chạm vào nút Thêm (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Cho tất cả ngôn ngữ"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Ngôn ngữ khác…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Xóa"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-zh-rCN/strings-appname.xml b/java/res/values-zh-rCN/strings-appname.xml
new file mode 100644
index 0000000..3f74ca4
--- /dev/null
+++ b/java/res/values-zh-rCN/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Android 键盘 (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android 拼写检查工具 (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Android 键盘设置 (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Android 拼写检查工具设置 (AOSP)"</string>
+</resources>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index 75b589d..4bb2ce4 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android 键盘 (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android 键盘设置 (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android 拼写检查工具 (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Android 拼写检查工具设置 (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"输入选项"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"研究记录命令"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"查找联系人姓名"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"双击空格键可插入句号并后跟空格"</string>
     <string name="auto_cap" msgid="1719746674854628252">"自动大写"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"句首字词大写"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"个人词典"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"附加词典"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"主词典"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"显示更正建议"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"始终显示"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"在纵向模式中显示"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"始终隐藏"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"屏蔽不文明的字词"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"屏蔽可能不文明的字词"</string>
     <string name="auto_correction" msgid="7630720885194996950">"自动更正"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"按空格键和标点可自动更正错别字"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"关闭"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"确定要为<xliff:g id="LOCALE_NAME">%s</xliff:g>安装此文件吗?"</string>
     <string name="error" msgid="8940763624668513648">"出现错误"</string>
     <string name="button_default" msgid="3988017840431881491">"默认"</string>
-    <string name="language_settings" msgid="1671153053201809031">"语言和输入法"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"选择输入法"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"欢迎使用 <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"体验顺畅的滑行输入体验"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"开始"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"下一步"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"设置 <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"启用 <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"请在“语言和输入法”设置中选中“<xliff:g id="APPLICATION_NAME">%s</xliff:g>”,授权这项应用在您的设备上运行。"</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"您已在“语言和输入法”设置中启用了 <xliff:g id="APPLICATION_NAME">%s</xliff:g>,因此这一步骤已完成。继续下一步吧!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"在设置中启用"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"切换到 <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"接下来,请选择“<xliff:g id="APPLICATION_NAME">%s</xliff:g>”作为您要使用的文字输入法。"</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"切换输入法"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"恭喜,您已完成了设置!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"现在,您可以在自己喜欢的所有应用中使用 <xliff:g id="APPLICATION_NAME">%s</xliff:g>来输入内容了。"</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"配置其他语言"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"完成"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"显示应用图标"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"在启动器中显示应用图标"</string>
     <string name="app_name" msgid="6320102637491234792">"词典提供程序"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"词典提供程序"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"词典服务"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"<xliff:g id="LANGUAGE">%1$s</xliff:g>词典可供下载"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"按此通知即可查看和下载"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"下载中:很快就能启用<xliff:g id="LANGUAGE">%1$s</xliff:g>的词典建议服务了!"</string>
+    <string name="version_text" msgid="2715354215568469385">"版本<xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"添加"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"添加到词典"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"词组"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"更多选项"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"隐藏部分选项"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"确定"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"字词:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"快捷键:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"语言:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"输入字词"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"快捷键(选填)"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"修改字词"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"修改"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"删除"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"用户词典中没有您定义的任何字词。您可以触摸“添加”(+) 按钮添加字词。"</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"所有语言"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"更多语言…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"删除"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-zh-rTW/strings-appname.xml b/java/res/values-zh-rTW/strings-appname.xml
new file mode 100644
index 0000000..8b2a417
--- /dev/null
+++ b/java/res/values-zh-rTW/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Android 鍵盤 (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android 拼字檢查 (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Android 鍵盤設定 (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Android 拼字檢查設定 (AOSP)"</string>
+</resources>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index 26a26c7..a0a017a 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android 鍵盤 (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android 鍵盤設定 (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android 拼字檢查 (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Android 拼字檢查設定 (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"輸入選項"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"研究紀錄指令"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"查詢聯絡人姓名"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"輕按兩下空格鍵可插入句號另加一個空格"</string>
     <string name="auto_cap" msgid="1719746674854628252">"自動大寫"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"句首字詞大寫"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"個人字典"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"外掛字典"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"主要字典"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"顯示修正建議"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"一律顯示"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"在垂直模式中顯示"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"永遠隱藏"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"封鎖令人反感的字詞"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"不建議可能令人反感的字詞"</string>
     <string name="auto_correction" msgid="7630720885194996950">"自動修正"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"按空白鍵或標點符號時,自動修正前面的錯字"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"關閉"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"準備為<xliff:g id="LOCALE_NAME">%s</xliff:g>版本安裝這個檔案嗎?"</string>
     <string name="error" msgid="8940763624668513648">"發生錯誤"</string>
     <string name="button_default" msgid="3988017840431881491">"預設"</string>
-    <string name="language_settings" msgid="1671153053201809031">"語言與輸入設定"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"選擇輸入法"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"歡迎使用「<xliff:g id="APPLICATION_NAME">%s</xliff:g>」"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"含手勢輸入功能"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"開始設定"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"下一步"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"正在設定「<xliff:g id="APPLICATION_NAME">%s</xliff:g>」"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"啟用「<xliff:g id="APPLICATION_NAME">%s</xliff:g>」"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"請在語言與輸入設定中勾選「<xliff:g id="APPLICATION_NAME">%s</xliff:g>」,授權這項應用程式在您的裝置上執行。"</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"您已在 [語言與輸入設定] 中啟用 <xliff:g id="APPLICATION_NAME">%s</xliff:g>,因此這個步驟已經完成。請進行下一個步驟!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"在設定中啟用"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"切換至「<xliff:g id="APPLICATION_NAME">%s</xliff:g>」"</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"接著,請選取「<xliff:g id="APPLICATION_NAME">%s</xliff:g>」做為目前使用的文字輸入法。"</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"切換輸入法"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"恭喜,您已完成設定!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"現在,您可以在自己喜愛的所有應用程式中使用「<xliff:g id="APPLICATION_NAME">%s</xliff:g>」輸入文字。"</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"設定其他語言"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"完成"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"顯示應用程式圖示"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"在啟動器中顯示應用程式圖示"</string>
     <string name="app_name" msgid="6320102637491234792">"字典提供者"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"字典提供者"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"字典服務"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"支援<xliff:g id="LANGUAGE">%1$s</xliff:g>字典"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"按下即可查看並下載"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"下載中:即將啟用<xliff:g id="LANGUAGE">%1$s</xliff:g>字詞建議服務。"</string>
+    <string name="version_text" msgid="2715354215568469385">"版本 <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"新增"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"加入字典"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"詞組"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"更多選項"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"較少選項"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"確定"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"字詞:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"快速鍵:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"語言:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"輸入字詞"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"可選用的快速鍵"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"編輯字詞"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"編輯"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"刪除"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"您的使用者字典中沒有任何字詞。如要新增字詞,請輕觸「新增」(+) 按鈕。"</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"所有語言"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"更多語言…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"刪除"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values-zu/strings-appname.xml b/java/res/values-zu/strings-appname.xml
new file mode 100644
index 0000000..1aa40f4
--- /dev/null
+++ b/java/res/values-zu/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="5940510615957428904">"Ikhibhodi ye-Android (AOSP)"</string>
+    <string name="spell_checker_service_name" msgid="1254221805440242662">"Isihloli sokupela se-Android (AOSP)"</string>
+    <string name="english_ime_settings" msgid="5760361067176802794">"Izilungiselelo zekhibhodi ye-Android (AOSP)"</string>
+    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Izilungiselelo zesihloli sokupela se-Android (AOSP)"</string>
+</resources>
diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml
index 146627f..0629c6c 100644
--- a/java/res/values-zu/strings.xml
+++ b/java/res/values-zu/strings.xml
@@ -20,10 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Ikhibhodi ye-Android (AOSP)"</string>
-    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Izilungiselelo zekhibhodi ye-Android (AOSP)"</string>
-    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Isihloli sokupela se-Android (AOSP)"</string>
-    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Izilungiselelo zesihloli sokupela se-Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Okukhethwa kukho kokungenayo"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Imiyalo yefayela lokungena lokucwaninga"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Bheka amagama woxhumana nabo"</string>
@@ -53,6 +49,7 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"Ukuthepha kabili kubha yesikhala kufaka isikhathi esilandelwa yisikhala"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Ukwenza ofeleba okuzenzakalelayo"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Yenza ufeleba wegama lokuqala lomusho ngamunye"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Isichazamazwi somuntu"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Faka izichazamazwi"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Isichazamazwi sakho ngqangi"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Bonisa ukusikesela kokulungisa"</string>
@@ -60,6 +57,8 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Bonisa njalo"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Bonisa ngomumo oqondile"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Fihla njalo"</string>
+    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Vimba amagama ahlaselayo"</string>
+    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Ungaphakamisi amagama angaba nokuhlaselayo"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Ukulungisa okuzenzakalelayo"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Ibha yesikhala nokubhala ngamagama amakhulu kulungisa amaphutha amagama athayiphwe kabi"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Valiwe"</string>
@@ -171,8 +170,24 @@
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Ufuna ukufakela i-<xliff:g id="LOCALE_NAME">%s</xliff:g> leli fayela ngokweqiniso?"</string>
     <string name="error" msgid="8940763624668513648">"Kube nephutha"</string>
     <string name="button_default" msgid="3988017840431881491">"Okuzenzakalelayo"</string>
-    <string name="language_settings" msgid="1671153053201809031">"Ulimi nokokufakwayo"</string>
-    <string name="select_input_method" msgid="4301602374609275003">"Khetha indlela yokufaka"</string>
+    <string name="setup_welcome_title" msgid="6112821709832031715">"Siyakwamukela ku-<xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"nokuthayipha ngokuthinta"</string>
+    <string name="setup_start_action" msgid="8936036460897347708">"Qalisa"</string>
+    <string name="setup_next_action" msgid="371821437915144603">"Isinyathelo esilandelayo"</string>
+    <string name="setup_steps_title" msgid="6400373034871816182">"Kusethwa i-<xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_title" msgid="3147967630253462315">"Nika amandla i-<xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"Sicela uhlole i-\"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" ngolimi lwakho nezilungiselelo zokokufaka. Lokhu kuzoyigunyaza ukuthi isebenze kudivayisi yakho."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"I-<xliff:g id="APPLICATION_NAME">%s</xliff:g> isivele inikwe amandla kulimi lwakho nakuzilungiselelo zokufaka, ngakho-ke lesi sinyathelo senziwe. Qhubekela kwesilandelayo!"</string>
+    <string name="setup_step1_action" msgid="4366513534999901728">"Nika amandla kuzilungiselelo"</string>
+    <string name="setup_step2_title" msgid="6860725447906690594">"Shintshela ku-<xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step2_instruction" msgid="9141481964870023336">"Okulandelayo, khetha i-\"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" njengendlela yakho yokufaka umbhalo osebenzayo."</string>
+    <string name="setup_step2_action" msgid="1660330307159824337">"Shintsha izindlela zokufaka"</string>
+    <string name="setup_step3_title" msgid="3154757183631490281">"Siyakuhalalisela, usumi ngomumo!"</string>
+    <string name="setup_step3_instruction" msgid="8025981829605426000">"Manje usungathayipha ngokufaka zonke izinhlelo zokusebenza eziyizintandokazi zakho nge-<xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
+    <string name="setup_step3_action" msgid="600879797256942259">"Lungiselela izilimi ezingeziwe"</string>
+    <string name="setup_finish_action" msgid="276559243409465389">"Iqedile"</string>
+    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Bonisa isithonjana sohlelo lokusebenza"</string>
+    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Bonisa isithonjana sohlelo lokusebenza kusiqalisi"</string>
     <string name="app_name" msgid="6320102637491234792">"Umhlinzeki wesichazamazwi"</string>
     <string name="dictionary_provider_name" msgid="3027315045397363079">"Umhlinzeki wesichazamazwi"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"Isevisi yesichazamazwi"</string>
@@ -203,4 +218,24 @@
     <string name="dict_available_notification_title" msgid="6514288591959117288">"Isichazamazwi se-<xliff:g id="LANGUAGE">%1$s</xliff:g> siyatholakala"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Cindezela ukuze ubuyekeze uphinde ulande"</string>
     <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Ukulanda: iziphakamiso ze-<xliff:g id="LANGUAGE">%1$s</xliff:g> zizolunga maduze."</string>
+    <string name="version_text" msgid="2715354215568469385">"Inguqulo engu-<xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
+    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Engeza"</string>
+    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Faka kusichazamazwi"</string>
+    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Umshwana"</string>
+    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Izinketho eziningi"</string>
+    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Izinketho ezincane"</string>
+    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"KULUNGILE"</string>
+    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Igama:"</string>
+    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Isinqamulelo:"</string>
+    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Ulimi:"</string>
+    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Thayipha igama"</string>
+    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Ukunqamulela okukhethekayo"</string>
+    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Hlela igama"</string>
+    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Hlela"</string>
+    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Susa"</string>
+    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Awunawo amagama kwisichazamazwi somsebenzisi. Ungafaka igama ngokuthinta inkinobho yokufaka (+)."</string>
+    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Okwazo zonke izilimi"</string>
+    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Izilimi eziningi…"</string>
+    <string name="user_dict_settings_delete" msgid="110413335187193859">"Susa"</string>
+    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
 </resources>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index a71e7cc..478a5c0 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -108,17 +108,25 @@
         <attr name="backgroundDimAlpha" format="integer" />
         <!-- More keys keyboard will shown at touched point. -->
         <attr name="showMoreKeysKeyboardAtTouchedPoint" format="boolean" />
+        <!-- Minimum distance between gesture trail sampling points. -->
+        <attr name="gestureTrailMinSamplingDistance" format="dimension" />
+        <!-- Maximum angular threshold between gesture trail interpolation segments in degree. -->
+        <attr name="gestureTrailMaxInterpolationAngularThreshold" format="integer" />
+        <!-- Maximum distance threshold between gesture trail interpolation segments. -->
+        <attr name="gestureTrailMaxInterpolationDistanceThreshold" format="dimension" />
+        <!-- Maximum number of gesture trail interpolation segments. -->
+        <attr name="gestureTrailMaxInterpolationSegments" format="integer" />
         <!-- Delay after gesture trail starts fading out in millisecond. -->
-        <attr name="gesturePreviewTrailFadeoutStartDelay" format="integer" />
-        <!-- Duration while gesture preview trail is fading out in millisecond. -->
-        <attr name="gesturePreviewTrailFadeoutDuration" format="integer" />
-        <!-- Interval of updating gesture preview trail in millisecond. -->
-        <attr name="gesturePreviewTrailUpdateInterval" format="integer" />
-        <attr name="gesturePreviewTrailColor" format="color" />
-        <attr name="gesturePreviewTrailStartWidth" format="dimension" />
-        <attr name="gesturePreviewTrailEndWidth" format="dimension" />
-        <attr name="gesturePreviewTrailBodyRatio" format="integer" />
-        <attr name="gesturePreviewTrailShadowRatio" format="integer" />
+        <attr name="gestureTrailFadeoutStartDelay" format="integer" />
+        <!-- Duration while gesture trail is fading out in millisecond. -->
+        <attr name="gestureTrailFadeoutDuration" format="integer" />
+        <!-- Interval of updating gesture trail in millisecond. -->
+        <attr name="gestureTrailUpdateInterval" format="integer" />
+        <attr name="gestureTrailColor" format="color" />
+        <attr name="gestureTrailStartWidth" format="dimension" />
+        <attr name="gestureTrailEndWidth" format="dimension" />
+        <attr name="gestureTrailBodyRatio" format="integer" />
+        <attr name="gestureTrailShadowRatio" format="integer" />
         <!-- Delay after gesture input and gesture floating preview text dismissing in millisecond -->
         <attr name="gestureFloatingPreviewTextLingerTimeout" format="integer" />
         <!-- Attributes for GestureFloatingPreviewText -->
diff --git a/java/res/values/colors.xml b/java/res/values/colors.xml
index 8a8049f..daa167c 100644
--- a/java/res/values/colors.xml
+++ b/java/res/values/colors.xml
@@ -58,4 +58,5 @@
     <color name="setup_text_dark">#FF707070</color>
     <color name="setup_text_action">@android:color/holo_blue_light</color>
     <color name="setup_step_background">@android:color/background_light</color>
+    <color name="setup_welcome_video_margin_color">#FFCCCCCC</color>
 </resources>
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index 4e2936c..d3a21f2 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -19,8 +19,6 @@
 -->
 
 <resources>
-    <!-- Device form factor. This value must be aligned with {@link KeyboardId.FORM_FACTOR_PHONE} -->
-    <integer name="config_device_form_factor">0</integer>
     <bool name="config_use_fullscreen_mode">false</bool>
     <bool name="config_enable_show_voice_key_option">true</bool>
     <bool name="config_enable_show_option_of_key_preview_popup">true</bool>
@@ -33,7 +31,7 @@
     <bool name="config_default_next_word_prediction">true</bool>
     <bool name="config_default_sound_enabled">false</bool>
     <bool name="config_default_vibration_enabled">true</bool>
-    <integer name="config_max_vibration_duration">250</integer> <!-- milliseconds -->
+    <integer name="config_max_vibration_duration">100</integer> <!-- milliseconds -->
     <integer name="config_delay_update_suggestions">100</integer>
     <integer name="config_delay_update_old_suggestions">300</integer>
     <integer name="config_delay_update_shift_state">100</integer>
@@ -75,10 +73,11 @@
     <!-- Showing more keys keyboard, just above the touched point if true, aligned to the key if
          false -->
     <bool name="config_show_more_keys_keyboard_at_touched_point">false</bool>
+    <bool name="config_block_potentially_offensive">true</bool>
     <integer name="config_gesture_floating_preview_text_linger_timeout">200</integer>
-    <integer name="config_gesture_preview_trail_fadeout_start_delay">100</integer>
-    <integer name="config_gesture_preview_trail_fadeout_duration">800</integer>
-    <integer name="config_gesture_preview_trail_update_interval">20</integer>
+    <integer name="config_gesture_trail_fadeout_start_delay">100</integer>
+    <integer name="config_gesture_trail_fadeout_duration">800</integer>
+    <integer name="config_gesture_trail_update_interval">20</integer>
     <!-- Static threshold for gesture after fast typing (msec) -->
     <integer name="config_gesture_static_time_threshold_after_fast_typing">500</integer>
     <!-- Static threshold for starting gesture detection (keyWidth%/sec) -->
@@ -111,15 +110,13 @@
         <!-- Aggressive -->
         <item>0.067</item>
         <!-- Very Aggressive : Suggestion whose normalized score is greater than this value
-             will be subject to auto-correction. -->
-        <item>0</item>
+             will be subject to auto-correction. "floatNegativeInfinity" is a special marker
+             string for Float.NEGATIVE_INFINITY -->
+        <item>floatNegativeInfinity</item>
     </string-array>
     <!-- Threshold of the normalized score of the best suggestion for the spell checker to declare
          a word to be "recommended" -->
     <string name="spellchecker_recommended_threshold_value" translatable="false">0.11</string>
-    <!-- Threshold of the normalized score of any dictionary lookup to be offered as a suggestion
-         by the spell checker -->
-    <string name="spellchecker_suggestion_threshold_value" translatable="false">0.03</string>
     <!--  Screen metrics for logging.
             0 = "mdpi phone screen"
             1 = "hdpi phone screen"
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index e9b34aa..98ae76c 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -100,14 +100,22 @@
     <integer name="suggestions_count_in_strip">3</integer>
     <fraction name="center_suggestion_percentile">36%</fraction>
 
-    <!-- Gesture preview trail parameters -->
-    <dimen name="gesture_preview_trail_start_width">10.0dp</dimen>
-    <dimen name="gesture_preview_trail_end_width">2.5dp</dimen>
+    <!-- Gesture trail parameters -->
+    <!-- Minimum distance between gesture trail sampling points. -->
+    <dimen name="gesture_trail_min_sampling_distance">9.6dp</dimen>
+    <!-- Maximum angular threshold between gesture trails interpolation segments in degree. -->
+    <integer name="gesture_trail_max_interpolation_angular_threshold">15</integer>
+    <!-- Maximum distance threshold between gesture trails interpolation segments. -->
+    <dimen name="gesture_trail_max_interpolation_distance_threshold">16.0dp</dimen>
+    <!-- Maximum number of gesture trail interpolation segments. -->
+    <integer name="gesture_trail_max_interpolation_segments">6</integer>
+    <dimen name="gesture_trail_start_width">10.0dp</dimen>
+    <dimen name="gesture_trail_end_width">2.5dp</dimen>
     <!-- Percentages of gesture preview taril body and shadow, in proportion to the trail width.
          A negative value of the shadow ratio disables drawing shadow. -->
     <!-- TODO: May use the shadow to alleviate rugged trail drawing. -->
-    <integer name="gesture_preview_trail_body_ratio">100</integer>
-    <integer name="gesture_preview_trail_shadow_ratio">-1</integer>
+    <integer name="gesture_trail_body_ratio">100</integer>
+    <integer name="gesture_trail_shadow_ratio">-1</integer>
     <!-- Gesture floating preview text parameters -->
     <dimen name="gesture_floating_preview_text_size">24dp</dimen>
     <dimen name="gesture_floating_preview_text_offset">73dp</dimen>
@@ -117,4 +125,6 @@
 
     <!-- Inset used in Accessibility mode to avoid accidental key presses when a finger slides off the screen. -->
     <dimen name="accessibility_edge_slop">8dp</dimen>
+
+    <integer name="user_dictionary_max_word_length" translatable="false">48</integer>
 </resources>
diff --git a/java/res/values/keyboard-heights.xml b/java/res/values/keyboard-heights.xml
index 418d3e5..c651a89 100644
--- a/java/res/values/keyboard-heights.xml
+++ b/java/res/values/keyboard-heights.xml
@@ -19,21 +19,21 @@
 -->
 
 <resources>
-    <!-- Build.HARDWARE,keyboard_height_in_dp -->
+    <!-- Build condition,keyboard_height_in_dp -->
     <string-array name="keyboard_heights" translatable="false">
     <!-- Preferable keyboard height in absolute scale: 1.285in -->
         <!-- Droid -->
-        <item>sholes,227.0167</item>
+        <item>HARDWARE=sholes,227.0167</item>
         <!-- Nexus One -->
-        <item>mahimahi,217.5932</item>
+        <item>HARDWARE=mahimahi,217.5932</item>
         <!-- Nexus S -->
-        <item>herring,200.8554</item>
+        <item>HARDWARE=herring,200.8554</item>
         <!-- Galaxy Nexus -->
-        <item>tuna,202.5869</item>
+        <item>HARDWARE=tuna,202.5869</item>
     <!-- Preferable keyboard height in absolute scale: 48.0mm -->
         <!-- Xoom -->
-        <item>stingray,283.1337</item>
+        <item>HARDWARE=stingray,283.1337</item>
     <!-- Default value for unknown device: empty string -->
-        <item>DEFAULT,</item>
+        <item>,</item>
     </string-array>
 </resources>
diff --git a/java/res/values/keypress-vibration-durations.xml b/java/res/values/keypress-vibration-durations.xml
index 10400be..9ce5051 100644
--- a/java/res/values/keypress-vibration-durations.xml
+++ b/java/res/values/keypress-vibration-durations.xml
@@ -18,17 +18,36 @@
 */
 -->
 <resources>
-    <!-- Build.HARDWARE,duration_in_milliseconds -->
+    <!-- Build condition,duration_in_milliseconds -->
     <string-array name="keypress_vibration_durations" translatable="false">
         <!-- Nexus S -->
-        <item>herring,5</item>
+        <item>MODEL=Nexus S:BRAND=google,5</item>
         <!-- Galaxy Nexus -->
-        <item>tuna,5</item>
+        <item>MODEL=Galaxy Nexus:BRAND=google,5</item>
         <!-- Nexus 4 -->
-        <item>mako,5</item>
+        <item>MODEL=Nexus 4:BRAND=google,8</item>
         <!-- Nexus 10 -->
-        <item>manta,16</item>
+        <item>MODEL=Nexus 10:BRAND=google,16</item>
+        <!-- Samsung Galaxy SII -->
+        <item>MODEL=GT-I(9100[GMPT]?|9108|9210T?):MANUFACTURER=samsung,8</item>
+        <item>MODEL=SGH-(I9[27]7R?|I927|T989D?):MANUFACTURER=samsung,8</item>
+        <item>MODEL=SHW-M250[KLS]?|SPH-D710|SCH-R760:MANUFACTURER=samsung,8</item>
+        <item>MODEL=ISW11SC|SC-02C:MANUFACTURER=samsung,8</item>
+        <!-- Samsung Galaxy SIII -->
+        <item>MODEL=(SAMSUNG-)?GT-I(930[05][NT]?|9308):MANUFACTURER=samsung,8</item>
+        <item>MODEL=(SAMSUNG-)?SGH-(T999[V]?|I747[M]?|N064|N035):MANUFACTURER=samsung,8</item>
+        <item>MODEL=(SAMSUNG-)?SCH-(J021|R530|I535|I939):MANUFACTURER=samsung,8</item>
+        <item>MODEL=(SAMSUNG-)?(SCL21|SC-06D|SC-03E]):MANUFACTURER=samsung,8</item>
+        <item>MODEL=(SAMSUNG-)?(SHV-210[KLS]?|SPH-L710):MANUFACTURER=samsung,8</item>
+        <!-- LG Optimus G -->
+        <item>MODEL=LG-E97[013]|LS970|L-01E:MANUFACTURER=LGE,15</item>
+        <!-- HTC One X -->
+        <item>MODEL=HTC One X:MANUFACTURER=HTC,20</item>
+        <!-- Motorola Razor M -->
+        <item>MODEL=XT907:MANUFACTURER=motorola,30</item>
+        <!-- Sony Xperia Z -->
+        <item>MODEL=C6603:MANUFACTURER=Sony,35</item>
         <!-- Default value for unknown device -->
-        <item>DEFAULT,20</item>
+        <item>,20</item>
     </string-array>
 </resources>
diff --git a/java/res/values/keypress-volumes.xml b/java/res/values/keypress-volumes.xml
index 047fe0c..a096c34 100644
--- a/java/res/values/keypress-volumes.xml
+++ b/java/res/values/keypress-volumes.xml
@@ -18,15 +18,15 @@
 */
 -->
 <resources>
+    <!-- Build condition,volume -->
     <string-array name="keypress_volumes" translatable="false">
-        <!-- Build.HARDWARE,volume -->
-        <item>herring,0.5f</item>
-        <item>tuna,0.5f</item>
-        <item>stingray,0.4f</item>
-        <item>grouper,0.3f</item>
-        <item>mako,0.3f</item>
-        <item>manta,0.2f</item>
+        <item>HARDWARE=herring,0.5f</item>
+        <item>HARDWARE=tuna,0.5f</item>
+        <item>HARDWARE=stingray,0.4f</item>
+        <item>HARDWARE=grouper,0.3f</item>
+        <item>HARDWARE=mako,0.3f</item>
+        <item>HARDWARE=manta,0.2f</item>
         <!-- Default value for unknown device -->
-        <item>DEFAULT,0.2f</item>
+        <item>,0.2f</item>
     </string-array>
 </resources>
diff --git a/java/res/values/phantom-sudden-move-event-device-list.xml b/java/res/values/phantom-sudden-move-event-device-list.xml
index 22f5102..53002b3 100644
--- a/java/res/values/phantom-sudden-move-event-device-list.xml
+++ b/java/res/values/phantom-sudden-move-event-device-list.xml
@@ -19,11 +19,11 @@
 -->
 <resources>
     <string-array name="phantom_sudden_move_event_device_list" translatable="false">
-        <!-- "Build.HARDWARE,true" that needs "phantom sudden move event" hack.
+        <!-- "Build condition,true" that needs "phantom sudden move event" hack.
              See {@link com.android.inputmethod.keyboard.PointerTracker}. -->
         <!-- Xoom -->
-        <item>stingray,true</item>
+        <item>HARDWARE=stingray,true</item>
         <!-- Default value for unknown device -->
-        <item>DEFAULT,false</item>
+        <item>,false</item>
     </string-array>
 </resources>
diff --git a/java/res/values/setup-styles-common.xml b/java/res/values/setup-styles-common.xml
new file mode 100644
index 0000000..686decf
--- /dev/null
+++ b/java/res/values/setup-styles-common.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <style name="setupTitleStyleCommon">
+        <item name="android:textColor">@color/setup_text_dark</item>
+        <item name="android:textSize">@dimen/setup_title_text_size</item>
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+    </style>
+    <style name="setupWelcomeDescritpionStyle" parent="setupTitleStyle">
+        <item name="android:textSize">@dimen/setup_welcome_description_text_size</item>
+    </style>
+    <style name="setupStepIndicatorStyle">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">@dimen/setup_step_triangle_indicator_height</item>
+    </style>
+    <style name="setupStepBulletStyle">
+        <item name="android:textColor">@color/setup_text_dark</item>
+        <item name="android:textSize">@dimen/setup_step_bullet_text_size</item>
+        <item name="android:layout_width">0dp</item>
+        <item name="android:layout_weight">1.0</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:gravity">center_horizontal</item>
+    </style>
+    <style name="setupStepBaseStyle">
+        <item name="android:textColor">@color/setup_text_dark</item>
+        <item name="android:background">@color/setup_step_background</item>
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:paddingLeft">@dimen/setup_step_horizontal_padding</item>
+        <item name="android:paddingRight">@dimen/setup_step_horizontal_padding</item>
+    </style>
+    <style name="setupStepTitleStyle" parent="setupStepBaseStyle">
+        <item name="android:textSize">@dimen/setup_step_title_text_size</item>
+    </style>
+    <style name="setupStepInstructionStyle" parent="setupStepBaseStyle">
+        <item name="android:textSize">@dimen/setup_step_instruction_text_size</item>
+    </style>
+    <style name="setupStepStartIndicatorStyle">
+        <!-- The triangle indicator here is placed sideways. -->
+        <item name="android:layout_width">@dimen/setup_step_triangle_indicator_height</item>
+        <item name="android:layout_height">@dimen/setup_step_action_height</item>
+    </style>
+    <style name="setupStepActionLabelStyleCommon" parent="setupStepBaseStyle">
+        <item name="android:textColor">@color/setup_step_action_color</item>
+        <item name="android:background">@drawable/setup_step_action_background</item>
+        <item name="android:layout_height">@dimen/setup_step_action_height</item>
+        <item name="android:paddingLeft">@dimen/setup_step_horizontal_padding_half</item>
+        <item name="android:textSize">@dimen/setup_step_action_text_size</item>
+        <item name="android:drawablePadding">@dimen/setup_step_horizontal_padding_half</item>
+        <item name="android:gravity">center_vertical</item>
+        <item name="android:clickable">true</item>
+        <item name="android:focusable">true</item>
+    </style>
+    <style name="setupStepStartActionLabelStyleCommon" parent="setupStepActionLabelStyleCommon">
+        <item name="android:paddingLeft">@dimen/setup_step_horizontal_padding</item>
+        <item name="android:paddingRight">@dimen/setup_step_horizontal_padding</item>
+    </style>
+</resources>
diff --git a/java/res/values/setup-styles.xml b/java/res/values/setup-styles.xml
index cfc689a..1ffe8ca 100644
--- a/java/res/values/setup-styles.xml
+++ b/java/res/values/setup-styles.xml
@@ -15,31 +15,6 @@
 -->
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <style name="setupTitleStyle">
-        <item name="android:textColor">@color/setup_text_dark</item>
-        <item name="android:textSize">@dimen/setup_title_text_size</item>
-    </style>
-    <style name="setupStepBulletStyle">
-        <item name="android:textColor">@color/setup_text_dark</item>
-        <item name="android:textSize">22sp</item>
-        <item name="android:layout_width">0dp</item>
-        <item name="android:layout_weight">1.0</item>
-        <item name="android:layout_height">wrap_content</item>
-        <item name="android:gravity">center_horizontal</item>
-    </style>
-    <style name="setupStepTitleStyle">
-        <item name="android:background">@color/setup_step_background</item>
-        <item name="android:textColor">@color/setup_text_dark</item>
-        <item name="android:textSize">22sp</item>
-    </style>
-    <style name="setupStepInstructionStyle">
-        <item name="android:background">@color/setup_step_background</item>
-        <item name="android:textColor">@color/setup_text_dark</item>
-        <item name="android:textSize">14sp</item>
-    </style>
-    <style name="setupStepActionLabelStyle">
-        <item name="android:background">@color/setup_step_background</item>
-        <item name="android:textColor">@color/setup_text_action</item>
-        <item name="android:textSize">18sp</item>
-    </style>
+    <style name="setupTitleStyle" parent="setupTitleStyleCommon" />
+    <style name="setupStepActionLabelStyle" parent="setupStepActionLabelStyleCommon" />
 </resources>
diff --git a/java/res/values/strings-appname.xml b/java/res/values/strings-appname.xml
new file mode 100644
index 0000000..46d8c44
--- /dev/null
+++ b/java/res/values/strings-appname.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources>
+    <!-- Application name for opensource Android keyboard. AOSP(Android Open Source Project) should not be translated.
+         This resource should be copied from msgid="8250992613616792321" -->
+    <string name="english_ime_name">Android Keyboard (AOSP)</string>
+
+    <!-- Name of Android spell checker service. AOSP(Android Open Source Project) should not be translated.
+         This resource should be copied from msgid="511950477199948048" -->
+    <string name="spell_checker_service_name">Android Spell Checker (AOSP)</string>
+
+    <!-- Title for Android Keyboard settings screen. AOSP(Android Open Source Project) should not be translated.
+         This resource should be copied from msgid="423615877174850267" -->
+    <string name="english_ime_settings">Android Keyboard Settings (AOSP)</string>
+
+    <!-- Title for the spell checking service settings screen. AOSP(Android Open Source Project) should not be translated.
+         This resource should be copied from msgid="2970535894327288421" -->
+    <string name="android_spell_checker_settings">Android Spell Checker Settings (AOSP)</string>
+</resources>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index f5e2441..85abb08 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -18,18 +18,6 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Application name for opensource Android keyboard. AOSP(Android Open Source Project) should not be translated. -->
-    <string name="aosp_android_keyboard_ime_name">Android Keyboard (AOSP)</string>
-
-    <!-- Title for Android Keyboard settings screen. AOSP(Android Open Source Project) should not be translated. -->
-    <string name="aosp_android_keyboard_ime_settings">Android Keyboard Settings (AOSP)</string>
-
-    <!-- Name of Android spell checker service. AOSP(Android Open Source Project) should not be translated. -->
-    <string name="aosp_spell_checker_service_name">Android Spell Checker (AOSP)</string>
-
-    <!-- Title for the spell checking service settings screen. AOSP(Android Open Source Project) should not be translated. -->
-    <string name="aosp_android_spell_checker_service_settings">Android Spell Checker Settings (AOSP)</string>
-
     <!-- Title for Latin keyboard input options dialog [CHAR LIMIT=25] -->
     <string name="english_ime_input_options">Input options</string>
 
@@ -107,6 +95,9 @@
     <!-- Description for option to enable auto capitalization of sentences -->
     <string name="auto_cap_summary">Capitalize the first word of each sentence</string>
 
+    <!-- Option to edit personal dictionary. [CHAR_LIMIT=30]-->
+    <string name="edit_personal_dictionary">Personal dictionary</string>
+
     <!-- Option to configure dictionaries -->
     <string name="configure_dictionaries_title">Add-on dictionaries</string>
     <!-- Name of the main dictionary, as opposed to auxiliary dictionaries (medical/entertainment/sports...) -->
@@ -120,6 +111,11 @@
     <string name="prefs_suggestion_visibility_show_only_portrait_name">Show in portrait mode</string>
     <string name="prefs_suggestion_visibility_hide_name">Always hide</string>
 
+    <!-- Option to block potentially offensive words to be shown [CHAR_LIMIT=30] -->
+    <string name="prefs_block_potentially_offensive_title">Block offensive words</string>
+    <!-- Summary for option to block potentially offensive words to be shown [CHAR_LIMIT=80 (two lines) or 40 (fits on one line, preferable)] -->
+    <string name="prefs_block_potentially_offensive_summary">Do not suggest potentially offensive words</string>
+
     <!-- Option to decide the auto correction threshold score -->
     <!-- Option to enable auto correction [CHAR LIMIT=20]-->
     <string name="auto_correction">Auto-correction</string>
@@ -442,33 +438,48 @@
     <!-- Title of the button to revert to the default value of the device in the settings dialog [CHAR LIMIT=15] -->
     <string name="button_default">Default</string>
 
-    <!-- TODO: Remove translatable="false" once wordings are finalized. -->
+    <!-- Title of the setup wizard welcome screen. [CHAR LIMT=40] -->
+    <string name="setup_welcome_title">"Welcome to <xliff:g id="application_name">%s</xliff:g>"</string>
+    <!-- Additional title of the setup wizard welcome screen, just below the setup_welcome_title. [CHAR_LIMIT=64] -->
+    <string name="setup_welcome_additional_description">with Gesture Typing</string>
+    <!-- The label of the button that starts the setup wizard. [CHAR_LIMIT=64] -->
+    <string name="setup_start_action">Get started</string>
+    <!-- The label of the button that navigates the user to the next step of the setup wizard. [CHAR_LIMIT=64] -->
+    <string name="setup_next_action">Next step</string>
     <!-- Title of the setup wizard. [CHAR LIMT=40] -->
-    <string name="setup_title" translatable="false">"Installing <xliff:g id="application_name">%s</xliff:g>"</string>
+    <string name="setup_steps_title">"Setting up <xliff:g id="application_name">%s</xliff:g>"</string>
     <!-- Ordinal number of the 1st step in the setup wizard. [CHAR LIMIT=5] -->
     <string name="setup_step1_bullet" translatable="false">1</string>
     <!-- Title of the 1st step in the setup wizard. [CHAR LIMIT=64] -->
-    <string name="setup_step1_title" translatable="false">"Enable <xliff:g id="application_name">%s</xliff:g> in settings."</string>
-    <!-- Detailed instruction of the 1st step in the setup wizard. [CHAR LIMIT=80] -->
-    <string name="setup_step1_instruction" translatable="false">"For security, please check \"<xliff:g id="application_name">%s</xliff:g>\""</string>
+    <string name="setup_step1_title">"Enable <xliff:g id="application_name">%s</xliff:g>"</string>
+    <!-- Detailed instruction of the 1st step in the setup wizard. [CHAR LIMIT=120] -->
+    <string name="setup_step1_instruction">"Please check \"<xliff:g id="application_name">%s</xliff:g>\" in your Language &amp; input settings. This will authorize it to run on your device."</string>
+    <!-- Detailed instruction of the already finished 1st step in the setup wizard. [CHAR LIMIT=120] -->
+    <string name="setup_step1_finished_instruction">"<xliff:g id="application_name">%s</xliff:g> is already enabled in your Language &amp; input settings, so this step is done. On to the next one!"</string>
+    <!-- The label of the button that triggers the Language & input settings in order to enable the keyboard. [CHAR_LIMIT=64] -->
+    <string name="setup_step1_action">Enable in Settings</string>
     <!-- Ordinal number of the 2nd step in the setup wizard. [CHAR LIMIT=5] -->
     <string name="setup_step2_bullet" translatable="false">2</string>
     <!-- Title of the 2nd step in the setup wizard. [CHAR LIMIT=64] -->
-    <string name="setup_step2_title" translatable="false">"Switch to <xliff:g id="application_name">%s</xliff:g>."</string>
-    <!-- Detailed instruction of the 2nd step in the setup wizard. [CHAR LIMIT=80] -->
-    <string name="setup_step2_instruction" translatable="false">"Now that you've enabled <xliff:g id="application_name">%s</xliff:g>, you can switch to it."</string>
+    <string name="setup_step2_title">"Switch to <xliff:g id="application_name">%s</xliff:g>"</string>
+    <!-- Detailed instruction of the 2nd step in the setup wizard. [CHAR LIMIT=120] -->
+    <string name="setup_step2_instruction">"Next, select \"<xliff:g id="application_name">%s</xliff:g>\" as your active text-input method."</string>
+    <!-- The label of the button that triggers the choose input method dialog in order to select the keyboard. [CHAR_LIMIT=64] -->
+    <string name="setup_step2_action">Switch input methods</string>
     <!-- Ordinal number of the 3rd step in the setup wizard. [CHAR LIMIT=5] -->
     <string name="setup_step3_bullet" translatable="false">3</string>
     <!-- Title of the 3rd step in the setup wizard. [CHAR LIMIT=64] -->
-    <string name="setup_step3_title" translatable="false">"Congratulations, you're all set!"</string>
-    <!-- Detailed instruction of the 3rd step in the setup wizard. [CHAR LIMIT=80] -->
-    <string name="setup_step3_instruction" translatable="false">Configure additional languages</string>
-    <!-- Title of the Language & input settings. This should be aligned with msgid="5292716747264442359" -->
-    <string name="language_settings">Language &amp; input</string>
-    <!-- Title of the Input method picker. This should be aligned with msgid="4653387336791222978" -->
-    <string name="select_input_method">Choose input method</string>
+    <string name="setup_step3_title">"Congratulations, you're all set!"</string>
+    <!-- Detailed instruction of the 3rd step in the setup wizard. [CHAR LIMIT=120] -->
+    <string name="setup_step3_instruction">Now you can type in all your favorite apps with <xliff:g id="application_name">%s</xliff:g>.</string>
+    <!-- The label of the button that triggers the screen for configuaring additional languages of the keyboard. [CHAR_LIMIT=64] -->
+    <string name="setup_step3_action">Configure additional languages</string>
+    <!-- The label of the button that finishes the setup wizard. [CHAR_LIMIT=64] -->
+    <string name="setup_finish_action">Finished</string>
     <!-- Option to show setup wizard icon. [CHAR LIMIT=30]-->
-    <string name="show_setup_wizard_icon" translatable="false">Show setup wizard icon</string>
+    <string name="show_setup_wizard_icon">Show app icon</string>
+    <!-- Description for the option to show setup wizard application icon of this IME in the laucher. [CHAR_LIMIT=65] -->
+    <string name="show_setup_wizard_icon_summary">Display application icon in the launcher</string>
 
     <!-- The dictionary provider application name. Visible in Settings/Applications/Manage applications. -->
     <string name="app_name">Dictionary Provider</string>
@@ -543,4 +554,70 @@
 
     <!-- The text of the toast warning a download is starting automatically to enable suggestions for the selected language [CHAR LIMIT=100] -->
     <string name="toast_downloading_suggestions">Downloading: suggestions for <xliff:g id="language" example="English">%1$s</xliff:g> will be ready soon.</string>
+
+    <!-- Version text [CHAR LIMIT=30]-->
+    <string name="version_text">Version <xliff:g id="version_number" example="1.0.1864.643521">%1$s</xliff:g></string>
+
+    <!-- User dictionary settings -->
+    <!-- User dictionary settings.  The summary of the listem item to go into the User dictionary settings screen. -->
+    <string name="user_dict_settings_summary" translatable="false">""</string>
+    <!-- User dictionary settings. The title of the menu item to add a new word to the user dictionary. -->
+    <!-- This resource is corresponding to  msgid="4056762757149923551" -->
+    <string name="user_dict_settings_add_menu_title">Add</string>
+    <!-- User dictionary settings. The title of the dialog to add a new word to the user dictionary. [CHAR LIMIT=25] -->
+    <!-- This resource is corresponding to msgid="4702613990174126482" -->
+    <string name="user_dict_settings_add_dialog_title">Add to dictionary</string>
+    <!-- User dictionary settings. The title of the screen to add/edit a new word to the user dictionary; it describes the phrase that will be added to the user dictionary. [CHAR LIMIT=25] -->
+    <!-- This resource is corresponding to msgid="742580720124344291" -->
+    <string name="user_dict_settings_add_screen_title">Phrase</string>
+    <!-- User dictionary settings. Text on the dialog button to pop more options for adding a word. [CHAR LIMIT=16] -->
+    <!-- This resource is corresponding to msgid="8848798370746019825" -->
+    <string name="user_dict_settings_add_dialog_more_options">More options</string>
+    <!-- User dictionary settings. Text on the dialog button mask advanced options. [CHAR LIMIT=15] -->
+    <!-- This resource is corresponding to msgid="2441785268726036101" -->
+    <string name="user_dict_settings_add_dialog_less_options">Less options</string>
+    <!-- User dictionary settings. Text on the dialog button to confirm adding a word. [CHAR LIMIT=15] -->
+    <!-- This resource is corresponding to msgid="6225823625332416144" -->
+    <string name="user_dict_settings_add_dialog_confirm">OK</string>
+    <!-- User dictionary settings. Label to put before the word field (that's the word that will actually be added to the user dictionary when OK is pressed). [CHAR LIMIT=20] -->
+    <!-- This resource is corresponding to msgid="7868879174905963135" -->
+    <string name="user_dict_settings_add_word_option_name">Word:</string>
+    <!-- User dictionary settings. Label to put before the shortcut field (once a shortcut is registered, the user can type the shortcut and get the word it points to in the suggestions). [CHAR LIMIT=20] -->
+    <!-- This resource is corresponding to msgid="660089258866063925" -->
+    <string name="user_dict_settings_add_shortcut_option_name">Shortcut:</string>
+    <!-- User dictionary settings. Label to put before the language field. [CHAR LIMIT=20] -->
+    <!-- This resource is corresponding to msgid="5696358317061318532" -->
+    <string name="user_dict_settings_add_locale_option_name">Language:</string>
+    <!-- User dictionary settings. Hint for the text field to type the word to add to the user dictionary. [CHAR LIMIT=35] -->
+    <!-- This resource is corresponding to msgid="5725254076556821247" -->
+    <string name="user_dict_settings_add_word_hint">Type a word</string>
+    <!-- User dictionary settings. Hint for the text field to type the optional shortcut to add to the user dictionary. [CHAR LIMIT=35] -->
+    <!-- This resource is corresponding to msgid="7333763456561873445" -->
+    <string name="user_dict_settings_add_shortcut_hint">Optional shortcut</string>
+    <!-- User dictionary settings. The title of the dialog to edit an existing word in the user dictionary. -->
+    <!-- This resource is corresponding to msgid="8967476444840548674" -->
+    <string name="user_dict_settings_edit_dialog_title">Edit word</string>
+    <!-- User dictionary settings. The title of the context menu item to edit the current word -->
+    <!-- This resource is corresponding to msgid="2210564879320004837" -->
+    <string name="user_dict_settings_context_menu_edit_title">Edit</string>
+    <!-- User dictionary settings. The title of the context menu item to delete the current word -->
+    <!-- This resource is corresponding to msgid="9140703913776549054" -->
+    <string name="user_dict_settings_context_menu_delete_title">Delete</string>
+    <!-- User dictionary settings. The text to show when there are no user-defined words in the dictionary  [CHAR LIMIT=200] -->
+    <!-- This resource is corresponding to msgid="8165273379942105271" -->
+    <string name="user_dict_settings_empty_text">You don\'t have any words in the user dictionary. Add a word by touching the Add (+) button.</string>
+    <!-- User dictionary settings. The list item to choose to insert a word into the user dictionary for all languages -->
+    <!-- This resource is corresponding to msgid="6742000040975959247" -->
+    <string name="user_dict_settings_all_languages">For all languages</string>
+    <!-- User dictionary settings. The text to show for the option that shows the entire list of supported locales to choose one [CHAR LIMIT=30] -->
+    <!-- This resource is corresponding to msgid="7316375944684977910" -->
+    <string name="user_dict_settings_more_languages">More languages…</string>
+    <!-- User dictionary settings. Label to delete an entry in the user dictionary [CHAR LIMIT=30]
+         This resource is copied from packages/apps/Settings/res/values/strings.xml -->
+    <!-- This resource is corresponding to msgid="4219243412325163003" -->
+    <string name="user_dict_settings_delete">Delete</string>
+    <!-- User dictionary settings. Index of the user dictionary [CHAR LIMIT=30]
+         This resource is copied from packages/apps/Settings/res/values/strings.xml -->
+    <!-- This resource is corresponding to msgid="5433275485499039199" -->
+    <string name="user_dict_fast_scroll_alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
 </resources>
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index 436e080..8b6c29e 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -64,14 +64,18 @@
         <item name="gestureFloatingPreviewHorizontalPadding">@dimen/gesture_floating_preview_horizontal_padding</item>
         <item name="gestureFloatingPreviewVerticalPadding">@dimen/gesture_floating_preview_vertical_padding</item>
         <item name="gestureFloatingPreviewRoundRadius">@dimen/gesture_floating_preview_round_radius</item>
-        <item name="gesturePreviewTrailFadeoutStartDelay">@integer/config_gesture_preview_trail_fadeout_start_delay</item>
-        <item name="gesturePreviewTrailFadeoutDuration">@integer/config_gesture_preview_trail_fadeout_duration</item>
-        <item name="gesturePreviewTrailUpdateInterval">@integer/config_gesture_preview_trail_update_interval</item>
-        <item name="gesturePreviewTrailColor">@color/highlight_color_default</item>
-        <item name="gesturePreviewTrailStartWidth">@dimen/gesture_preview_trail_start_width</item>
-        <item name="gesturePreviewTrailEndWidth">@dimen/gesture_preview_trail_end_width</item>
-        <item name="gesturePreviewTrailBodyRatio">@integer/gesture_preview_trail_body_ratio</item>
-        <item name="gesturePreviewTrailShadowRatio">@integer/gesture_preview_trail_shadow_ratio</item>
+        <item name="gestureTrailMinSamplingDistance">@dimen/gesture_trail_min_sampling_distance</item>
+        <item name="gestureTrailMaxInterpolationAngularThreshold">@integer/gesture_trail_max_interpolation_angular_threshold</item>
+        <item name="gestureTrailMaxInterpolationDistanceThreshold">@dimen/gesture_trail_max_interpolation_distance_threshold</item>
+        <item name="gestureTrailMaxInterpolationSegments">@integer/gesture_trail_max_interpolation_segments</item>
+        <item name="gestureTrailFadeoutStartDelay">@integer/config_gesture_trail_fadeout_start_delay</item>
+        <item name="gestureTrailFadeoutDuration">@integer/config_gesture_trail_fadeout_duration</item>
+        <item name="gestureTrailUpdateInterval">@integer/config_gesture_trail_update_interval</item>
+        <item name="gestureTrailColor">@color/highlight_color_default</item>
+        <item name="gestureTrailStartWidth">@dimen/gesture_trail_start_width</item>
+        <item name="gestureTrailEndWidth">@dimen/gesture_trail_end_width</item>
+        <item name="gestureTrailBodyRatio">@integer/gesture_trail_body_ratio</item>
+        <item name="gestureTrailShadowRatio">@integer/gesture_trail_shadow_ratio</item>
         <!-- Common attributes of MainKeyboardView -->
         <item name="keyHysteresisDistance">@dimen/config_key_hysteresis_distance</item>
         <item name="keyHysteresisDistanceForSlidingModifier">@dimen/config_key_hysteresis_distance_for_sliding_modifier</item>
@@ -94,8 +98,10 @@
         <item name="showMoreKeysKeyboardAtTouchedPoint">@bool/config_show_more_keys_keyboard_at_touched_point</item>
         <item name="languageOnSpacebarFinalAlpha">@integer/config_language_on_spacebar_final_alpha</item>
         <item name="languageOnSpacebarFadeoutAnimator">@anim/language_on_spacebar_fadeout</item>
+        <!-- Remove animations for now because it could drain a non-negligible amount of battery while typing.
         <item name="altCodeKeyWhileTypingFadeoutAnimator">@anim/alt_code_key_while_typing_fadeout</item>
         <item name="altCodeKeyWhileTypingFadeinAnimator">@anim/alt_code_key_while_typing_fadein</item>
+        -->
         <!-- Common attributes of MainKeyboardView for gesture typing detection and recognition -->
         <item name="gestureFloatingPreviewTextLingerTimeout">@integer/config_gesture_floating_preview_text_linger_timeout</item>
         <item name="gestureStaticTimeThresholdAfterFastTyping">@integer/config_gesture_static_time_threshold_after_fast_typing</item>
@@ -342,7 +348,7 @@
         <item name="keyTextShadowRadius">0.0</item>
         <item name="slidingKeyInputPreviewColor">@color/highlight_translucent_color_ics</item>
         <item name="gestureFloatingPreviewTextColor">@color/highlight_color_ics</item>
-        <item name="gesturePreviewTrailColor">@color/highlight_color_ics</item>
+        <item name="gestureTrailColor">@color/highlight_color_ics</item>
     </style>
     <style
         name="MainKeyboardView.IceCreamSandwich"
diff --git a/java/res/values/sudden-jumping-touch-event-device-list.xml b/java/res/values/sudden-jumping-touch-event-device-list.xml
index 3fdc0c7..3a9c379 100644
--- a/java/res/values/sudden-jumping-touch-event-device-list.xml
+++ b/java/res/values/sudden-jumping-touch-event-device-list.xml
@@ -19,13 +19,13 @@
 -->
 <resources>
     <string-array name="sudden_jumping_touch_event_device_list" translatable="false">
-        <!-- "Build.HARDWARE,true" that needs "sudden jump touch event" hack.
+        <!-- "Build condition,true" that needs "sudden jump touch event" hack.
              See {@link com.android.inputmethod.keyboard.SuddenJumpingTouchEventHandler}. -->
         <!-- Nexus One -->
-        <item>mahimahi,true</item>
+        <item>HARDWARE=mahimahi,true</item>
         <!-- Droid -->
-        <item>sholes,true</item>
+        <item>HARDWARE=sholes,true</item>
         <!-- Default value for unknown device -->
-        <item>DEFAULT,false</item>
+        <item>,false</item>
     </string-array>
 </resources>
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index 1581e5f..51e3420 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -54,6 +54,11 @@
         android:title="@string/correction_category"
         android:key="correction_settings">
         <PreferenceScreen
+            android:key="edit_personal_dictionary"
+            android:title="@string/edit_personal_dictionary">
+            <intent android:action="android.settings.USER_DICTIONARY_SETTINGS" />
+        </PreferenceScreen>
+        <PreferenceScreen
             android:key="configure_dictionaries_key"
             android:title="@string/configure_dictionaries_title">
            <intent
@@ -64,6 +69,12 @@
                  android:value="@string/dictionary_pack_client_id" />
            </intent>
         </PreferenceScreen>
+        <CheckBoxPreference
+            android:key="pref_key_block_potentially_offensive"
+            android:title="@string/prefs_block_potentially_offensive_title"
+            android:summary="@string/prefs_block_potentially_offensive_summary"
+            android:persistent="true"
+            android:defaultValue="@bool/config_block_potentially_offensive" />
         <ListPreference
             android:key="auto_correction_threshold"
             android:title="@string/auto_correction"
@@ -172,16 +183,19 @@
                 android:key="pref_keypress_sound_volume"
                 android:title="@string/prefs_keypress_sound_volume_settings"
                 latin:maxValue="100" /> <!-- percent -->
-            <!-- The show setup wizard icon settings shouldn't be persistent and the default value
-                 is added programmatically. -->
+            <!-- The settigs for showing setup wizard application icon shouldn't be persistent and
+                 the default value is added programmatically. -->
             <CheckBoxPreference
                 android:key="pref_show_setup_wizard_icon"
-                android:title="@string/show_setup_wizard_icon" />
+                android:title="@string/show_setup_wizard_icon"
+                android:summary="@string/show_setup_wizard_icon_summary" />
         </PreferenceScreen>
         <PreferenceScreen
             android:key="send_feedback"
             android:title="@string/send_feedback" />
         <PreferenceScreen
+            android:key="about_keyboard" />
+        <PreferenceScreen
             android:key="debug_settings"
             android:title="Debug settings"
             android:persistent="true"
diff --git a/java/res/xml/spellchecker.xml b/java/res/xml/spellchecker.xml
index 813319c..13e6132 100644
--- a/java/res/xml/spellchecker.xml
+++ b/java/res/xml/spellchecker.xml
@@ -21,7 +21,7 @@
      for the spell checker -->
 
 <spell-checker xmlns:android="http://schemas.android.com/apk/res/android"
-        android:label="@string/aosp_spell_checker_service_name"
+        android:label="@string/spell_checker_service_name"
         android:settingsActivity="com.android.inputmethod.latin.spellcheck.SpellCheckerSettingsActivity">
     <subtype
             android:label="@string/subtype_generic"
diff --git a/java/src/com/android/inputmethod/compat/TextViewCompatUtils.java b/java/src/com/android/inputmethod/compat/TextViewCompatUtils.java
index d4f1ea8..f8e1902 100644
--- a/java/src/com/android/inputmethod/compat/TextViewCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/TextViewCompatUtils.java
@@ -22,23 +22,23 @@
 import java.lang.reflect.Method;
 
 public final class TextViewCompatUtils {
-    // Note that TextView.setCompoundDrawablesRelative(Drawable,Drawable,Drawable,Drawable) has
-    // been introduced in API level 17 (Build.VERSION_CODE.JELLY_BEAN_MR1).
-    private static final Method METHOD_setCompoundDrawablesRelative = CompatUtils.getMethod(
-            TextView.class, "setCompoundDrawablesRelative",
+    // Note that TextView.setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable,Drawable,
+    // Drawable,Drawable) has been introduced in API level 17 (Build.VERSION_CODE.JELLY_BEAN_MR1).
+    private static final Method METHOD_setCompoundDrawablesRelativeWithIntrinsicBounds =
+            CompatUtils.getMethod(TextView.class, "setCompoundDrawablesRelativeWithIntrinsicBounds",
             Drawable.class, Drawable.class, Drawable.class, Drawable.class);
 
     private TextViewCompatUtils() {
         // This utility class is not publicly instantiable.
     }
 
-    public static void setCompoundDrawablesRelative(final TextView textView, final Drawable start,
-            final Drawable top, final Drawable end, final Drawable bottom) {
-        if (METHOD_setCompoundDrawablesRelative == null) {
-            textView.setCompoundDrawables(start, top, end, bottom);
+    public static void setCompoundDrawablesRelativeWithIntrinsicBounds(final TextView textView,
+            final Drawable start, final Drawable top, final Drawable end, final Drawable bottom) {
+        if (METHOD_setCompoundDrawablesRelativeWithIntrinsicBounds == null) {
+            textView.setCompoundDrawablesWithIntrinsicBounds(start, top, end, bottom);
             return;
         }
-        CompatUtils.invoke(textView, null, METHOD_setCompoundDrawablesRelative,
+        CompatUtils.invoke(textView, null, METHOD_setCompoundDrawablesRelativeWithIntrinsicBounds,
                 start, top, end, bottom);
     }
 }
diff --git a/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java b/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java
index ff6561c..a0d7641 100644
--- a/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java
@@ -28,6 +28,7 @@
     private static final Method METHOD_addWord = CompatUtils.getMethod(Words.class, "addWord",
             Context.class, String.class, Integer.TYPE, String.class, Locale.class);
 
+    @SuppressWarnings("deprecation")
     public static void addWord(final Context context, final String word, final int freq,
             final String shortcut, final Locale locale) {
         if (hasNewerAddWord()) {
@@ -39,13 +40,18 @@
             if (null == locale) {
                 localeType = Words.LOCALE_TYPE_ALL;
             } else {
-                localeType = Words.LOCALE_TYPE_CURRENT;
+                final Locale currentLocale = context.getResources().getConfiguration().locale;
+                if (locale.equals(currentLocale)) {
+                    localeType = Words.LOCALE_TYPE_CURRENT;
+                } else {
+                    localeType = Words.LOCALE_TYPE_ALL;
+                }
             }
             Words.addWord(context, word, freq, localeType);
         }
     }
 
-    private static final boolean hasNewerAddWord() {
+    public static final boolean hasNewerAddWord() {
         return null != METHOD_addWord;
     }
 }
diff --git a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java b/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java
index df4a52f..bf22305 100644
--- a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java
+++ b/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java
@@ -138,7 +138,12 @@
             if (null == manager) return;
 
             // This is an upgraded word list: we should download it.
-            final Uri uri = Uri.parse(mWordList.mRemoteFilename);
+            // Adding a disambiguator to circumvent a bug in older versions of DownloadManager.
+            // DownloadManager also stupidly cuts the extension to replace with its own that it
+            // gets from the content-type. We need to circumvent this.
+            final String disambiguator = "#" + System.currentTimeMillis()
+                    + com.android.inputmethod.latin.Utils.getVersionName(context) + ".dict";
+            final Uri uri = Uri.parse(mWordList.mRemoteFilename + disambiguator);
             final Request request = new Request(uri);
 
             final Resources res = context.getResources();
@@ -174,7 +179,7 @@
             final long downloadId = UpdateHandler.registerDownloadRequest(manager, request, db,
                     mWordList.mId, mWordList.mVersion);
             Utils.l("Starting download of", uri, "with id", downloadId);
-            PrivateLog.log("Starting download of " + uri + ", id : " + downloadId, context);
+            PrivateLog.log("Starting download of " + uri + ", id : " + downloadId);
         }
     }
 
@@ -333,7 +338,7 @@
                     mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mChecksum,
                     mWordList.mFileSize, mWordList.mVersion, mWordList.mFormatVersion);
             PrivateLog.log("Insert 'available' record for " + mWordList.mDescription
-                    + " and locale " + mWordList.mLocale, context);
+                    + " and locale " + mWordList.mLocale);
             db.insert(MetadataDbHelper.METADATA_TABLE_NAME, null, values);
         }
     }
@@ -383,7 +388,7 @@
                     mWordList.mChecksum, mWordList.mFileSize, mWordList.mVersion,
                     mWordList.mFormatVersion);
             PrivateLog.log("Insert 'preinstalled' record for " + mWordList.mDescription
-                    + " and locale " + mWordList.mLocale, context);
+                    + " and locale " + mWordList.mLocale);
             db.insert(MetadataDbHelper.METADATA_TABLE_NAME, null, values);
         }
     }
@@ -424,7 +429,7 @@
                     mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mChecksum,
                     mWordList.mFileSize, mWordList.mVersion, mWordList.mFormatVersion);
             PrivateLog.log("Updating record for " + mWordList.mDescription
-                    + " and locale " + mWordList.mLocale, context);
+                    + " and locale " + mWordList.mLocale);
             db.update(MetadataDbHelper.METADATA_TABLE_NAME, values,
                     MetadataDbHelper.WORDLISTID_COLUMN + " = ? AND "
                             + MetadataDbHelper.VERSION_COLUMN + " = ?",
@@ -478,13 +483,14 @@
             if (MetadataDbHelper.STATUS_INSTALLED == status
                     || MetadataDbHelper.STATUS_DISABLED == status
                     || MetadataDbHelper.STATUS_DELETING == status) {
-                // If it is installed or disabled, then we cannot remove the entry lest the user
-                // lose the ability to delete the file or otherwise administrate it. We will thus
-                // leave it as is, but remove the URI from the database since it is not supposed to
-                // be accessible any more.
+                // If it is installed or disabled, we need to mark it as deleted so that LatinIME
+                // will remove it next time it enquires for dictionaries.
                 // If it is deleting and we don't have a new version, then we have to wait until
-                // Android Keyboard actually has deleted it before we can remove its metadata.
+                // LatinIME actually has deleted it before we can remove its metadata.
+                // In both cases, remove the URI from the database since it is not supposed to
+                // be accessible any more.
                 values.put(MetadataDbHelper.REMOTE_FILENAME_COLUMN, "");
+                values.put(MetadataDbHelper.STATUS_COLUMN, MetadataDbHelper.STATUS_DELETING);
                 db.update(MetadataDbHelper.METADATA_TABLE_NAME, values,
                         MetadataDbHelper.WORDLISTID_COLUMN + " = ? AND "
                                 + MetadataDbHelper.VERSION_COLUMN + " = ?",
diff --git a/java/src/com/android/inputmethod/dictionarypack/ButtonSwitcher.java b/java/src/com/android/inputmethod/dictionarypack/ButtonSwitcher.java
new file mode 100644
index 0000000..5ab94a4
--- /dev/null
+++ b/java/src/com/android/inputmethod/dictionarypack/ButtonSwitcher.java
@@ -0,0 +1,155 @@
+/**
+ * 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.dictionarypack;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+import android.widget.Button;
+import android.widget.FrameLayout;
+
+import com.android.inputmethod.latin.R;
+
+/**
+ * A view that handles buttons inside it according to a status.
+ */
+public class ButtonSwitcher extends FrameLayout {
+    public static final int NOT_INITIALIZED = -1;
+    public static final int STATUS_NO_BUTTON = 0;
+    public static final int STATUS_INSTALL = 1;
+    public static final int STATUS_CANCEL = 2;
+    public static final int STATUS_DELETE = 3;
+    // One of the above
+    private int mStatus = NOT_INITIALIZED;
+    private int mAnimateToStatus = NOT_INITIALIZED;
+
+    // Animation directions
+    public static final int ANIMATION_IN = 1;
+    public static final int ANIMATION_OUT = 2;
+
+    private Button mInstallButton;
+    private Button mCancelButton;
+    private Button mDeleteButton;
+    private OnClickListener mOnClickListener;
+
+    public ButtonSwitcher(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public ButtonSwitcher(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void onLayout(final boolean changed, final int left, final int top, final int right,
+            final int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        mInstallButton = (Button)findViewById(R.id.dict_install_button);
+        mCancelButton = (Button)findViewById(R.id.dict_cancel_button);
+        mDeleteButton = (Button)findViewById(R.id.dict_delete_button);
+        mInstallButton.setOnClickListener(mOnClickListener);
+        mCancelButton.setOnClickListener(mOnClickListener);
+        mDeleteButton.setOnClickListener(mOnClickListener);
+        setButtonPositionWithoutAnimation(mStatus);
+        if (mAnimateToStatus != NOT_INITIALIZED) {
+            // We have been asked to animate before we were ready, so we took a note of it.
+            // We are now ready: launch the animation.
+            animateButtonPosition(mStatus, mAnimateToStatus);
+            mStatus = mAnimateToStatus;
+            mAnimateToStatus = NOT_INITIALIZED;
+        }
+    }
+
+    private Button getButton(final int status) {
+        switch(status) {
+        case STATUS_INSTALL:
+            return mInstallButton;
+        case STATUS_CANCEL:
+            return mCancelButton;
+        case STATUS_DELETE:
+            return mDeleteButton;
+        default:
+            return null;
+        }
+    }
+
+    public void setStatusAndUpdateVisuals(final int status) {
+        if (mStatus == NOT_INITIALIZED) {
+            setButtonPositionWithoutAnimation(status);
+            mStatus = status;
+        } else {
+            if (null == mInstallButton) {
+                // We may come here before we have been layout. In this case we don't know our
+                // size yet so we can't start animations so we need to remember what animation to
+                // start once layout has gone through.
+                mAnimateToStatus = status;
+            } else {
+                animateButtonPosition(mStatus, status);
+                mStatus = status;
+            }
+        }
+    }
+
+    private void setButtonPositionWithoutAnimation(final int status) {
+        // This may be called by setStatus() before the layout has come yet.
+        if (null == mInstallButton) return;
+        final int width = getWidth();
+        // Set to out of the screen if that's not the currently displayed status
+        mInstallButton.setTranslationX(STATUS_INSTALL == status ? 0 : width);
+        mCancelButton.setTranslationX(STATUS_CANCEL == status ? 0 : width);
+        mDeleteButton.setTranslationX(STATUS_DELETE == status ? 0 : width);
+    }
+
+    private void animateButtonPosition(final int oldStatus, final int newStatus) {
+        final View oldButton = getButton(oldStatus);
+        final View newButton = getButton(newStatus);
+        if (null != oldButton && null != newButton) {
+            // Transition between two buttons : animate out, then in
+            animateButton(oldButton, ANIMATION_OUT).setListener(
+                    new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(final Animator animation) {
+                            if (newStatus != mStatus) return;
+                            animateButton(newButton, ANIMATION_IN);
+                        }
+                    });
+        } else if (null != oldButton) {
+            animateButton(oldButton, ANIMATION_OUT);
+        } else if (null != newButton) {
+            animateButton(newButton, ANIMATION_IN);
+        }
+    }
+
+    public void setInternalOnClickListener(final OnClickListener listener) {
+        mOnClickListener = listener;
+    }
+
+    private ViewPropertyAnimator animateButton(final View button, final int direction) {
+        final float outerX = getWidth();
+        final float innerX = button.getX() - button.getTranslationX();
+        if (ANIMATION_IN == direction) {
+            button.setClickable(true);
+            return button.animate().translationX(0);
+        } else {
+            button.setClickable(false);
+            return button.animate().translationX(outerX - innerX);
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java
new file mode 100644
index 0000000..88b5032
--- /dev/null
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java
@@ -0,0 +1,178 @@
+/**
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.android.inputmethod.dictionarypack;
+
+import android.app.DownloadManager;
+import android.app.DownloadManager.Query;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.ProgressBar;
+
+public class DictionaryDownloadProgressBar extends ProgressBar {
+    @SuppressWarnings("unused")
+    private static final String TAG = DictionaryDownloadProgressBar.class.getSimpleName();
+    private static final int NOT_A_DOWNLOADMANAGER_PENDING_ID = 0;
+
+    private String mClientId;
+    private String mWordlistId;
+    private boolean mIsCurrentlyAttachedToWindow = false;
+    private Thread mReporterThread = null;
+
+    public DictionaryDownloadProgressBar(final Context context) {
+        super(context);
+    }
+
+    public DictionaryDownloadProgressBar(final Context context, final AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void setIds(final String clientId, final String wordlistId) {
+        mClientId = clientId;
+        mWordlistId = wordlistId;
+    }
+
+    static private int getDownloadManagerPendingIdFromWordlistId(final Context context,
+            final String clientId, final String wordlistId) {
+        final SQLiteDatabase db = MetadataDbHelper.getDb(context, clientId);
+        final ContentValues wordlistValues =
+                MetadataDbHelper.getContentValuesOfLatestAvailableWordlistById(db, wordlistId);
+        if (null == wordlistValues) {
+            // We don't know anything about a word list with this id. Bug? This should never
+            // happen, but still return to prevent a crash.
+            Log.e(TAG, "Unexpected word list ID: " + wordlistId);
+            return NOT_A_DOWNLOADMANAGER_PENDING_ID;
+        }
+        return wordlistValues.getAsInteger(MetadataDbHelper.PENDINGID_COLUMN);
+    }
+
+    /*
+     * This method will stop any running updater thread for this progress bar and create and run
+     * a new one only if the progress bar is visible.
+     * Hence, as a result of calling this method, the progress bar will have an updater thread
+     * running if and only if the progress bar is visible.
+     */
+    private void updateReporterThreadRunningStatusAccordingToVisibility() {
+        if (null != mReporterThread) mReporterThread.interrupt();
+        if (mIsCurrentlyAttachedToWindow && View.VISIBLE == getVisibility()) {
+            final int downloadManagerPendingId =
+                    getDownloadManagerPendingIdFromWordlistId(getContext(), mClientId, mWordlistId);
+            if (NOT_A_DOWNLOADMANAGER_PENDING_ID == downloadManagerPendingId) {
+                // Can't get the ID. This is never supposed to happen, but still clear the updater
+                // thread and return to avoid a crash.
+                mReporterThread = null;
+                return;
+            }
+            final UpdaterThread updaterThread =
+                    new UpdaterThread(getContext(), downloadManagerPendingId);
+            updaterThread.start();
+            mReporterThread = updaterThread;
+        } else {
+            // We're not going to restart the thread anyway, so we may as well garbage collect it.
+            mReporterThread = null;
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        mIsCurrentlyAttachedToWindow = true;
+        updateReporterThreadRunningStatusAccordingToVisibility();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        mIsCurrentlyAttachedToWindow = false;
+        updateReporterThreadRunningStatusAccordingToVisibility();
+    }
+
+    private class UpdaterThread extends Thread {
+        private final static int REPORT_PERIOD = 150; // how often to report progress, in ms
+        final DownloadManager mDownloadManager;
+        final int mId;
+        public UpdaterThread(final Context context, final int id) {
+            super();
+            mDownloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
+            mId = id;
+        }
+        @Override
+        public void run() {
+            try {
+                // It's almost impossible that mDownloadManager is null (it would mean it has been
+                // disabled between pressing the 'install' button and displaying the progress
+                // bar), but just in case.
+                if (null == mDownloadManager) return;
+                final UpdateHelper updateHelper = new UpdateHelper();
+                final Query query = new Query().setFilterById(mId);
+                int lastProgress = 0;
+                setIndeterminate(true);
+                while (!isInterrupted()) {
+                    final Cursor cursor = mDownloadManager.query(query);
+                    if (null == cursor) {
+                        // Can't contact DownloadManager: this should never happen.
+                        return;
+                    }
+                    try {
+                        if (cursor.moveToNext()) {
+                            final int columnBytesDownloadedSoFar = cursor.getColumnIndex(
+                                    DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);
+                            final int bytesDownloadedSoFar =
+                                    cursor.getInt(columnBytesDownloadedSoFar);
+                            updateHelper.setProgressFromAnotherThread(bytesDownloadedSoFar);
+                        } else {
+                            // Download has finished and DownloadManager has already been asked to
+                            // clean up the db entry.
+                            updateHelper.setProgressFromAnotherThread(getMax());
+                            return;
+                        }
+                    } finally {
+                        cursor.close();
+                    }
+                    Thread.sleep(REPORT_PERIOD);
+                }
+            } catch (InterruptedException e) {
+                // Do nothing and terminate normally.
+            }
+        }
+
+        private class UpdateHelper implements Runnable {
+            private int mProgress;
+            @Override
+            public void run() {
+                setIndeterminate(false);
+                setProgress(mProgress);
+            }
+            public void setProgressFromAnotherThread(final int progress) {
+                if (mProgress != progress) {
+                    mProgress = progress;
+                    // For some unknown reason, setProgress just does not work from a separate
+                    // thread, although the code in ProgressBar looks like it should. Thus, we
+                    // resort to a runnable posted to the handler of the view.
+                    final Handler handler = getHandler();
+                    // It's possible to come here before this view has been laid out. If so,
+                    // just ignore the call - it will be updated again later.
+                    if (null == handler) return;
+                    handler.post(this);
+                }
+            }
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java
new file mode 100644
index 0000000..de3711c
--- /dev/null
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.android.inputmethod.dictionarypack;
+
+import com.android.inputmethod.latin.CollectionUtils;
+
+import java.util.HashMap;
+
+/**
+ * Helper class to maintain the interface state of word list preferences.
+ *
+ * This is necessary because the views are created on-demand by calling code. There are many
+ * situations where views are renewed with little relation with user interaction. For example,
+ * when scrolling, the view is reused so it doesn't keep its state, which means we need to keep
+ * it separately. Also whenever the underlying dictionary list undergoes a change (for example,
+ * update the metadata, or finish downloading) the whole list has to be thrown out and recreated
+ * in case some dictionaries appeared, disappeared, changed states etc.
+ */
+public class DictionaryListInterfaceState {
+    private static class State {
+        public boolean mOpen = false;
+        public int mStatus = MetadataDbHelper.STATUS_UNKNOWN;
+    }
+
+    private HashMap<String, State> mWordlistToState = CollectionUtils.newHashMap();
+
+    public boolean isOpen(final String wordlistId) {
+        final State state = mWordlistToState.get(wordlistId);
+        if (null == state) return false;
+        return state.mOpen;
+    }
+
+    public int getStatus(final String wordlistId) {
+        final State state = mWordlistToState.get(wordlistId);
+        if (null == state) return MetadataDbHelper.STATUS_UNKNOWN;
+        return state.mStatus;
+    }
+
+    public void setOpen(final String wordlistId, final int status) {
+        final State newState;
+        final State state = mWordlistToState.get(wordlistId);
+        newState = null == state ? new State() : state;
+        newState.mOpen = true;
+        newState.mStatus = status;
+        mWordlistToState.put(wordlistId, newState);
+    }
+
+    public void closeAll() {
+        for (final State state : mWordlistToState.values()) {
+            state.mOpen = false;
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java
index f8d1c4f..4fbe162 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java
@@ -189,7 +189,7 @@
      */
     @Override
     public String getType(final Uri uri) {
-        PrivateLog.log("Asked for type of : " + uri, this);
+        PrivateLog.log("Asked for type of : " + uri);
         final int match = matchUri(uri);
         switch (match) {
             case NO_MATCH: return null;
@@ -220,7 +220,7 @@
     public Cursor query(final Uri uri, final String[] projection, final String selection,
             final String[] selectionArgs, final String sortOrder) {
         Utils.l("Uri =", uri);
-        PrivateLog.log("Query : " + uri, this);
+        PrivateLog.log("Query : " + uri);
         final String clientId = getClientId(uri);
         final int match = matchUri(uri);
         switch (match) {
@@ -228,7 +228,7 @@
             case DICTIONARY_V2_WHOLE_LIST:
                 final Cursor c = MetadataDbHelper.queryDictionaries(getContext(), clientId);
                 Utils.l("List of dictionaries with count", c.getCount());
-                PrivateLog.log("Returned a list of " + c.getCount() + " items", this);
+                PrivateLog.log("Returned a list of " + c.getCount() + " items");
                 return c;
             case DICTIONARY_V2_DICT_INFO:
                 // In protocol version 2, we return null if the client is unknown. Otherwise
@@ -248,10 +248,10 @@
                 // TODO: pass clientId to the following function
                 DictionaryService.updateNowIfNotUpdatedInAVeryLongTime(getContext());
                 if (null != dictFiles && dictFiles.size() > 0) {
-                    PrivateLog.log("Returned " + dictFiles.size() + " files", this);
+                    PrivateLog.log("Returned " + dictFiles.size() + " files");
                     return new ResourcePathCursor(dictFiles);
                 } else {
-                    PrivateLog.log("No dictionary files for this URL", this);
+                    PrivateLog.log("No dictionary files for this URL");
                     return new ResourcePathCursor(Collections.<WordListInfo>emptyList());
                 }
             // V2_METADATA and V2_DATAFILE are not supported for query()
@@ -488,7 +488,7 @@
     public Uri insert(final Uri uri, final ContentValues values)
             throws UnsupportedOperationException {
         if (null == uri || null == values) return null; // Should never happen but let's be safe
-        PrivateLog.log("Insert, uri = " + uri.toString(), this);
+        PrivateLog.log("Insert, uri = " + uri.toString());
         final String clientId = getClientId(uri);
         switch (matchUri(uri)) {
             case DICTIONARY_V2_METADATA:
@@ -517,7 +517,7 @@
                 break;
             case DICTIONARY_V1_WHOLE_LIST:
             case DICTIONARY_V1_DICT_INFO:
-                PrivateLog.log("Attempt to insert : " + uri, this);
+                PrivateLog.log("Attempt to insert : " + uri);
                 throw new UnsupportedOperationException(
                         "Insertion in the dictionary is not supported in this version");
         }
@@ -532,7 +532,7 @@
     @Override
     public int update(final Uri uri, final ContentValues values, final String selection,
             final String[] selectionArgs) throws UnsupportedOperationException {
-        PrivateLog.log("Attempt to update : " + uri, this);
+        PrivateLog.log("Attempt to update : " + uri);
         throw new UnsupportedOperationException("Updating dictionary words is not supported");
     }
 }
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
index 5817eb4..46bb554 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
@@ -21,7 +21,6 @@
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.os.IBinder;
 import android.text.format.DateUtils;
 import android.util.Log;
@@ -190,7 +189,7 @@
         // is still more recent than UPDATE_FREQUENCY, do nothing.
         if (!isLastUpdateAtLeastThisOld(context, UPDATE_FREQUENCY)) return;
 
-        PrivateLog.log("Date changed - registering alarm", context);
+        PrivateLog.log("Date changed - registering alarm");
         AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
 
         // Best effort to wake between midnight and MAX_ALARM_DELAY in the morning.
@@ -215,7 +214,7 @@
     private static boolean isLastUpdateAtLeastThisOld(final Context context, final long time) {
         final long now = System.currentTimeMillis();
         final long lastUpdate = MetadataDbHelper.getOldestUpdateTime(context);
-        PrivateLog.log("Last update was " + lastUpdate, context);
+        PrivateLog.log("Last update was " + lastUpdate);
         return lastUpdate + time < now;
     }
 
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java
index 9e27c1f..6183223 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java
@@ -64,6 +64,10 @@
     private ConnectivityManager mConnectivityManager;
     private MenuItem mUpdateNowMenu;
     private boolean mChangedSettings;
+    private DictionaryListInterfaceState mDictionaryListInterfaceState =
+            new DictionaryListInterfaceState();
+    private TreeMap<String, WordListPreference> mCurrentPreferenceMap =
+            new TreeMap<String, WordListPreference>(); // never null
 
     private final BroadcastReceiver mConnectivityChangedReceiver = new BroadcastReceiver() {
             @Override
@@ -276,13 +280,14 @@
             return result;
         } else {
             final String systemLocaleString = Locale.getDefault().toString();
-            final TreeMap<String, WordListPreference> prefList =
+            final TreeMap<String, WordListPreference> prefMap =
                     new TreeMap<String, WordListPreference>();
             final int idIndex = cursor.getColumnIndex(MetadataDbHelper.WORDLISTID_COLUMN);
             final int versionIndex = cursor.getColumnIndex(MetadataDbHelper.VERSION_COLUMN);
             final int localeIndex = cursor.getColumnIndex(MetadataDbHelper.LOCALE_COLUMN);
             final int descriptionIndex = cursor.getColumnIndex(MetadataDbHelper.DESCRIPTION_COLUMN);
             final int statusIndex = cursor.getColumnIndex(MetadataDbHelper.STATUS_COLUMN);
+            final int filesizeIndex = cursor.getColumnIndex(MetadataDbHelper.FILESIZE_COLUMN);
             do {
                 final String wordlistId = cursor.getString(idIndex);
                 final int version = cursor.getInt(versionIndex);
@@ -292,18 +297,35 @@
                 final int status = cursor.getInt(statusIndex);
                 final int matchLevel = LocaleUtils.getMatchLevel(systemLocaleString, localeString);
                 final String matchLevelString = LocaleUtils.getMatchLevelSortedString(matchLevel);
+                final int filesize = cursor.getInt(filesizeIndex);
                 // The key is sorted in lexicographic order, according to the match level, then
                 // the description.
                 final String key = matchLevelString + "." + description + "." + wordlistId;
-                final WordListPreference existingPref = prefList.get(key);
+                final WordListPreference existingPref = prefMap.get(key);
                 if (null == existingPref || hasPriority(status, existingPref.mStatus)) {
-                    final WordListPreference pref = new WordListPreference(activity, mClientId,
-                            wordlistId, version, locale, description, status);
-                    prefList.put(key, pref);
+                    final WordListPreference oldPreference = mCurrentPreferenceMap.get(key);
+                    final WordListPreference pref;
+                    if (null != oldPreference
+                            && oldPreference.mVersion == version
+                            && oldPreference.mLocale.equals(locale)) {
+                        // If the old preference has all the new attributes, reuse it. We test
+                        // for version and locale because although attributes other than status
+                        // need to be the same, others have been tested through the key of the
+                        // map. Also, status may differ so we don't want to use #equals() here.
+                        pref = oldPreference;
+                        pref.mStatus = status;
+                    } else {
+                        // Otherwise, discard it and create a new one instead.
+                        pref = new WordListPreference(activity, mDictionaryListInterfaceState,
+                                mClientId, wordlistId, version, locale, description, status,
+                                filesize);
+                    }
+                    prefMap.put(key, pref);
                 }
             } while (cursor.moveToNext());
             cursor.close();
-            return prefList.values();
+            mCurrentPreferenceMap = prefMap;
+            return prefMap.values();
         }
     }
 
diff --git a/java/src/com/android/inputmethod/dictionarypack/LogProblemReporter.java b/java/src/com/android/inputmethod/dictionarypack/LogProblemReporter.java
index c127ad5..c9e128d 100644
--- a/java/src/com/android/inputmethod/dictionarypack/LogProblemReporter.java
+++ b/java/src/com/android/inputmethod/dictionarypack/LogProblemReporter.java
@@ -28,7 +28,8 @@
         TAG = tag;
     }
 
+    @Override
     public void report(final Exception e) {
-        Log.e(TAG, "Reporting problem : " + e);
+        Log.e(TAG, "Reporting problem", e);
     }
 }
diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
index 55f545a..03ed267 100644
--- a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
+++ b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
@@ -16,13 +16,11 @@
 
 package com.android.inputmethod.dictionarypack;
 
-import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
-import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -47,16 +45,16 @@
     private static final int METADATA_DATABASE_INITIAL_VERSION = 3;
     // This is the first released version of the database that implements CLIENTID. It is
     // used to identify the versions for upgrades. This should never change going forward.
-    private static final int METADATA_DATABASE_VERSION_WITH_CLIENTID = 5;
+    private static final int METADATA_DATABASE_VERSION_WITH_CLIENTID = 6;
     // This is the current database version. It should be updated when the database schema
     // gets updated. It is passed to the framework constructor of SQLiteOpenHelper, so
     // that's what the framework uses to track our database version.
-    private static final int METADATA_DATABASE_VERSION = 5;
+    private static final int METADATA_DATABASE_VERSION = 6;
 
     private final static long NOT_A_DOWNLOAD_ID = -1;
 
     public static final String METADATA_TABLE_NAME = "pendingUpdates";
-    private static final String CLIENT_TABLE_NAME = "clients";
+    static final String CLIENT_TABLE_NAME = "clients";
     public static final String PENDINGID_COLUMN = "pendingid"; // Download Manager ID
     public static final String TYPE_COLUMN = "type";
     public static final String STATUS_COLUMN = "status";
@@ -75,6 +73,7 @@
 
     private static final String CLIENT_CLIENT_ID_COLUMN = "clientid";
     private static final String CLIENT_METADATA_URI_COLUMN = "uri";
+    private static final String CLIENT_METADATA_ADDITIONAL_ID_COLUMN = "additionalid";
     private static final String CLIENT_LAST_UPDATE_DATE_COLUMN = "lastupdate";
     private static final String CLIENT_PENDINGID_COLUMN = "pendingid"; // Download Manager ID
 
@@ -130,6 +129,7 @@
             "CREATE TABLE IF NOT EXISTS " + CLIENT_TABLE_NAME + " ("
             + CLIENT_CLIENT_ID_COLUMN + " TEXT, "
             + CLIENT_METADATA_URI_COLUMN + " TEXT, "
+            + CLIENT_METADATA_ADDITIONAL_ID_COLUMN + " TEXT, "
             + CLIENT_LAST_UPDATE_DATE_COLUMN + " INTEGER NOT NULL DEFAULT 0, "
             + CLIENT_PENDINGID_COLUMN + " INTEGER, "
             + FLAGS_COLUMN + " INTEGER, "
@@ -284,14 +284,15 @@
      * @return the string representation of the URI
      */
     public static String getMetadataUriAsString(final Context context, final String clientId) {
-        SQLiteDatabase defaultDb = getDb(context, null);
-        final Cursor cursor = defaultDb.query(CLIENT_TABLE_NAME,
-                new String[] { CLIENT_METADATA_URI_COLUMN },
-                CLIENT_CLIENT_ID_COLUMN + " = ?", new String[] { clientId },
+        SQLiteDatabase defaultDb = MetadataDbHelper.getDb(context, null);
+        final Cursor cursor = defaultDb.query(MetadataDbHelper.CLIENT_TABLE_NAME,
+                new String[] { MetadataDbHelper.CLIENT_METADATA_URI_COLUMN,
+                        MetadataDbHelper.CLIENT_METADATA_ADDITIONAL_ID_COLUMN },
+                MetadataDbHelper.CLIENT_CLIENT_ID_COLUMN + " = ?", new String[] { clientId },
                 null, null, null, null);
         try {
             if (!cursor.moveToFirst()) return null;
-            return cursor.getString(0); // Only one column, return it
+            return MetadataUriGetter.getUri(context, cursor.getString(0), cursor.getString(1));
         } finally {
             cursor.close();
         }
@@ -300,7 +301,8 @@
     /**
      * Update the last metadata update time for all clients using a particular URI.
      *
-     * All clients using this metadata URI will be indicated as having been updated now.
+     * This method searches for all clients using a particular URI and updates the last
+     * update time for this client.
      * The current time is used as the latest update time. This saved date will be what
      * is returned henceforth by {@link #getLastUpdateDateForClient(Context, String)},
      * until this method is called again.
@@ -309,13 +311,26 @@
      * @param uri the metadata URI we just downloaded
      */
     public static void saveLastUpdateTimeOfUri(final Context context, final String uri) {
-        PrivateLog.log("Save last update time of URI : " + uri + " " + System.currentTimeMillis(),
-                context);
+        PrivateLog.log("Save last update time of URI : " + uri + " " + System.currentTimeMillis());
         final ContentValues values = new ContentValues();
         values.put(CLIENT_LAST_UPDATE_DATE_COLUMN, System.currentTimeMillis());
         final SQLiteDatabase defaultDb = getDb(context, null);
-        defaultDb.update(CLIENT_TABLE_NAME, values,
-                CLIENT_METADATA_URI_COLUMN + " = ?", new String[] { uri });
+        final Cursor cursor = MetadataDbHelper.queryClientIds(context);
+        if (null == cursor) return;
+        try {
+            if (!cursor.moveToFirst()) return;
+            do {
+                final String clientId = cursor.getString(0);
+                final String metadataUri =
+                        MetadataDbHelper.getMetadataUriAsString(context, clientId);
+                if (metadataUri.equals(uri)) {
+                    defaultDb.update(CLIENT_TABLE_NAME, values,
+                            CLIENT_CLIENT_ID_COLUMN + " = ?", new String[] { clientId });
+                }
+            } while (cursor.moveToNext());
+        } finally {
+            cursor.close();
+        }
     }
 
     /**
@@ -730,11 +745,13 @@
     /**
      * Updates information relative to a specific client.
      *
-     * Updatable information includes only the metadata URI, but may be expanded in the future.
+     * Updatable information includes the metadata URI and the additional ID column. It may be
+     * expanded in the future.
      * The passed values must include a client ID in the key CLIENT_CLIENT_ID_COLUMN, and it must
-     * be equal to the string passed as an argument for clientId.
-     * The passed values must also include a non-empty metadata URI in the
-     * CLIENT_METADATA_URI_COLUMN column.
+     * be equal to the string passed as an argument for clientId. It may not be empty.
+     * The passed values must also include a non-null metadata URI in the
+     * CLIENT_METADATA_URI_COLUMN column, as well as a non-null additional ID in the
+     * CLIENT_METADATA_ADDITIONAL_ID_COLUMN. Both these strings may be empty.
      * If any of the above is not complied with, this function returns without updating data.
      *
      * @param context the context, to open the database
@@ -746,10 +763,16 @@
         // Sanity check the content values
         final String valuesClientId = values.getAsString(CLIENT_CLIENT_ID_COLUMN);
         final String valuesMetadataUri = values.getAsString(CLIENT_METADATA_URI_COLUMN);
-        // Empty string is a valid client ID, but external apps may not configure it.
-        // Empty string is a valid metadata URI if the client does not want updates.
-        if (TextUtils.isEmpty(valuesClientId) || null == valuesMetadataUri) {
-            // We need both these columns to be filled in
+        final String valuesMetadataAdditionalId =
+                values.getAsString(CLIENT_METADATA_ADDITIONAL_ID_COLUMN);
+        // Empty string is a valid client ID, but external apps may not configure it, so disallow
+        // both null and empty string.
+        // Empty string is a valid metadata URI if the client does not want updates, so allow
+        // empty string but disallow null.
+        // Empty string is a valid additional ID so allow empty string but disallow null.
+        if (TextUtils.isEmpty(valuesClientId) || null == valuesMetadataUri
+                || null == valuesMetadataAdditionalId) {
+            // We need all these columns to be filled in
             Utils.l("Missing parameter for updateClientInfo");
             return;
         }
@@ -780,8 +803,9 @@
      * Register a download ID for a specific metadata URI.
      *
      * This method should be called when a download for a metadata URI is starting. It will
-     * register the download ID for all clients using this metadata URI into the database
-     * for later retrieval by {@link #getDownloadRecordsForDownloadId(Context, long)}.
+     * search for all clients using this metadata URI and will register for each of them
+     * the download ID into the database for later retrieval by
+     * {@link #getDownloadRecordsForDownloadId(Context, long)}.
      *
      * @param context a context for opening databases
      * @param uri the metadata URI
@@ -792,8 +816,22 @@
         final ContentValues values = new ContentValues();
         values.put(CLIENT_PENDINGID_COLUMN, downloadId);
         final SQLiteDatabase defaultDb = getDb(context, "");
-        defaultDb.update(CLIENT_TABLE_NAME, values,
-                CLIENT_METADATA_URI_COLUMN + " = ?", new String[] { uri });
+        final Cursor cursor = MetadataDbHelper.queryClientIds(context);
+        if (null == cursor) return;
+        try {
+            if (!cursor.moveToFirst()) return;
+            do {
+                final String clientId = cursor.getString(0);
+                final String metadataUri =
+                        MetadataDbHelper.getMetadataUriAsString(context, clientId);
+                if (metadataUri.equals(uri)) {
+                    defaultDb.update(CLIENT_TABLE_NAME, values,
+                            CLIENT_CLIENT_ID_COLUMN + " = ?", new String[] { clientId });
+                }
+            } while (cursor.moveToNext());
+        } finally {
+            cursor.close();
+        }
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataUriGetter.java b/java/src/com/android/inputmethod/dictionarypack/MetadataUriGetter.java
new file mode 100644
index 0000000..ed81765
--- /dev/null
+++ b/java/src/com/android/inputmethod/dictionarypack/MetadataUriGetter.java
@@ -0,0 +1,33 @@
+/*
+ * 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.dictionarypack;
+
+import android.content.Context;
+
+/**
+ * Helper to get the metadata URI from its base URI and the additional ID, if any.
+ */
+public class MetadataUriGetter {
+    private MetadataUriGetter() {
+        // This helper class is not instantiable.
+    }
+
+    public static String getUri(final Context context, final String baseUri,
+            final String additionalId) {
+        return baseUri;
+    }
+}
diff --git a/java/src/com/android/inputmethod/dictionarypack/PrivateLog.java b/java/src/com/android/inputmethod/dictionarypack/PrivateLog.java
index 8593c1c..67dd7b9 100644
--- a/java/src/com/android/inputmethod/dictionarypack/PrivateLog.java
+++ b/java/src/com/android/inputmethod/dictionarypack/PrivateLog.java
@@ -16,7 +16,6 @@
 
 package com.android.inputmethod.dictionarypack;
 
-import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.sqlite.SQLiteDatabase;
@@ -24,6 +23,7 @@
 
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.Locale;
 
 /**
  * Class to keep long-term log. This is inactive in production, and is only for debug purposes.
@@ -44,10 +44,10 @@
             + COLUMN_EVENT + " TEXT);";
 
     private static final SimpleDateFormat sDateFormat =
-            new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+            new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.US);
 
     private static PrivateLog sInstance = new PrivateLog();
-    private static DebugHelper mDebugHelper = null;
+    private static DebugHelper sDebugHelper = null;
 
     private PrivateLog() {
     }
@@ -55,8 +55,8 @@
     public static synchronized PrivateLog getInstance(final Context context) {
         if (!DEBUG) return sInstance;
         synchronized(PrivateLog.class) {
-            if (sInstance.mDebugHelper == null) {
-                sInstance.mDebugHelper = new DebugHelper(context);
+            if (sDebugHelper == null) {
+                sDebugHelper = new DebugHelper(context);
             }
             return sInstance;
         }
@@ -94,16 +94,9 @@
 
     }
 
-    public static void log(String event, Context context) {
+    public static void log(String event) {
         if (!DEBUG) return;
-        final SQLiteDatabase l = getInstance(context).mDebugHelper.getWritableDatabase();
-        mDebugHelper.insert(l, event);
-    }
-
-    public static void log(String event, ContentProvider provider) {
-        if (!DEBUG) return;
-        final SQLiteDatabase l =
-                getInstance(provider.getContext()).mDebugHelper.getWritableDatabase();
-        mDebugHelper.insert(l, event);
+        final SQLiteDatabase l = sDebugHelper.getWritableDatabase();
+        DebugHelper.insert(l, event);
     }
 }
diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
index 3173e91..3f917f1 100644
--- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
+++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
@@ -183,7 +183,7 @@
                 final String clientId = cursor.getString(0);
                 final String metadataUri =
                         MetadataDbHelper.getMetadataUriAsString(context, clientId);
-                PrivateLog.log("Update for clientId " + Utils.s(clientId), context);
+                PrivateLog.log("Update for clientId " + Utils.s(clientId));
                 Utils.l("Update for clientId", clientId, " which uses URI ", metadataUri);
                 uris.add(metadataUri);
             } while (cursor.moveToNext());
@@ -211,8 +211,13 @@
      */
     private static void updateClientsWithMetadataUri(final Context context,
             final boolean updateNow, final String metadataUri) {
-        PrivateLog.log("Update for metadata URI " + Utils.s(metadataUri), context);
-        final Request metadataRequest = new Request(Uri.parse(metadataUri));
+        PrivateLog.log("Update for metadata URI " + Utils.s(metadataUri));
+        // Adding a disambiguator to circumvent a bug in older versions of DownloadManager.
+        // DownloadManager also stupidly cuts the extension to replace with its own that it
+        // gets from the content-type. We need to circumvent this.
+        final String disambiguator = "#" + System.currentTimeMillis()
+                + com.android.inputmethod.latin.Utils.getVersionName(context) + ".json";
+        final Request metadataRequest = new Request(Uri.parse(metadataUri + disambiguator));
         Utils.l("Request =", metadataRequest);
 
         final Resources res = context.getResources();
@@ -257,7 +262,7 @@
             // method will ignore it.
             writeMetadataDownloadId(context, metadataUri, downloadId);
         }
-        PrivateLog.log("Requested download with id " + downloadId, context);
+        PrivateLog.log("Requested download with id " + downloadId);
     }
 
     /**
@@ -351,7 +356,13 @@
                 final int columnUri = cursor.getColumnIndex(DownloadManager.COLUMN_URI);
                 final int error = cursor.getInt(columnError);
                 status = cursor.getInt(columnStatus);
-                uri = cursor.getString(columnUri);
+                final String uriWithAnchor = cursor.getString(columnUri);
+                int anchorIndex = uriWithAnchor.indexOf('#');
+                if (anchorIndex != -1) {
+                    uri = uriWithAnchor.substring(0, anchorIndex);
+                } else {
+                    uri = uriWithAnchor;
+                }
                 if (DownloadManager.STATUS_SUCCESSFUL != status) {
                     Log.e(TAG, "Permanent failure of download " + downloadId
                             + " with error code: " + error);
@@ -404,7 +415,7 @@
     /* package */ static void downloadFinished(final Context context, final Intent intent) {
         // Get and check the ID of the file that was downloaded
         final long fileId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, NOT_AN_ID);
-        PrivateLog.log("Download finished with id " + fileId, context);
+        PrivateLog.log("Download finished with id " + fileId);
         Utils.l("DownloadFinished with id", fileId);
         if (NOT_AN_ID == fileId) return; // Spurious wake-up: ignore
 
@@ -491,7 +502,7 @@
 
     private static void publishUpdateCycleCompletedEvent(final Context context) {
         // Even if this is not successful, we have to publish the new state.
-        PrivateLog.log("Publishing update cycle completed event", context);
+        PrivateLog.log("Publishing update cycle completed event");
         Utils.l("Publishing update cycle completed event");
         for (UpdateEventListener listener : linkedCopyOfList(sUpdateEventListeners)) {
             listener.updateCycleCompleted();
@@ -582,7 +593,7 @@
         }
 
         Utils.l("Downloaded metadata :", newMetadata);
-        PrivateLog.log("Downloaded metadata\n" + newMetadata, context);
+        PrivateLog.log("Downloaded metadata\n" + newMetadata);
 
         final ActionBatch actions = computeUpgradeTo(context, clientId, newMetadata);
         // TODO: Check with UX how we should report to the user
@@ -610,7 +621,7 @@
                 MetadataDbHelper.DESCRIPTION_COLUMN), "for", downloadRecord.mClientId);
         PrivateLog.log("Downloaded a new word list with description : "
                 + downloadRecord.mAttributes.getAsString(MetadataDbHelper.DESCRIPTION_COLUMN)
-                + " for " + downloadRecord.mClientId, context);
+                + " for " + downloadRecord.mClientId);
 
         final String locale =
                 downloadRecord.mAttributes.getAsString(MetadataDbHelper.LOCALE_COLUMN);
diff --git a/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java b/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java
index 0d923ae..451a0fb 100644
--- a/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java
+++ b/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java
@@ -16,14 +16,15 @@
 
 package com.android.inputmethod.dictionarypack;
 
-import android.app.Dialog;
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.preference.DialogPreference;
+import android.preference.Preference;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.Button;
+import android.view.ViewParent;
+import android.widget.ListView;
+import android.widget.TextView;
 
 import com.android.inputmethod.latin.R;
 
@@ -36,7 +37,7 @@
  * pack. Upon being pressed, it displays a menu to allow the user to install, disable,
  * enable or delete it as appropriate for the current state of the word list.
  */
-public final class WordListPreference extends DialogPreference {
+public final class WordListPreference extends Preference {
     static final private String TAG = WordListPreference.class.getSimpleName();
 
     // What to display in the "status" field when we receive unknown data as a status from
@@ -57,26 +58,32 @@
     // The metadata word list id and version of this word list.
     public final String mWordlistId;
     public final int mVersion;
+    public final Locale mLocale;
+    public final String mDescription;
     // The status
     public int mStatus;
+    // The size of the dictionary file
+    private final int mFilesize;
 
-    // Animation directions
-    static final private int ANIMATION_IN = 1;
-    static final private int ANIMATION_OUT = 2;
-
-    private static Button sLastClickedActionButton = null;
+    private final DictionaryListInterfaceState mInterfaceState;
     private final OnWordListPreferenceClick mPreferenceClickHandler =
             new OnWordListPreferenceClick();
     private final OnActionButtonClick mActionButtonClickHandler =
             new OnActionButtonClick();
 
-    public WordListPreference(final Context context, final String clientId, final String wordlistId,
-            final int version, final Locale locale, final String description, final int status) {
+    public WordListPreference(final Context context,
+            final DictionaryListInterfaceState dictionaryListInterfaceState, final String clientId,
+            final String wordlistId, final int version, final Locale locale,
+            final String description, final int status, final int filesize) {
         super(context, null);
         mContext = context;
+        mInterfaceState = dictionaryListInterfaceState;
         mClientId = clientId;
         mVersion = version;
         mWordlistId = wordlistId;
+        mFilesize = filesize;
+        mLocale = locale;
+        mDescription = description;
 
         setLayoutResource(R.layout.dictionary_line);
 
@@ -89,12 +96,6 @@
         if (status == mStatus) return;
         mStatus = status;
         setSummary(getSummary(status));
-        // If we are currently displaying the dialog, we should update it, or at least
-        // dismiss it.
-        final Dialog dialog = getDialog();
-        if (null != dialog) {
-            dialog.dismiss();
-        }
     }
 
     private String getSummary(final int status) {
@@ -117,29 +118,31 @@
         }
     }
 
+    // The table below needs to be kept in sync with MetadataDbHelper.STATUS_* since it uses
+    // the values as indices.
     private static final int sStatusActionList[][] = {
         // MetadataDbHelper.STATUS_UNKNOWN
         {},
         // MetadataDbHelper.STATUS_AVAILABLE
-        { R.string.install_dict, ACTION_ENABLE_DICT },
+        { ButtonSwitcher.STATUS_INSTALL, ACTION_ENABLE_DICT },
         // MetadataDbHelper.STATUS_DOWNLOADING
-        { R.string.cancel_download_dict, ACTION_DISABLE_DICT },
+        { ButtonSwitcher.STATUS_CANCEL, ACTION_DISABLE_DICT },
         // MetadataDbHelper.STATUS_INSTALLED
-        { R.string.delete_dict, ACTION_DELETE_DICT },
+        { ButtonSwitcher.STATUS_DELETE, ACTION_DELETE_DICT },
         // MetadataDbHelper.STATUS_DISABLED
-        { R.string.delete_dict, ACTION_DELETE_DICT },
+        { ButtonSwitcher.STATUS_DELETE, ACTION_DELETE_DICT },
         // MetadataDbHelper.STATUS_DELETING
         // We show 'install' because the file is supposed to be deleted.
         // The user may reinstall it.
-        { R.string.install_dict, ACTION_ENABLE_DICT }
+        { ButtonSwitcher.STATUS_INSTALL, ACTION_ENABLE_DICT }
     };
 
-    private CharSequence getButtonLabel(final int status) {
+    private int getButtonSwitcherStatus(final int status) {
         if (status >= sStatusActionList.length) {
             Log.e(TAG, "Unknown status " + status);
-            return "";
+            return ButtonSwitcher.STATUS_NO_BUTTON;
         }
-        return mContext.getString(sStatusActionList[status][0]);
+        return sStatusActionList[status][0];
     }
 
     private static int getActionIdFromStatusAndMenuEntry(final int status) {
@@ -194,36 +197,70 @@
     protected void onBindView(final View view) {
         super.onBindView(view);
         ((ViewGroup)view).setLayoutTransition(null);
-        final Button button = (Button)view.findViewById(R.id.wordlist_button);
-        button.setText(getButtonLabel(mStatus));
-        button.setVisibility(View.INVISIBLE);
-        button.setOnClickListener(mActionButtonClickHandler);
+
+        final DictionaryDownloadProgressBar progressBar =
+                (DictionaryDownloadProgressBar)view.findViewById(R.id.dictionary_line_progress_bar);
+        final TextView status = (TextView)view.findViewById(android.R.id.summary);
+        progressBar.setIds(mClientId, mWordlistId);
+        progressBar.setMax(mFilesize);
+        final boolean showProgressBar = (MetadataDbHelper.STATUS_DOWNLOADING == mStatus);
+        status.setVisibility(showProgressBar ? View.INVISIBLE : View.VISIBLE);
+        progressBar.setVisibility(showProgressBar ? View.VISIBLE : View.INVISIBLE);
+
+        final ButtonSwitcher buttonSwitcher =
+                (ButtonSwitcher)view.findViewById(R.id.wordlist_button_switcher);
+        if (mInterfaceState.isOpen(mWordlistId)) {
+            // The button is open.
+            final int previousStatus = mInterfaceState.getStatus(mWordlistId);
+            buttonSwitcher.setStatusAndUpdateVisuals(getButtonSwitcherStatus(previousStatus));
+            if (previousStatus != mStatus) {
+                // We come here if the status has changed since last time. We need to animate
+                // the transition.
+                buttonSwitcher.setStatusAndUpdateVisuals(getButtonSwitcherStatus(mStatus));
+                mInterfaceState.setOpen(mWordlistId, mStatus);
+            }
+        } else {
+            // The button is closed.
+            buttonSwitcher.setStatusAndUpdateVisuals(ButtonSwitcher.STATUS_NO_BUTTON);
+        }
+        buttonSwitcher.setInternalOnClickListener(mActionButtonClickHandler);
         view.setOnClickListener(mPreferenceClickHandler);
     }
 
     private class OnWordListPreferenceClick implements View.OnClickListener {
         @Override
         public void onClick(final View v) {
-            final Button button = (Button)v.findViewById(R.id.wordlist_button);
-            if (null != sLastClickedActionButton) {
-                animateButton(sLastClickedActionButton, ANIMATION_OUT);
+            // Note : v is the preference view
+            final ViewParent parent = v.getParent();
+            // Just in case something changed in the framework, test for the concrete class
+            if (!(parent instanceof ListView)) return;
+            final ListView listView = (ListView)parent;
+            final int indexToOpen;
+            // Close all first, we'll open back any item that needs to be open.
+            final boolean wasOpen = mInterfaceState.isOpen(mWordlistId);
+            mInterfaceState.closeAll();
+            if (wasOpen) {
+                // This button being shown. Take note that we don't want to open any button in the
+                // loop below.
+                indexToOpen = -1;
+            } else {
+                // This button was not being shown. Open it, and remember the index of this
+                // child as the one to open in the following loop.
+                mInterfaceState.setOpen(mWordlistId, mStatus);
+                indexToOpen = listView.indexOfChild(v);
             }
-            animateButton(button, ANIMATION_IN);
-            sLastClickedActionButton = button;
-        }
-    }
-
-    private void animateButton(final Button button, final int direction) {
-        final float outerX = ((View)button.getParent()).getWidth();
-        final float innerX = button.getX() - button.getTranslationX();
-        if (View.INVISIBLE == button.getVisibility()) {
-            button.setTranslationX(outerX - innerX);
-            button.setVisibility(View.VISIBLE);
-        }
-        if (ANIMATION_IN == direction) {
-            button.animate().translationX(0);
-        } else {
-            button.animate().translationX(outerX - innerX);
+            final int lastDisplayedIndex =
+                    listView.getLastVisiblePosition() - listView.getFirstVisiblePosition();
+            // The "lastDisplayedIndex" is actually displayed, hence the <=
+            for (int i = 0; i <= lastDisplayedIndex; ++i) {
+                final ButtonSwitcher buttonSwitcher = (ButtonSwitcher)listView.getChildAt(i)
+                        .findViewById(R.id.wordlist_button_switcher);
+                if (i == indexToOpen) {
+                    buttonSwitcher.setStatusAndUpdateVisuals(getButtonSwitcherStatus(mStatus));
+                } else {
+                    buttonSwitcher.setStatusAndUpdateVisuals(ButtonSwitcher.STATUS_NO_BUTTON);
+                }
+            }
         }
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index d160038..1550e77 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -105,7 +105,7 @@
     /** Hit bounding box of the key */
     public final Rect mHitBox = new Rect();
 
-    /** More keys */
+    /** More keys. It is guaranteed that this is null or an array of one or more elements */
     public final MoreKeySpec[] mMoreKeys;
     /** More keys column number and flags */
     private final int mMoreKeysColumnAndFlags;
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
index c76acd1..9eeee5b 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
@@ -27,8 +27,9 @@
      *
      * @param primaryCode the unicode of the key being pressed. If the touch is not on a valid key,
      *            the value will be zero.
+     * @param isSinglePointer true if pressing has occurred while no other key is being pressed.
      */
-    public void onPressKey(int primaryCode);
+    public void onPressKey(int primaryCode, boolean isSinglePointer);
 
     /**
      * Called when the user releases a key. This is sent after the {@link #onCodeInput} is called.
@@ -88,14 +89,21 @@
     public void onCancelInput();
 
     /**
+     * Called when user finished sliding key input.
+     */
+    public void onFinishSlidingInput();
+
+    /**
      * Send a non-"code input" custom request to the listener.
      * @return true if the request has been consumed, false otherwise.
      */
     public boolean onCustomRequest(int requestCode);
 
     public static class Adapter implements KeyboardActionListener {
+        public static final Adapter EMPTY_LISTENER = new Adapter();
+
         @Override
-        public void onPressKey(int primaryCode) {}
+        public void onPressKey(int primaryCode, boolean isSinglePointer) {}
         @Override
         public void onReleaseKey(int primaryCode, boolean withSliding) {}
         @Override
@@ -113,6 +121,8 @@
         @Override
         public void onCancelInput() {}
         @Override
+        public void onFinishSlidingInput() {}
+        @Override
         public boolean onCustomRequest(int requestCode) {
             return false;
         }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index ee8ee9a..aa27067 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -56,13 +56,8 @@
     public static final int ELEMENT_PHONE_SYMBOLS = 8;
     public static final int ELEMENT_NUMBER = 9;
 
-    public static final int FORM_FACTOR_PHONE = 0;
-    public static final int FORM_FACTOR_TABLET7 = 1;
-    public static final int FORM_FACTOR_TABLET10 = 2;
-
     public final InputMethodSubtype mSubtype;
     public final Locale mLocale;
-    public final int mDeviceFormFactor;
     // TODO: Remove this member. It is used only for logging purpose.
     public final int mOrientation;
     public final int mWidth;
@@ -82,7 +77,6 @@
     public KeyboardId(final int elementId, final KeyboardLayoutSet.Params params) {
         mSubtype = params.mSubtype;
         mLocale = SubtypeLocale.getSubtypeLocale(mSubtype);
-        mDeviceFormFactor = params.mDeviceFormFactor;
         mOrientation = params.mOrientation;
         mWidth = params.mKeyboardWidth;
         mHeight = params.mKeyboardHeight;
@@ -107,7 +101,6 @@
 
     private static int computeHashCode(final KeyboardId id) {
         return Arrays.hashCode(new Object[] {
-                id.mDeviceFormFactor,
                 id.mOrientation,
                 id.mElementId,
                 id.mMode,
@@ -130,8 +123,7 @@
     private boolean equals(final KeyboardId other) {
         if (other == this)
             return true;
-        return other.mDeviceFormFactor == mDeviceFormFactor
-                && other.mOrientation == mOrientation
+        return other.mOrientation == mOrientation
                 && other.mElementId == mElementId
                 && other.mMode == mMode
                 && other.mWidth == mWidth
@@ -195,11 +187,11 @@
     public String toString() {
         final String orientation = (mOrientation == Configuration.ORIENTATION_PORTRAIT)
                 ? "port" : "land";
-        return String.format("[%s %s:%s %s-%s:%dx%d %s %s %s%s%s%s%s%s%s%s%s]",
+        return String.format("[%s %s:%s %s:%dx%d %s %s %s%s%s%s%s%s%s%s%s]",
                 elementIdToName(mElementId),
                 mLocale,
                 mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET),
-                deviceFormFactor(mDeviceFormFactor), orientation, mWidth, mHeight,
+                orientation, mWidth, mHeight,
                 modeName(mMode),
                 imeAction(),
                 (navigateNext() ? "navigateNext" : ""),
@@ -238,15 +230,6 @@
         }
     }
 
-    public static String deviceFormFactor(final int deviceFormFactor) {
-        switch (deviceFormFactor) {
-        case FORM_FACTOR_PHONE: return "phone";
-        case FORM_FACTOR_TABLET7: return "tablet7";
-        case FORM_FACTOR_TABLET10: return "tablet10";
-        default: return null;
-        }
-    }
-
     public static String modeName(final int mode) {
         switch (mode) {
         case MODE_TEXT: return "text";
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index 5e68c70..1fe23a3 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -29,17 +29,18 @@
 import android.content.res.XmlResourceParser;
 import android.text.InputType;
 import android.text.TextUtils;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.Xml;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethodSubtype;
 
-import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.compat.EditorInfoCompatUtils;
 import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
 import com.android.inputmethod.keyboard.internal.KeyboardParams;
 import com.android.inputmethod.keyboard.internal.KeysCache;
+import com.android.inputmethod.latin.AdditionalSubtype;
 import com.android.inputmethod.latin.CollectionUtils;
 import com.android.inputmethod.latin.InputAttributes;
 import com.android.inputmethod.latin.InputTypeUtils;
@@ -72,6 +73,8 @@
     private static final String TAG_ELEMENT = "Element";
 
     private static final String KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX = "keyboard_layout_set_";
+    private static final int SPELLCHECKER_DUMMY_KEYBOARD_WIDTH = 480;
+    private static final int SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT = 800;
 
     private final Context mContext;
     private final Params mParams;
@@ -106,7 +109,6 @@
         boolean mNoSettingsKey;
         boolean mLanguageSwitchKeyEnabled;
         InputMethodSubtype mSubtype;
-        int mDeviceFormFactor;
         int mOrientation;
         int mKeyboardWidth;
         int mKeyboardHeight;
@@ -217,10 +219,8 @@
                     mPackageName, NO_SETTINGS_KEY, mEditorInfo);
         }
 
-        public Builder setScreenGeometry(final int deviceFormFactor, final int widthPixels,
-                final int heightPixels) {
+        public Builder setScreenGeometry(final int widthPixels, final int heightPixels) {
             final Params params = mParams;
-            params.mDeviceFormFactor = deviceFormFactor;
             params.mOrientation = (heightPixels > widthPixels)
                     ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
             setDefaultKeyboardSize(widthPixels, heightPixels);
@@ -285,8 +285,7 @@
             return this;
         }
 
-        @UsedForTesting
-        public void disableTouchPositionCorrectionDataForTest() {
+        public void disableTouchPositionCorrectionData() {
             mParams.mDisableTouchPositionCorrectionDataForTest = true;
         }
 
@@ -416,4 +415,47 @@
             }
         }
     }
+
+    public static KeyboardLayoutSet createKeyboardSetForSpellChecker(final Context context,
+            final String locale, final String layout) {
+        final InputMethodSubtype subtype =
+                AdditionalSubtype.createAdditionalSubtype(locale, layout, null);
+        return createKeyboardSet(context, subtype, SPELLCHECKER_DUMMY_KEYBOARD_WIDTH,
+                SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT, false);
+    }
+
+    public static KeyboardLayoutSet createKeyboardSetForTest(final Context context,
+            final InputMethodSubtype subtype, final int orientation,
+            final boolean testCasesHaveTouchCoordinates) {
+        final DisplayMetrics dm = context.getResources().getDisplayMetrics();
+        final int width;
+        final int height;
+        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+            width = Math.max(dm.widthPixels, dm.heightPixels);
+            height = Math.min(dm.widthPixels, dm.heightPixels);
+        } else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+            width = Math.min(dm.widthPixels, dm.heightPixels);
+            height = Math.max(dm.widthPixels, dm.heightPixels);
+        } else {
+            throw new RuntimeException("Orientation should be ORIENTATION_LANDSCAPE or "
+                    + "ORIENTATION_PORTRAIT: orientation=" + orientation);
+        }
+        return createKeyboardSet(context, subtype, width, height, testCasesHaveTouchCoordinates);
+    }
+
+    private static KeyboardLayoutSet createKeyboardSet(final Context context,
+            final InputMethodSubtype subtype, final int width, final int height,
+            final boolean testCasesHaveTouchCoordinates) {
+        final EditorInfo editorInfo = new EditorInfo();
+        editorInfo.inputType = InputType.TYPE_CLASS_TEXT;
+        final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
+                context, editorInfo);
+        builder.setScreenGeometry(width, height);
+        builder.setSubtype(subtype);
+        if (!testCasesHaveTouchCoordinates) {
+            // For spell checker and tests
+            builder.disableTouchPositionCorrectionData();
+        }
+        return builder.build();
+    }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 4e41b77..ad08d64 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -142,8 +142,7 @@
                 mThemeContext, editorInfo);
         final Resources res = mThemeContext.getResources();
         final DisplayMetrics dm = res.getDisplayMetrics();
-        builder.setScreenGeometry(res.getInteger(R.integer.config_device_form_factor),
-                dm.widthPixels, dm.heightPixels);
+        builder.setScreenGeometry(dm.widthPixels, dm.heightPixels);
         builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype());
         builder.setOptions(
                 settingsValues.isVoiceKeyEnabled(editorInfo),
@@ -217,19 +216,19 @@
         mState.onResetKeyboardStateToAlphabet();
     }
 
-    public void onPressKey(final int code) {
+    public void onPressKey(final int code, final boolean isSinglePointer) {
         if (isVibrateAndSoundFeedbackRequired()) {
             mFeedbackManager.hapticAndAudioFeedback(code, mKeyboardView);
         }
-        mState.onPressKey(code, isSinglePointer(), mLatinIME.getCurrentAutoCapsState());
+        mState.onPressKey(code, isSinglePointer, mLatinIME.getCurrentAutoCapsState());
     }
 
     public void onReleaseKey(final int code, final boolean withSliding) {
         mState.onReleaseKey(code, withSliding);
     }
 
-    public void onCancelInput() {
-        mState.onCancelInput(isSinglePointer());
+    public void onFinishSlidingInput() {
+        mState.onFinishSlidingInput();
     }
 
     // Implements {@link KeyboardState.SwitchActions}.
@@ -347,15 +346,11 @@
         return mKeyboardView != null && !mKeyboardView.isInSlidingKeyInput();
     }
 
-    private boolean isSinglePointer() {
-        return mKeyboardView != null && mKeyboardView.getPointerCount() == 1;
-    }
-
     /**
      * Updates state machine to figure out when to automatically switch back to the previous mode.
      */
     public void onCodeInput(final int code) {
-        mState.onCodeInput(code, isSinglePointer(), mLatinIME.getCurrentAutoCapsState());
+        mState.onCodeInput(code, mLatinIME.getCurrentAutoCapsState());
     }
 
     public MainKeyboardView getMainKeyboardView() {
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index e4e75c3..7941fcb 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -497,7 +497,7 @@
             }
         }
 
-        if (key.hasPopupHint() && key.mMoreKeys != null && key.mMoreKeys.length > 0) {
+        if (key.hasPopupHint() && key.mMoreKeys != null) {
             drawKeyPopupHint(key, canvas, paint, params);
         }
     }
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index ba78d01..6c6fc61 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -333,6 +333,10 @@
 
         private static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel,
                 final ObjectAnimator animatorToStart) {
+            if (animatorToCancel == null || animatorToStart == null) {
+                // TODO: Stop using null as a no-operation animator.
+                return;
+            }
             float startFraction = 0.0f;
             if (animatorToCancel.isStarted()) {
                 animatorToCancel.cancel();
@@ -366,7 +370,9 @@
             // When user hits the space or the enter key, just cancel the while-typing timer.
             final int typedCode = typedKey.mCode;
             if (typedCode == Constants.CODE_SPACE || typedCode == Constants.CODE_ENTER) {
-                startWhileTypingFadeinAnimation(keyboardView);
+                if (isTyping) {
+                    startWhileTypingFadeinAnimation(keyboardView);
+                }
                 return;
             }
 
@@ -581,6 +587,7 @@
 
     private ObjectAnimator loadObjectAnimator(final int resId, final Object target) {
         if (resId == 0) {
+            // TODO: Stop returning null.
             return null;
         }
         final ObjectAnimator animator = (ObjectAnimator)AnimatorInflater.loadAnimator(
@@ -609,8 +616,18 @@
 
     @ExternallyReferenced
     public void setAltCodeKeyWhileTypingAnimAlpha(final int alpha) {
+        if (mAltCodeKeyWhileTypingAnimAlpha == alpha) {
+            return;
+        }
+        // Update the visual of alt-code-key-while-typing.
         mAltCodeKeyWhileTypingAnimAlpha = alpha;
-        updateAltCodeKeyWhileTyping();
+        final Keyboard keyboard = getKeyboard();
+        if (keyboard == null) {
+            return;
+        }
+        for (final Key key : keyboard.mAltCodeKeysWhileTyping) {
+            invalidateKey(key);
+        }
     }
 
     public void setKeyboardActionListener(final KeyboardActionListener listener) {
@@ -893,10 +910,10 @@
         mSlidingKeyInputPreview.dismissSlidingKeyInputPreview();
     }
 
-    public void setGesturePreviewMode(final boolean drawsGesturePreviewTrail,
+    public void setGesturePreviewMode(final boolean drawsGestureTrail,
             final boolean drawsGestureFloatingPreviewText) {
         mGestureFloatingPreviewText.setPreviewEnabled(drawsGestureFloatingPreviewText);
-        mGestureTrailsPreview.setPreviewEnabled(drawsGesturePreviewTrail);
+        mGestureTrailsPreview.setPreviewEnabled(drawsGestureTrail);
     }
 
     public void showGestureFloatingPreviewText(final SuggestedWords suggestedWords) {
@@ -910,7 +927,7 @@
     }
 
     @Override
-    public void showGesturePreviewTrail(final PointerTracker tracker) {
+    public void showGestureTrail(final PointerTracker tracker) {
         locatePreviewPlacerView();
         mGestureFloatingPreviewText.setPreviewPosition(tracker);
         mGestureTrailsPreview.setPreviewPosition(tracker);
@@ -1054,6 +1071,7 @@
 
     @Override
     public void onShowMoreKeysPanel(final MoreKeysPanel panel) {
+        locatePreviewPlacerView();
         if (isShowingMoreKeysPanel()) {
             onDismissMoreKeysPanel();
         }
@@ -1082,10 +1100,6 @@
         return false;
     }
 
-    public int getPointerCount() {
-        return mOldPointerCount;
-    }
-
     @Override
     public boolean dispatchTouchEvent(MotionEvent event) {
         if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
@@ -1189,10 +1203,12 @@
                 if (ENABLE_USABILITY_STUDY_LOG) {
                     writeUsabilityStudyLog(me, action, eventTime, i, pointerId, px, py);
                 }
-                if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
-                    ResearchLogger.mainKeyboardView_processMotionEvent(
-                            me, action, eventTime, i, pointerId, px, py);
-                }
+                // TODO: This seems to be no longer necessary, and confusing because it leads to
+                // duplicate MotionEvents being recorded.
+                // if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
+                //     ResearchLogger.mainKeyboardView_processMotionEvent(
+                //             me, action, eventTime, i, pointerId, px, py);
+                // }
             }
         } else {
             final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
@@ -1274,16 +1290,6 @@
         invalidateKey(shortcutKey);
     }
 
-    private void updateAltCodeKeyWhileTyping() {
-        final Keyboard keyboard = getKeyboard();
-        if (keyboard == null) {
-            return;
-        }
-        for (final Key key : keyboard.mAltCodeKeysWhileTyping) {
-            invalidateKey(key);
-        }
-    }
-
     public void startDisplayLanguageOnSpacebar(final boolean subtypeChanged,
             final boolean needsToDisplayLanguage, final boolean hasMultipleEnabledIMEsOrSubtypes) {
         mNeedsToDisplayLanguage = needsToDisplayLanguage;
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 91c4319..1742393 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -25,6 +25,7 @@
 import com.android.inputmethod.keyboard.internal.GestureStroke;
 import com.android.inputmethod.keyboard.internal.GestureStroke.GestureStrokeParams;
 import com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewPoints;
+import com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewPoints.GestureStrokePreviewParams;
 import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
 import com.android.inputmethod.latin.CollectionUtils;
 import com.android.inputmethod.latin.Constants;
@@ -83,7 +84,7 @@
         public void dismissKeyPreview(PointerTracker tracker);
         public void showSlidingKeyInputPreview(PointerTracker tracker);
         public void dismissSlidingKeyInputPreview();
-        public void showGesturePreviewTrail(PointerTracker tracker);
+        public void showGestureTrail(PointerTracker tracker);
     }
 
     public interface TimerProxy {
@@ -161,6 +162,7 @@
     // Parameters for pointer handling.
     private static PointerTrackerParams sParams;
     private static GestureStrokeParams sGestureStrokeParams;
+    private static GestureStrokePreviewParams sGesturePreviewParams;
     private static boolean sNeedsPhantomSuddenMoveEventHack;
     // Move this threshold to resource.
     // TODO: Device specific parameter would be better for device specific hack?
@@ -176,7 +178,7 @@
     private DrawingProxy mDrawingProxy;
     private TimerProxy mTimerProxy;
     private KeyDetector mKeyDetector;
-    private KeyboardActionListener mListener = EMPTY_LISTENER;
+    private KeyboardActionListener mListener = KeyboardActionListener.Adapter.EMPTY_LISTENER;
 
     private Keyboard mKeyboard;
     private int mPhantonSuddenMoveThreshold;
@@ -318,8 +320,8 @@
     // true if keyboard layout has been changed.
     private boolean mKeyboardLayoutHasBeenChanged;
 
-    // true if this pointer is no longer tracking touch event.
-    private boolean mIsTrackingCanceled;
+    // true if this pointer is no longer triggering any action because it has been canceled.
+    private boolean mIsTrackingForActionDisabled;
 
     // the more keys panel currently being shown. equals null if no panel is active.
     private MoreKeysPanel mMoreKeysPanel;
@@ -333,22 +335,20 @@
     // true if a sliding key input is allowed.
     private boolean mIsAllowedSlidingKeyInput;
 
-    // Empty {@link KeyboardActionListener}
-    private static final KeyboardActionListener EMPTY_LISTENER =
-            new KeyboardActionListener.Adapter();
-
     private final GestureStrokeWithPreviewPoints mGestureStrokeWithPreviewPoints;
 
     public static void init(final boolean needsPhantomSuddenMoveEventHack) {
         sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack;
         sParams = PointerTrackerParams.DEFAULT;
         sGestureStrokeParams = GestureStrokeParams.DEFAULT;
+        sGesturePreviewParams = GestureStrokePreviewParams.DEFAULT;
         sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
     }
 
     public static void setParameters(final TypedArray mainKeyboardViewAttr) {
         sParams = new PointerTrackerParams(mainKeyboardViewAttr);
         sGestureStrokeParams = new GestureStrokeParams(mainKeyboardViewAttr);
+        sGesturePreviewParams = new GestureStrokePreviewParams(mainKeyboardViewAttr);
         sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
     }
 
@@ -432,7 +432,7 @@
         }
         mPointerId = id;
         mGestureStrokeWithPreviewPoints = new GestureStrokeWithPreviewPoints(
-                id, sGestureStrokeParams);
+                id, sGestureStrokeParams, sGesturePreviewParams);
         setKeyDetectorInner(handler.getKeyDetector());
         mListener = handler.getKeyboardActionListener();
         mDrawingProxy = handler.getDrawingProxy();
@@ -441,7 +441,11 @@
 
     // Returns true if keyboard has been changed by this callback.
     private boolean callListenerOnPressAndCheckKeyboardLayoutChange(final Key key) {
-        if (sInGesture || mIsDetectingGesture) {
+        // While gesture input is going on, this method should be a no-operation. But when gesture
+        // input has been canceled, <code>sInGesture</code> and <code>mIsDetectingGesture</code>
+        // are set to false. To keep this method is a no-operation,
+        // <code>mIsTrackingForActionDisabled</code> should also be taken account of.
+        if (sInGesture || mIsDetectingGesture || mIsTrackingForActionDisabled) {
             return false;
         }
         final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier();
@@ -455,7 +459,7 @@
             return false;
         }
         if (key.isEnabled()) {
-            mListener.onPressKey(key.mCode);
+            mListener.onPressKey(key.mCode, getActivePointerTrackerCount() == 1);
             final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
             mKeyboardLayoutHasBeenChanged = false;
             mTimerProxy.startTypingStateTimer(key);
@@ -500,7 +504,8 @@
     // primaryCode is different from {@link Key#mCode}.
     private void callListenerOnRelease(final Key key, final int primaryCode,
             final boolean withSliding) {
-        if (sInGesture || mIsDetectingGesture) {
+        // See the comment at {@link #callListenerOnPressAndCheckKeyboardLayoutChange(Key}}.
+        if (sInGesture || mIsDetectingGesture || mIsTrackingForActionDisabled) {
             return;
         }
         final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier();
@@ -522,6 +527,13 @@
         }
     }
 
+    private void callListenerOnFinishSlidingInput() {
+        if (DEBUG_LISTENER) {
+            Log.d(TAG, String.format("[%d] onFinishSlidingInput", mPointerId));
+        }
+        mListener.onFinishSlidingInput();
+    }
+
     private void callListenerOnCancelInput() {
         if (DEBUG_LISTENER) {
             Log.d(TAG, String.format("[%d] onCancelInput", mPointerId));
@@ -732,7 +744,7 @@
             dismissAllMoreKeysPanels();
         }
         mTimerProxy.cancelLongPressTimer();
-        mDrawingProxy.showGesturePreviewTrail(this);
+        mDrawingProxy.showGestureTrail(this);
     }
 
     public void updateBatchInputByTimer(final long eventTime) {
@@ -745,10 +757,10 @@
         if (key != null) {
             updateBatchInput(eventTime);
         }
-        if (mIsTrackingCanceled) {
+        if (mIsTrackingForActionDisabled) {
             return;
         }
-        mDrawingProxy.showGesturePreviewTrail(this);
+        mDrawingProxy.showGestureTrail(this);
     }
 
     private void updateBatchInput(final long eventTime) {
@@ -777,7 +789,7 @@
                 sInGesture = false;
                 sTimeRecorder.onEndBatchInput(eventTime);
                 mTimerProxy.cancelAllUpdateBatchInputTimers();
-                if (!mIsTrackingCanceled) {
+                if (!mIsTrackingForActionDisabled) {
                     if (DEBUG_LISTENER) {
                         Log.d(TAG, String.format("[%d] onEndBatchInput   : batchPoints=%d",
                                 mPointerId, sAggregratedPointers.getPointerSize()));
@@ -786,10 +798,10 @@
                 }
             }
         }
-        if (mIsTrackingCanceled) {
+        if (mIsTrackingForActionDisabled) {
             return;
         }
-        mDrawingProxy.showGesturePreviewTrail(this);
+        mDrawingProxy.showGestureTrail(this);
     }
 
     private void cancelBatchInput() {
@@ -846,7 +858,7 @@
                 if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
                     ResearchLogger.pointerTracker_onDownEvent(deltaT, distance * distance);
                 }
-                cancelTracking();
+                cancelTrackingForAction();
                 return;
             }
         }
@@ -887,7 +899,7 @@
                 || (key != null && key.isModifier())
                 || mKeyDetector.alwaysAllowsSlidingInput();
         mKeyboardLayoutHasBeenChanged = false;
-        mIsTrackingCanceled = false;
+        mIsTrackingForActionDisabled = false;
         resetSlidingKeyInput();
         if (key != null) {
             // This onPress call may have changed keyboard layout. Those cases are detected at
@@ -947,7 +959,7 @@
         if (DEBUG_MOVE_EVENT) {
             printTouchEvent("onMoveEvent:", x, y, eventTime);
         }
-        if (mIsTrackingCanceled) {
+        if (mIsTrackingForActionDisabled) {
             return;
         }
 
@@ -985,6 +997,9 @@
             key = onMoveKey(x, y);
         }
         onMoveToNewKey(key, x, y);
+        if (mIsTrackingForActionDisabled) {
+            return;
+        }
         startLongPressTimer(key);
         setPressedKeyGraphics(key, eventTime);
     }
@@ -1028,7 +1043,7 @@
 
     private void processSildeOutFromOldKey(final Key oldKey) {
         setReleasedKeyGraphics(oldKey);
-        callListenerOnRelease(oldKey, oldKey.mCode, true);
+        callListenerOnRelease(oldKey, oldKey.mCode, true /* withSliding */);
         startSlidingKeyInput(oldKey);
         mTimerProxy.cancelKeyTimers();
     }
@@ -1069,11 +1084,11 @@
                         + " detected sliding finger while multi touching", mPointerId));
             }
             onUpEvent(x, y, eventTime);
-            cancelTracking();
+            cancelTrackingForAction();
             setReleasedKeyGraphics(oldKey);
         } else {
             if (!mIsDetectingGesture) {
-                cancelTracking();
+                cancelTrackingForAction();
             }
             setReleasedKeyGraphics(oldKey);
         }
@@ -1087,7 +1102,7 @@
             onMoveToNewKey(null, x, y);
         } else {
             if (!mIsDetectingGesture) {
-                cancelTracking();
+                cancelTrackingForAction();
             }
         }
     }
@@ -1155,11 +1170,13 @@
             return;
         }
         onUpEventInternal(mLastX, mLastY, eventTime);
-        cancelTracking();
+        cancelTrackingForAction();
     }
 
     private void onUpEventInternal(final int x, final int y, final long eventTime) {
         mTimerProxy.cancelKeyTimers();
+        final boolean isInSlidingKeyInput = mIsInSlidingKeyInput;
+        final boolean isInSlidingKeyInputFromModifier = mIsInSlidingKeyInputFromModifier;
         resetSlidingKeyInput();
         mIsDetectingGesture = false;
         final Key currentKey = mCurrentKey;
@@ -1168,7 +1185,7 @@
         setReleasedKeyGraphics(currentKey);
 
         if (isShowingMoreKeysPanel()) {
-            if (!mIsTrackingCanceled) {
+            if (!mIsTrackingForActionDisabled) {
                 final int translatedX = mMoreKeysPanel.translateX(x);
                 final int translatedY = mMoreKeysPanel.translateY(y);
                 mMoreKeysPanel.onUpEvent(translatedX, translatedY, mPointerId, eventTime);
@@ -1180,17 +1197,22 @@
 
         if (sInGesture) {
             if (currentKey != null) {
-                callListenerOnRelease(currentKey, currentKey.mCode, true);
+                callListenerOnRelease(currentKey, currentKey.mCode, true /* withSliding */);
             }
             mayEndBatchInput(eventTime);
             return;
         }
 
-        if (mIsTrackingCanceled) {
+        if (mIsTrackingForActionDisabled) {
             return;
         }
-        if (currentKey != null && !currentKey.isRepeatable()) {
-            detectAndSendKey(currentKey, mKeyX, mKeyY, eventTime);
+        if (currentKey != null && currentKey.isRepeatable() && !isInSlidingKeyInput) {
+            // Repeatable key has been registered in {@link #onDownEventInternal(int,int,long)}.
+            return;
+        }
+        detectAndSendKey(currentKey, mKeyX, mKeyY, eventTime);
+        if (isInSlidingKeyInputFromModifier) {
+            callListenerOnFinishSlidingInput();
         }
     }
 
@@ -1203,16 +1225,16 @@
     }
 
     @Override
-    public void cancelTracking() {
+    public void cancelTrackingForAction() {
         if (isShowingMoreKeysPanel()) {
             return;
         }
-        mIsTrackingCanceled = true;
+        mIsTrackingForActionDisabled = true;
     }
 
     public void onLongPressed() {
         resetSlidingKeyInput();
-        cancelTracking();
+        cancelTrackingForAction();
         setReleasedKeyGraphics(mCurrentKey);
         sPointerTrackerQueue.remove(this);
     }
@@ -1239,10 +1261,13 @@
     }
 
     private void startRepeatKey(final Key key) {
-        if (key != null && key.isRepeatable() && !sInGesture) {
-            onRegisterKey(key);
-            mTimerProxy.startKeyRepeatTimer(this);
-        }
+        if (sInGesture) return;
+        if (key == null) return;
+        if (!key.isRepeatable()) return;
+        // Don't start key repeat when we are in sliding input mode.
+        if (mIsInSlidingKeyInput) return;
+        onRegisterKey(key);
+        mTimerProxy.startKeyRepeatTimer(this);
     }
 
     public void onRegisterKey(final Key key) {
@@ -1295,9 +1320,15 @@
     }
 
     private void startLongPressTimer(final Key key) {
-        if (key != null && key.isLongPressEnabled() && !sInGesture) {
-            mTimerProxy.startLongPressTimer(this);
-        }
+        if (sInGesture) return;
+        if (key == null) return;
+        if (!key.isLongPressEnabled()) return;
+        // Caveat: Please note that isLongPressEnabled() can be true even if the current key
+        // doesn't have its more keys. (e.g. spacebar, globe key)
+        // We always need to start the long press timer if the key has its more keys regardless of
+        // whether or not we are in the sliding input mode.
+        if (mIsInSlidingKeyInput && key.mMoreKeys == null) return;
+        mTimerProxy.startLongPressTimer(this);
     }
 
     private void detectAndSendKey(final Key key, final int x, final int y, final long eventTime) {
@@ -1308,7 +1339,7 @@
 
         final int code = key.mCode;
         callListenerOnCodeInput(key, code, x, y, eventTime);
-        callListenerOnRelease(key, code, false);
+        callListenerOnRelease(key, code, false /* withSliding */);
     }
 
     private void printTouchEvent(final String title, final int x, final int y,
@@ -1316,6 +1347,6 @@
         final Key key = mKeyDetector.detectHitKey(x, y);
         final String code = KeyDetector.printableCode(key);
         Log.d(TAG, String.format("[%d]%s%s %4d %4d %5d %s", mPointerId,
-                (mIsTrackingCanceled ? "-" : " "), title, x, y, eventTime, code));
+                (mIsTrackingForActionDisabled ? "-" : " "), title, x, y, eventTime, code));
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
index b77e378..57d3fed 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -79,23 +79,6 @@
         mNativeProximityInfo = createNativeProximityInfo(touchPositionCorrection);
     }
 
-    /**
-     * Constructor for subclasses such as
-     * {@link com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo}.
-     */
-    protected ProximityInfo(final int[] proximityCharsArray, final int gridWidth,
-            final int gridHeight) {
-        this("", 1, 1, 1, 1, 1, 1, EMPTY_KEY_ARRAY, null);
-        mNativeProximityInfo = setProximityInfoNative("" /* locale */,
-                gridWidth /* displayWidth */, gridHeight /* displayHeight */,
-                gridWidth, gridHeight, 1 /* mostCommonKeyWidth */,
-                1 /* mostCommonKeyHeight */, proximityCharsArray, 0 /* keyCount */,
-                null /*keyXCoordinates */, null /* keyYCoordinates */,
-                null /* keyWidths */, null /* keyHeights */, null /* keyCharCodes */,
-                null /* sweetSpotCenterXs */, null /* sweetSpotCenterYs */,
-                null /* sweetSpotRadii */);
-    }
-
     private long mNativeProximityInfo;
     static {
         JniUtils.loadNativeLibrary();
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java
index 53da47c..70363e6 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java
@@ -83,9 +83,8 @@
         public final int mRecognitionMinimumTime; // msec
         public final float mRecognitionSpeedThreshold; // keyWidth/sec
 
-        // Default GestureStroke parameters for test.
-        public static final GestureStrokeParams FOR_TEST = new GestureStrokeParams();
-        public static final GestureStrokeParams DEFAULT = FOR_TEST;
+        // Default GestureStroke parameters.
+        public static final GestureStrokeParams DEFAULT = new GestureStrokeParams();
 
         private GestureStrokeParams() {
             // These parameter values are default and intended for testing.
@@ -146,7 +145,7 @@
     public void setKeyboardGeometry(final int keyWidth, final int keyboardHeight) {
         mKeyWidth = keyWidth;
         mMinYCoordinate = -(int)(keyboardHeight * EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO);
-        mMaxYCoordinate = keyboardHeight - 1;
+        mMaxYCoordinate = keyboardHeight;
         // TODO: Find an appropriate base metric for these length. Maybe diagonal length of the key?
         mDetectFastMoveSpeedThreshold = (int)(keyWidth * mParams.mDetectFastMoveSpeedThreshold);
         mGestureDynamicDistanceThresholdFrom =
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java
index 3315954..b31f00b 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java
@@ -16,40 +16,74 @@
 
 package com.android.inputmethod.keyboard.internal;
 
+import android.content.res.TypedArray;
+
+import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.ResizableIntArray;
 
 public final class GestureStrokeWithPreviewPoints extends GestureStroke {
     public static final int PREVIEW_CAPACITY = 256;
 
-    private static final boolean ENABLE_INTERPOLATION = true;
-
     private final ResizableIntArray mPreviewEventTimes = new ResizableIntArray(PREVIEW_CAPACITY);
     private final ResizableIntArray mPreviewXCoordinates = new ResizableIntArray(PREVIEW_CAPACITY);
     private final ResizableIntArray mPreviewYCoordinates = new ResizableIntArray(PREVIEW_CAPACITY);
 
+    private final GestureStrokePreviewParams mPreviewParams;
+
     private int mStrokeId;
     private int mLastPreviewSize;
     private final HermiteInterpolator mInterpolator = new HermiteInterpolator();
     private int mLastInterpolatedPreviewIndex;
 
-    private int mMinPreviewSamplingDistanceSquared;
     private int mLastX;
     private int mLastY;
-    private double mMinPreviewSamplingDistance;
     private double mDistanceFromLastSample;
 
-    // TODO: Move these constants to resource.
-    // The minimum linear distance between sample points for preview in keyWidth unit.
-    private static final float MIN_PREVIEW_SAMPLING_RATIO_TO_KEY_WIDTH = 0.1f;
-    // The minimum trail distance between sample points for preview in keyWidth unit when using
-    // interpolation.
-    private static final float MIN_PREVIEW_SAMPLING_RATIO_TO_KEY_WIDTH_WITH_INTERPOLATION = 0.2f;
-    // The angular threshold to use interpolation in radian. PI/12 is 15 degree.
-    private static final double INTERPOLATION_ANGULAR_THRESHOLD = Math.PI / 12.0d;
-    private static final int MAX_INTERPOLATION_PARTITION = 4;
+    public static final class GestureStrokePreviewParams {
+        public final double mMinSamplingDistance; // in pixel
+        public final double mMaxInterpolationAngularThreshold; // in radian
+        public final double mMaxInterpolationDistanceThreshold; // in pixel
+        public final int mMaxInterpolationSegments;
 
-    public GestureStrokeWithPreviewPoints(final int pointerId, final GestureStrokeParams params) {
-        super(pointerId, params);
+        public static final GestureStrokePreviewParams DEFAULT = new GestureStrokePreviewParams();
+
+        private static final int DEFAULT_MAX_INTERPOLATION_ANGULAR_THRESHOLD = 15; // in degree
+
+        private GestureStrokePreviewParams() {
+            mMinSamplingDistance = 0.0d;
+            mMaxInterpolationAngularThreshold =
+                    degreeToRadian(DEFAULT_MAX_INTERPOLATION_ANGULAR_THRESHOLD);
+            mMaxInterpolationDistanceThreshold = mMinSamplingDistance;
+            mMaxInterpolationSegments = 4;
+        }
+
+        private static double degreeToRadian(final int degree) {
+            return (double)degree / 180.0d * Math.PI;
+        }
+
+        public GestureStrokePreviewParams(final TypedArray mainKeyboardViewAttr) {
+            mMinSamplingDistance = mainKeyboardViewAttr.getDimension(
+                    R.styleable.MainKeyboardView_gestureTrailMinSamplingDistance,
+                    (float)DEFAULT.mMinSamplingDistance);
+            final int interpolationAngularDegree = mainKeyboardViewAttr.getInteger(R.styleable
+                    .MainKeyboardView_gestureTrailMaxInterpolationAngularThreshold, 0);
+            mMaxInterpolationAngularThreshold = (interpolationAngularDegree <= 0)
+                    ? DEFAULT.mMaxInterpolationAngularThreshold
+                    : degreeToRadian(interpolationAngularDegree);
+            mMaxInterpolationDistanceThreshold = mainKeyboardViewAttr.getDimension(R.styleable
+                    .MainKeyboardView_gestureTrailMaxInterpolationDistanceThreshold,
+                    (float)DEFAULT.mMaxInterpolationDistanceThreshold);
+            mMaxInterpolationSegments = mainKeyboardViewAttr.getInteger(
+                    R.styleable.MainKeyboardView_gestureTrailMaxInterpolationSegments,
+                    DEFAULT.mMaxInterpolationSegments);
+        }
+    }
+
+    public GestureStrokeWithPreviewPoints(final int pointerId,
+            final GestureStrokeParams strokeParams,
+            final GestureStrokePreviewParams previewParams) {
+        super(pointerId, strokeParams);
+        mPreviewParams = previewParams;
     }
 
     @Override
@@ -67,34 +101,13 @@
         return mStrokeId;
     }
 
-    @Override
-    public void setKeyboardGeometry(final int keyWidth, final int keyboardHeight) {
-        super.setKeyboardGeometry(keyWidth, keyboardHeight);
-        final float samplingRatioToKeyWidth = ENABLE_INTERPOLATION
-                ? MIN_PREVIEW_SAMPLING_RATIO_TO_KEY_WIDTH_WITH_INTERPOLATION
-                : MIN_PREVIEW_SAMPLING_RATIO_TO_KEY_WIDTH;
-        mMinPreviewSamplingDistance = keyWidth * samplingRatioToKeyWidth;
-        mMinPreviewSamplingDistanceSquared = (int)(
-                mMinPreviewSamplingDistance * mMinPreviewSamplingDistance);
-    }
-
-    private boolean needsSampling(final int x, final int y, final boolean isMajorEvent) {
-        if (ENABLE_INTERPOLATION) {
-            mDistanceFromLastSample += Math.hypot(x - mLastX, y - mLastY);
-            mLastX = x;
-            mLastY = y;
-            if (mDistanceFromLastSample >= mMinPreviewSamplingDistance) {
-                mDistanceFromLastSample = 0.0d;
-                return true;
-            }
-            return false;
-        }
-
-        final int dx = x - mLastX;
-        final int dy = y - mLastY;
-        if (isMajorEvent || dx * dx + dy * dy >= mMinPreviewSamplingDistanceSquared) {
-            mLastX = x;
-            mLastY = y;
+    private boolean needsSampling(final int x, final int y) {
+        mDistanceFromLastSample += Math.hypot(x - mLastX, y - mLastY);
+        mLastX = x;
+        mLastY = y;
+        final boolean isDownEvent = (mPreviewEventTimes.getLength() == 0);
+        if (mDistanceFromLastSample >= mPreviewParams.mMinSamplingDistance || isDownEvent) {
+            mDistanceFromLastSample = 0.0d;
             return true;
         }
         return false;
@@ -103,7 +116,7 @@
     @Override
     public boolean addPointOnKeyboard(final int x, final int y, final int time,
             final boolean isMajorEvent) {
-        if (needsSampling(x, y, isMajorEvent)) {
+        if (needsSampling(x, y)) {
             mPreviewEventTimes.add(time);
             mPreviewXCoordinates.add(x);
             mPreviewYCoordinates.add(y);
@@ -132,17 +145,14 @@
      *
      * @param lastInterpolatedIndex the start index of the last interpolated segment of
      *        <code>eventTimes</code>, <code>xCoords</code>, and <code>yCoords</code>.
-     * @param eventTimes the event time array of gesture preview trail to be drawn.
-     * @param xCoords the x-coordinates array of gesture preview trail to be drawn.
-     * @param yCoords the y-coordinates array of gesture preview trail to be drawn.
+     * @param eventTimes the event time array of gesture trail to be drawn.
+     * @param xCoords the x-coordinates array of gesture trail to be drawn.
+     * @param yCoords the y-coordinates array of gesture trail to be drawn.
      * @return the start index of the last interpolated segment of input arrays.
      */
     public int interpolateStrokeAndReturnStartIndexOfLastSegment(final int lastInterpolatedIndex,
             final ResizableIntArray eventTimes, final ResizableIntArray xCoords,
-            final ResizableIntArray yCoords) {
-        if (!ENABLE_INTERPOLATION) {
-            return lastInterpolatedIndex;
-        }
+            final ResizableIntArray yCoords, final ResizableIntArray types) {
         final int size = mPreviewEventTimes.getLength();
         final int[] pt = mPreviewEventTimes.getPrimitiveArray();
         final int[] px = mPreviewXCoordinates.getPrimitiveArray();
@@ -161,23 +171,35 @@
             mInterpolator.setInterval(p0, p1, p2, p3);
             final double m1 = Math.atan2(mInterpolator.mSlope1Y, mInterpolator.mSlope1X);
             final double m2 = Math.atan2(mInterpolator.mSlope2Y, mInterpolator.mSlope2X);
-            final double dm = Math.abs(angularDiff(m2, m1));
-            final int partition = Math.min((int)Math.ceil(dm / INTERPOLATION_ANGULAR_THRESHOLD),
-                    MAX_INTERPOLATION_PARTITION);
+            final double deltaAngle = Math.abs(angularDiff(m2, m1));
+            final int segmentsByAngle = (int)Math.ceil(
+                    deltaAngle / mPreviewParams.mMaxInterpolationAngularThreshold);
+            final double deltaDistance = Math.hypot(mInterpolator.mP1X - mInterpolator.mP2X,
+                    mInterpolator.mP1Y - mInterpolator.mP2Y);
+            final int segmentsByDistance = (int)Math.ceil(deltaDistance
+                    / mPreviewParams.mMaxInterpolationDistanceThreshold);
+            final int segments = Math.min(mPreviewParams.mMaxInterpolationSegments,
+                    Math.max(segmentsByAngle, segmentsByDistance));
             final int t1 = eventTimes.get(d1);
             final int dt = pt[p2] - pt[p1];
             d1++;
-            for (int i = 1; i < partition; i++) {
-                final float t = i / (float)partition;
+            for (int i = 1; i < segments; i++) {
+                final float t = i / (float)segments;
                 mInterpolator.interpolate(t);
                 eventTimes.add(d1, (int)(dt * t) + t1);
                 xCoords.add(d1, (int)mInterpolator.mInterpolatedX);
                 yCoords.add(d1, (int)mInterpolator.mInterpolatedY);
+                if (GestureTrail.DBG_SHOW_POINTS) {
+                    types.add(d1, GestureTrail.POINT_TYPE_INTERPOLATED);
+                }
                 d1++;
             }
             eventTimes.add(d1, pt[p2]);
             xCoords.add(d1, px[p2]);
             yCoords.add(d1, py[p2]);
+            if (GestureTrail.DBG_SHOW_POINTS) {
+                types.add(d1, GestureTrail.POINT_TYPE_SAMPLED);
+            }
         }
         return lastInterpolatedDrawIndex;
     }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java b/java/src/com/android/inputmethod/keyboard/internal/GestureTrail.java
similarity index 81%
rename from java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java
rename to java/src/com/android/inputmethod/keyboard/internal/GestureTrail.java
index 7fd1bed..03dd1c3 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureTrail.java
@@ -18,6 +18,7 @@
 
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.Rect;
@@ -28,19 +29,26 @@
 import com.android.inputmethod.latin.ResizableIntArray;
 
 /*
- * @attr ref R.styleable#MainKeyboardView_gesturePreviewTrailFadeoutStartDelay
- * @attr ref R.styleable#MainKeyboardView_gesturePreviewTrailFadeoutDuration
- * @attr ref R.styleable#MainKeyboardView_gesturePreviewTrailUpdateInterval
- * @attr ref R.styleable#MainKeyboardView_gesturePreviewTrailColor
- * @attr ref R.styleable#MainKeyboardView_gesturePreviewTrailWidth
+ * @attr ref R.styleable#MainKeyboardView_gestureTrailFadeoutStartDelay
+ * @attr ref R.styleable#MainKeyboardView_gestureTrailFadeoutDuration
+ * @attr ref R.styleable#MainKeyboardView_gestureTrailUpdateInterval
+ * @attr ref R.styleable#MainKeyboardView_gestureTrailColor
+ * @attr ref R.styleable#MainKeyboardView_gestureTrailWidth
  */
-final class GesturePreviewTrail {
+final class GestureTrail {
+    public static final boolean DBG_SHOW_POINTS = false;
+    public static final int POINT_TYPE_SAMPLED = 0;
+    public static final int POINT_TYPE_INTERPOLATED = 1;
+    public static final int POINT_TYPE_COMPROMISED = 2;
+
     private static final int DEFAULT_CAPACITY = GestureStrokeWithPreviewPoints.PREVIEW_CAPACITY;
 
     // These three {@link ResizableIntArray}s should be synchronized by {@link #mEventTimes}.
     private final ResizableIntArray mXCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
     private final ResizableIntArray mYCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
     private final ResizableIntArray mEventTimes = new ResizableIntArray(DEFAULT_CAPACITY);
+    private final ResizableIntArray mPointTypes = new ResizableIntArray(
+            DBG_SHOW_POINTS ? DEFAULT_CAPACITY : 0);
     private int mCurrentStrokeId = -1;
     // The wall time of the zero value in {@link #mEventTimes}
     private long mCurrentTimeBase;
@@ -62,26 +70,26 @@
 
         public Params(final TypedArray mainKeyboardViewAttr) {
             mTrailColor = mainKeyboardViewAttr.getColor(
-                    R.styleable.MainKeyboardView_gesturePreviewTrailColor, 0);
+                    R.styleable.MainKeyboardView_gestureTrailColor, 0);
             mTrailStartWidth = mainKeyboardViewAttr.getDimension(
-                    R.styleable.MainKeyboardView_gesturePreviewTrailStartWidth, 0.0f);
+                    R.styleable.MainKeyboardView_gestureTrailStartWidth, 0.0f);
             mTrailEndWidth = mainKeyboardViewAttr.getDimension(
-                    R.styleable.MainKeyboardView_gesturePreviewTrailEndWidth, 0.0f);
+                    R.styleable.MainKeyboardView_gestureTrailEndWidth, 0.0f);
             final int PERCENTAGE_INT = 100;
             mTrailBodyRatio = (float)mainKeyboardViewAttr.getInt(
-                    R.styleable.MainKeyboardView_gesturePreviewTrailBodyRatio, PERCENTAGE_INT)
+                    R.styleable.MainKeyboardView_gestureTrailBodyRatio, PERCENTAGE_INT)
                     / (float)PERCENTAGE_INT;
             final int trailShadowRatioInt = mainKeyboardViewAttr.getInt(
-                    R.styleable.MainKeyboardView_gesturePreviewTrailShadowRatio, 0);
+                    R.styleable.MainKeyboardView_gestureTrailShadowRatio, 0);
             mTrailShadowEnabled = (trailShadowRatioInt > 0);
             mTrailShadowRatio = (float)trailShadowRatioInt / (float)PERCENTAGE_INT;
-            mFadeoutStartDelay = mainKeyboardViewAttr.getInt(
-                    R.styleable.MainKeyboardView_gesturePreviewTrailFadeoutStartDelay, 0);
-            mFadeoutDuration = mainKeyboardViewAttr.getInt(
-                    R.styleable.MainKeyboardView_gesturePreviewTrailFadeoutDuration, 0);
+            mFadeoutStartDelay = DBG_SHOW_POINTS ? 2000 : mainKeyboardViewAttr.getInt(
+                    R.styleable.MainKeyboardView_gestureTrailFadeoutStartDelay, 0);
+            mFadeoutDuration = DBG_SHOW_POINTS ? 200 : mainKeyboardViewAttr.getInt(
+                    R.styleable.MainKeyboardView_gestureTrailFadeoutDuration, 0);
             mTrailLingerDuration = mFadeoutStartDelay + mFadeoutDuration;
             mUpdateInterval = mainKeyboardViewAttr.getInt(
-                    R.styleable.MainKeyboardView_gesturePreviewTrailUpdateInterval, 0);
+                    R.styleable.MainKeyboardView_gestureTrailUpdateInterval, 0);
         }
     }
 
@@ -125,7 +133,7 @@
         final int lastInterpolatedIndex = (strokeId == mCurrentStrokeId)
                 ? mLastInterpolatedDrawIndex : trailSize;
         mLastInterpolatedDrawIndex = stroke.interpolateStrokeAndReturnStartIndexOfLastSegment(
-                lastInterpolatedIndex, mEventTimes, mXCoordinates, mYCoordinates);
+                lastInterpolatedIndex, mEventTimes, mXCoordinates, mYCoordinates, mPointTypes);
         if (strokeId != mCurrentStrokeId) {
             final int elapsedTime = (int)(downTime - mCurrentTimeBase);
             for (int i = mTrailStartIndex; i < trailSize; i++) {
@@ -178,12 +186,12 @@
     private final Rect mRoundedLineBounds = new Rect();
 
     /**
-     * Draw gesture preview trail
-     * @param canvas The canvas to draw the gesture preview trail
-     * @param paint The paint object to be used to draw the gesture preview trail
+     * Draw gesture trail
+     * @param canvas The canvas to draw the gesture trail
+     * @param paint The paint object to be used to draw the gesture trail
      * @param outBoundsRect the bounding box of this gesture trail drawing
-     * @param params The drawing parameters of gesture preview trail
-     * @return true if some gesture preview trails remain to be drawn
+     * @param params The drawing parameters of gesture trail
+     * @return true if some gesture trails remain to be drawn
      */
     public boolean drawGestureTrail(final Canvas canvas, final Paint paint,
             final Rect outBoundsRect, final Params params) {
@@ -204,6 +212,7 @@
         final int[] eventTimes = mEventTimes.getPrimitiveArray();
         final int[] xCoords = mXCoordinates.getPrimitiveArray();
         final int[] yCoords = mYCoordinates.getPrimitiveArray();
+        final int[] pointTypes = mPointTypes.getPrimitiveArray();
         final int sinceDown = (int)(SystemClock.uptimeMillis() - mCurrentTimeBase);
         int startIndex;
         for (startIndex = mTrailStartIndex; startIndex < trailSize; startIndex++) {
@@ -246,6 +255,17 @@
                         final int alpha = getAlpha(elapsedTime, params);
                         paint.setAlpha(alpha);
                         canvas.drawPath(path, paint);
+                        if (DBG_SHOW_POINTS) {
+                            if (pointTypes[i] == POINT_TYPE_INTERPOLATED) {
+                                paint.setColor(Color.RED);
+                            } else if (pointTypes[i] == POINT_TYPE_SAMPLED) {
+                                paint.setColor(0xFFA000FF);
+                            } else {
+                                paint.setColor(Color.GREEN);
+                            }
+                            canvas.drawCircle(p1x - 1, p1y - 1, 2, paint);
+                            paint.setColor(params.mTrailColor);
+                        }
                     }
                 }
                 p1x = p2x;
@@ -265,6 +285,9 @@
             mEventTimes.setLength(newSize);
             mXCoordinates.setLength(newSize);
             mYCoordinates.setLength(newSize);
+            if (DBG_SHOW_POINTS) {
+                mPointTypes.setLength(newSize);
+            }
             // The start index of the last segment of the stroke
             // {@link mLastInterpolatedDrawIndex} should also be updated because all array
             // elements have just been shifted for compaction or been zeroed.
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java
index 85558f1..1e4c43e 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java
@@ -29,7 +29,7 @@
 import android.view.View;
 
 import com.android.inputmethod.keyboard.PointerTracker;
-import com.android.inputmethod.keyboard.internal.GesturePreviewTrail.Params;
+import com.android.inputmethod.keyboard.internal.GestureTrail.Params;
 import com.android.inputmethod.latin.CollectionUtils;
 import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
 
@@ -37,9 +37,8 @@
  * Draw gesture trail preview graphics during gesture.
  */
 public final class GestureTrailsPreview extends AbstractDrawingPreview {
-    private final SparseArray<GesturePreviewTrail> mGesturePreviewTrails =
-            CollectionUtils.newSparseArray();
-    private final Params mGesturePreviewTrailParams;
+    private final SparseArray<GestureTrail> mGestureTrails = CollectionUtils.newSparseArray();
+    private final Params mGestureTrailParams;
     private final Paint mGesturePaint;
     private int mOffscreenWidth;
     private int mOffscreenHeight;
@@ -48,20 +47,20 @@
     private final Canvas mOffscreenCanvas = new Canvas();
     private final Rect mOffscreenSrcRect = new Rect();
     private final Rect mDirtyRect = new Rect();
-    private final Rect mGesturePreviewTrailBoundsRect = new Rect(); // per trail
+    private final Rect mGestureTrailBoundsRect = new Rect(); // per trail
 
     private final DrawingHandler mDrawingHandler;
 
     private static final class DrawingHandler
             extends StaticInnerHandlerWrapper<GestureTrailsPreview> {
-        private static final int MSG_UPDATE_GESTURE_PREVIEW_TRAIL = 0;
+        private static final int MSG_UPDATE_GESTURE_TRAIL = 0;
 
-        private final Params mGesturePreviewTrailParams;
+        private final Params mGestureTrailParams;
 
         public DrawingHandler(final GestureTrailsPreview outerInstance,
-                final Params gesturePreviewTrailParams) {
+                final Params gestureTrailParams) {
             super(outerInstance);
-            mGesturePreviewTrailParams = gesturePreviewTrailParams;
+            mGestureTrailParams = gestureTrailParams;
         }
 
         @Override
@@ -69,23 +68,23 @@
             final GestureTrailsPreview preview = getOuterInstance();
             if (preview == null) return;
             switch (msg.what) {
-            case MSG_UPDATE_GESTURE_PREVIEW_TRAIL:
+            case MSG_UPDATE_GESTURE_TRAIL:
                 preview.getDrawingView().invalidate();
                 break;
             }
         }
 
         public void postUpdateGestureTrailPreview() {
-            removeMessages(MSG_UPDATE_GESTURE_PREVIEW_TRAIL);
-            sendMessageDelayed(obtainMessage(MSG_UPDATE_GESTURE_PREVIEW_TRAIL),
-                    mGesturePreviewTrailParams.mUpdateInterval);
+            removeMessages(MSG_UPDATE_GESTURE_TRAIL);
+            sendMessageDelayed(obtainMessage(MSG_UPDATE_GESTURE_TRAIL),
+                    mGestureTrailParams.mUpdateInterval);
         }
     }
 
     public GestureTrailsPreview(final View drawingView, final TypedArray mainKeyboardViewAttr) {
         super(drawingView);
-        mGesturePreviewTrailParams = new Params(mainKeyboardViewAttr);
-        mDrawingHandler = new DrawingHandler(this, mGesturePreviewTrailParams);
+        mGestureTrailParams = new Params(mainKeyboardViewAttr);
+        mDrawingHandler = new DrawingHandler(this, mGestureTrailParams);
         final Paint gesturePaint = new Paint();
         gesturePaint.setAntiAlias(true);
         gesturePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
@@ -133,21 +132,20 @@
             offscreenCanvas.drawRect(dirtyRect, paint);
         }
         dirtyRect.setEmpty();
-        boolean needsUpdatingGesturePreviewTrail = false;
+        boolean needsUpdatingGestureTrail = false;
         // Draw gesture trails to offscreen buffer.
-        synchronized (mGesturePreviewTrails) {
+        synchronized (mGestureTrails) {
             // Trails count == fingers count that have ever been active.
-            final int trailsCount = mGesturePreviewTrails.size();
+            final int trailsCount = mGestureTrails.size();
             for (int index = 0; index < trailsCount; index++) {
-                final GesturePreviewTrail trail = mGesturePreviewTrails.valueAt(index);
-                needsUpdatingGesturePreviewTrail |=
-                        trail.drawGestureTrail(offscreenCanvas, paint,
-                                mGesturePreviewTrailBoundsRect, mGesturePreviewTrailParams);
-                // {@link #mGesturePreviewTrailBoundsRect} has bounding box of the trail.
-                dirtyRect.union(mGesturePreviewTrailBoundsRect);
+                final GestureTrail trail = mGestureTrails.valueAt(index);
+                needsUpdatingGestureTrail |= trail.drawGestureTrail(offscreenCanvas, paint,
+                        mGestureTrailBoundsRect, mGestureTrailParams);
+                // {@link #mGestureTrailBoundsRect} has bounding box of the trail.
+                dirtyRect.union(mGestureTrailBoundsRect);
             }
         }
-        return needsUpdatingGesturePreviewTrail;
+        return needsUpdatingGestureTrail;
     }
 
     /**
@@ -161,9 +159,9 @@
         }
         mayAllocateOffscreenBuffer();
         // Draw gesture trails to offscreen buffer.
-        final boolean needsUpdatingGesturePreviewTrail = drawGestureTrails(
+        final boolean needsUpdatingGestureTrail = drawGestureTrails(
                 mOffscreenCanvas, mGesturePaint, mDirtyRect);
-        if (needsUpdatingGesturePreviewTrail) {
+        if (needsUpdatingGestureTrail) {
             mDrawingHandler.postUpdateGestureTrailPreview();
         }
         // Transfer offscreen buffer to screen.
@@ -185,12 +183,12 @@
         if (!isPreviewEnabled()) {
             return;
         }
-        GesturePreviewTrail trail;
-        synchronized (mGesturePreviewTrails) {
-            trail = mGesturePreviewTrails.get(tracker.mPointerId);
+        GestureTrail trail;
+        synchronized (mGestureTrails) {
+            trail = mGestureTrails.get(tracker.mPointerId);
             if (trail == null) {
-                trail = new GesturePreviewTrail();
-                mGesturePreviewTrails.put(tracker.mPointerId, trail);
+                trail = new GestureTrail();
+                mGestureTrails.put(tracker.mPointerId, trail);
             }
         }
         trail.addStroke(tracker.getGestureStrokeWithPreviewPoints(), tracker.getDownTime());
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
index b1d4997..6af1bd7 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
@@ -28,9 +28,9 @@
  * This class contains all keyboard state transition logic.
  *
  * The input events are {@link #onLoadKeyboard()}, {@link #onSaveKeyboardState()},
- * {@link #onPressKey(int, boolean, int)}, {@link #onReleaseKey(int, boolean)},
- * {@link #onCodeInput(int, boolean, int)}, {@link #onCancelInput(boolean)},
- * {@link #onUpdateShiftState(int, int)}, {@link #onLongPressTimeout(int)}.
+ * {@link #onPressKey(int,boolean,int)}, {@link #onReleaseKey(int,boolean)},
+ * {@link #onCodeInput(int,int)}, {@link #onFinishSlidingInput()}, {@link #onCancelInput()},
+ * {@link #onUpdateShiftState(int,int)}, {@link #onLongPressTimeout(int)}.
  *
  * The actions are {@link SwitchActions}'s methods.
  */
@@ -74,6 +74,7 @@
     private static final int SWITCH_STATE_SYMBOL = 2;
     private static final int SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL = 3;
     private static final int SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE = 4;
+    private static final int SWITCH_STATE_MOMENTARY_ALPHA_SHIFT = 5;
     private int mSwitchState = SWITCH_STATE_ALPHA;
 
     private boolean mIsAlphabetMode;
@@ -96,16 +97,16 @@
         public boolean mIsValid;
         public boolean mIsAlphabetMode;
         public boolean mIsAlphabetShiftLocked;
-        public boolean mIsShifted;
+        public int mShiftMode;
 
         @Override
         public String toString() {
             if (!mIsValid) return "INVALID";
             if (mIsAlphabetMode) {
                 if (mIsAlphabetShiftLocked) return "ALPHABET_SHIFT_LOCKED";
-                return mIsShifted ? "ALPHABET_SHIFTED" : "ALPHABET";
+                return "ALPHABET_" + shiftModeToString(mShiftMode);
             } else {
-                return mIsShifted ? "SYMBOLS_SHIFTED" : "SYMBOLS";
+                return "SYMBOLS_" + shiftModeToString(mShiftMode);
             }
         }
     }
@@ -128,16 +129,21 @@
         onRestoreKeyboardState();
     }
 
+    private static final int UNSHIFT = 0;
+    private static final int MANUAL_SHIFT = 1;
+    private static final int AUTOMATIC_SHIFT = 2;
+    private static final int SHIFT_LOCK_SHIFTED = 3;
+
     public void onSaveKeyboardState() {
         final SavedKeyboardState state = mSavedKeyboardState;
         state.mIsAlphabetMode = mIsAlphabetMode;
         if (mIsAlphabetMode) {
             state.mIsAlphabetShiftLocked = mAlphabetShiftState.isShiftLocked();
-            state.mIsShifted = !state.mIsAlphabetShiftLocked
-                    && mAlphabetShiftState.isShiftedOrShiftLocked();
+            state.mShiftMode = mAlphabetShiftState.isAutomaticShifted() ? AUTOMATIC_SHIFT
+                    : (mAlphabetShiftState.isShiftedOrShiftLocked() ? MANUAL_SHIFT : UNSHIFT);
         } else {
             state.mIsAlphabetShiftLocked = mPrevMainKeyboardWasShiftLocked;
-            state.mIsShifted = mIsSymbolShifted;
+            state.mShiftMode = mIsSymbolShifted ? MANUAL_SHIFT : UNSHIFT;
         }
         state.mIsValid = true;
         if (DEBUG_EVENT) {
@@ -153,7 +159,7 @@
         if (!state.mIsValid || state.mIsAlphabetMode) {
             setAlphabetKeyboard();
         } else {
-            if (state.mIsShifted) {
+            if (state.mShiftMode == MANUAL_SHIFT) {
                 setSymbolsShiftedKeyboard();
             } else {
                 setSymbolsKeyboard();
@@ -166,18 +172,13 @@
         if (state.mIsAlphabetMode) {
             setShiftLocked(state.mIsAlphabetShiftLocked);
             if (!state.mIsAlphabetShiftLocked) {
-                setShifted(state.mIsShifted ? MANUAL_SHIFT : UNSHIFT);
+                setShifted(state.mShiftMode);
             }
         } else {
             mPrevMainKeyboardWasShiftLocked = state.mIsAlphabetShiftLocked;
         }
     }
 
-    private static final int UNSHIFT = 0;
-    private static final int MANUAL_SHIFT = 1;
-    private static final int AUTOMATIC_SHIFT = 2;
-    private static final int SHIFT_LOCK_SHIFTED = 3;
-
     private void setShifted(final int shiftMode) {
         if (DEBUG_ACTION) {
             Log.d(TAG, "setShifted: shiftMode=" + shiftModeToString(shiftMode) + " " + this);
@@ -525,6 +526,9 @@
             } else if (mAlphabetShiftState.isShiftLockShifted() && withSliding) {
                 // In shift locked state, shift has been pressed and slid out to other key.
                 setShiftLocked(true);
+            } else if (mAlphabetShiftState.isManualShifted() && withSliding) {
+                // Shift has been pressed and slid out to other key.
+                mSwitchState = SWITCH_STATE_MOMENTARY_ALPHA_SHIFT;
             } else if (isShiftLocked && !mAlphabetShiftState.isShiftLockShifted()
                     && (mShiftKeyState.isPressing() || mShiftKeyState.isPressingOnShifted())
                     && !withSliding) {
@@ -554,17 +558,21 @@
         mShiftKeyState.onRelease();
     }
 
-    public void onCancelInput(final boolean isSinglePointer) {
+    public void onFinishSlidingInput() {
         if (DEBUG_EVENT) {
-            Log.d(TAG, "onCancelInput: single=" + isSinglePointer + " " + this);
+            Log.d(TAG, "onFinishSlidingInput: " + this);
         }
         // Switch back to the previous keyboard mode if the user cancels sliding input.
-        if (isSinglePointer) {
-            if (mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL) {
-                toggleAlphabetAndSymbols();
-            } else if (mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE) {
-                toggleShiftInSymbols();
-            }
+        switch (mSwitchState) {
+        case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL:
+            toggleAlphabetAndSymbols();
+            break;
+        case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE:
+            toggleShiftInSymbols();
+            break;
+        case SWITCH_STATE_MOMENTARY_ALPHA_SHIFT:
+            setAlphabetKeyboard();
+            break;
         }
     }
 
@@ -577,10 +585,9 @@
         return c == Constants.CODE_SPACE || c == Constants.CODE_ENTER;
     }
 
-    public void onCodeInput(final int code, final boolean isSinglePointer, final int autoCaps) {
+    public void onCodeInput(final int code, final int autoCaps) {
         if (DEBUG_EVENT) {
             Log.d(TAG, "onCodeInput: code=" + Constants.printableCode(code)
-                    + " single=" + isSinglePointer
                     + " autoCaps=" + autoCaps + " " + this);
         }
 
@@ -593,23 +600,12 @@
                 } else {
                     mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
                 }
-            } else if (isSinglePointer) {
-                // Switch back to the previous keyboard mode if the user pressed the mode change key
-                // and slid to other key, then released the finger.
-                // If the user cancels the sliding input, switching back to the previous keyboard
-                // mode is handled by {@link #onCancelInput}.
-                toggleAlphabetAndSymbols();
             }
             break;
         case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE:
             if (code == Constants.CODE_SHIFT) {
                 // Detected only the shift key has been pressed on symbol layout, and then released.
                 mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
-            } else if (isSinglePointer) {
-                // Switch back to the previous keyboard mode if the user pressed the shift key on
-                // symbol mode and slid to other key, then released the finger.
-                toggleShiftInSymbols();
-                mSwitchState = SWITCH_STATE_SYMBOL;
             }
             break;
         case SWITCH_STATE_SYMBOL_BEGIN:
@@ -634,7 +630,7 @@
         }
     }
 
-    private static String shiftModeToString(final int shiftMode) {
+    static String shiftModeToString(final int shiftMode) {
         switch (shiftMode) {
         case UNSHIFT: return "UNSHIFT";
         case MANUAL_SHIFT: return "MANUAL";
@@ -650,6 +646,7 @@
         case SWITCH_STATE_SYMBOL: return "SYMBOL";
         case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL: return "MOMENTARY-ALPHA-SYMBOL";
         case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE: return "MOMENTARY-SYMBOL-MORE";
+        case SWITCH_STATE_MOMENTARY_ALPHA_SHIFT: return "MOMENTARY-ALPHA_SHIFT";
         default: return null;
         }
     }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
index 6bc6acc..31ef3cd 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
@@ -30,7 +30,7 @@
         public boolean isModifier();
         public boolean isInSlidingKeyInput();
         public void onPhantomUpEvent(long eventTime);
-        public void cancelTracking();
+        public void cancelTrackingForAction();
     }
 
     private static final int INITIAL_CAPACITY = 10;
@@ -48,6 +48,9 @@
 
     public void add(final Element pointer) {
         synchronized (mExpandableArrayOfActivePointers) {
+            if (DEBUG) {
+                Log.d(TAG, "add: " + pointer + " " + this);
+            }
             final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
             final int arraySize = mArraySize;
             if (arraySize < expandableArray.size()) {
@@ -61,24 +64,27 @@
 
     public void remove(final Element pointer) {
         synchronized (mExpandableArrayOfActivePointers) {
+            if (DEBUG) {
+                Log.d(TAG, "remove: " + pointer + " " + this);
+            }
             final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
             final int arraySize = mArraySize;
-            int newSize = 0;
+            int newIndex = 0;
             for (int index = 0; index < arraySize; index++) {
                 final Element element = expandableArray.get(index);
                 if (element == pointer) {
-                    if (newSize != index) {
+                    if (newIndex != index) {
                         Log.w(TAG, "Found duplicated element in remove: " + pointer);
                     }
                     continue; // Remove this element from the expandableArray.
                 }
-                if (newSize != index) {
+                if (newIndex != index) {
                     // Shift this element toward the beginning of the expandableArray.
-                    expandableArray.set(newSize, element);
+                    expandableArray.set(newIndex, element);
                 }
-                newSize++;
+                newIndex++;
             }
-            mArraySize = newSize;
+            mArraySize = newIndex;
         }
     }
 
@@ -95,8 +101,8 @@
             }
             final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
             final int arraySize = mArraySize;
-            int newSize, index;
-            for (newSize = index = 0; index < arraySize; index++) {
+            int newIndex, index;
+            for (newIndex = index = 0; index < arraySize; index++) {
                 final Element element = expandableArray.get(index);
                 if (element == pointer) {
                     break; // Stop releasing elements.
@@ -105,29 +111,30 @@
                     element.onPhantomUpEvent(eventTime);
                     continue; // Remove this element from the expandableArray.
                 }
-                if (newSize != index) {
+                if (newIndex != index) {
                     // Shift this element toward the beginning of the expandableArray.
-                    expandableArray.set(newSize, element);
+                    expandableArray.set(newIndex, element);
                 }
-                newSize++;
+                newIndex++;
             }
             // Shift rest of the expandableArray.
             int count = 0;
             for (; index < arraySize; index++) {
                 final Element element = expandableArray.get(index);
                 if (element == pointer) {
-                    if (count > 0) {
+                    count++;
+                    if (count > 1) {
                         Log.w(TAG, "Found duplicated element in releaseAllPointersOlderThan: "
                                 + pointer);
                     }
-                    count++;
                 }
-                if (newSize != index) {
-                    expandableArray.set(newSize, expandableArray.get(index));
-                    newSize++;
+                if (newIndex != index) {
+                    // Shift this element toward the beginning of the expandableArray.
+                    expandableArray.set(newIndex, expandableArray.get(index));
                 }
+                newIndex++;
             }
-            mArraySize = newSize;
+            mArraySize = newIndex;
         }
     }
 
@@ -146,26 +153,26 @@
             }
             final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
             final int arraySize = mArraySize;
-            int newSize = 0, count = 0;
+            int newIndex = 0, count = 0;
             for (int index = 0; index < arraySize; index++) {
                 final Element element = expandableArray.get(index);
                 if (element == pointer) {
-                    if (count > 0) {
+                    count++;
+                    if (count > 1) {
                         Log.w(TAG, "Found duplicated element in releaseAllPointersExcept: "
                                 + pointer);
                     }
-                    count++;
                 } else {
                     element.onPhantomUpEvent(eventTime);
                     continue; // Remove this element from the expandableArray.
                 }
-                if (newSize != index) {
+                if (newIndex != index) {
                     // Shift this element toward the beginning of the expandableArray.
-                    expandableArray.set(newSize, element);
+                    expandableArray.set(newIndex, element);
                 }
-                newSize++;
+                newIndex++;
             }
-            mArraySize = newSize;
+            mArraySize = newIndex;
         }
     }
 
@@ -202,11 +209,14 @@
 
     public void cancelAllPointerTracker() {
         synchronized (mExpandableArrayOfActivePointers) {
+            if (DEBUG) {
+                Log.d(TAG, "cancelAllPointerTracker: " + this);
+            }
             final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
             final int arraySize = mArraySize;
             for (int index = 0; index < arraySize; index++) {
                 final Element element = expandableArray.get(index);
-                element.cancelTracking();
+                element.cancelTrackingForAction();
             }
         }
     }
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index dbc2b90..4fc1919 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -107,13 +107,16 @@
 
     @Override
     public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
-            final String prevWord, final ProximityInfo proximityInfo) {
-        return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, 0);
+            final String prevWord, final ProximityInfo proximityInfo,
+            final boolean blockOffensiveWords) {
+        return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, blockOffensiveWords,
+                0 /* sessionId */);
     }
 
     @Override
     public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer,
-            final String prevWord, final ProximityInfo proximityInfo, int sessionId) {
+            final String prevWord, final ProximityInfo proximityInfo,
+            final boolean blockOffensiveWords, final int sessionId) {
         if (!isValidDictionary()) return null;
 
         Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE);
@@ -147,10 +150,21 @@
                 ++len;
             }
             if (len > 0) {
-                final int score = SuggestedWordInfo.KIND_WHITELIST == mOutputTypes[j]
+                final int flags = mOutputTypes[j] & SuggestedWordInfo.KIND_MASK_FLAGS;
+                if (blockOffensiveWords
+                        && 0 != (flags & SuggestedWordInfo.KIND_FLAG_POSSIBLY_OFFENSIVE)
+                        && 0 == (flags & SuggestedWordInfo.KIND_FLAG_EXACT_MATCH)) {
+                    // If we block potentially offensive words, and if the word is possibly
+                    // offensive, then we don't output it unless it's also an exact match.
+                    continue;
+                }
+                final int kind = mOutputTypes[j] & SuggestedWordInfo.KIND_MASK_KIND;
+                final int score = SuggestedWordInfo.KIND_WHITELIST == kind
                         ? SuggestedWordInfo.MAX_SCORE : mOutputScores[j];
+                // TODO: check that all users of the `kind' parameter are ready to accept
+                // flags too and pass mOutputTypes[j] instead of kind
                 suggestions.add(new SuggestedWordInfo(new String(mOutputCodePoints, start, len),
-                        score, mOutputTypes[j], mDictType));
+                        score, kind, mDictType));
             }
         }
         return suggestions;
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index 42f7136..a9b58de 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -81,6 +81,7 @@
     private static final String QUERY_PATH_METADATA = "metadata";
     private static final String INSERT_METADATA_CLIENT_ID_COLUMN = "clientid";
     private static final String INSERT_METADATA_METADATA_URI_COLUMN = "uri";
+    private static final String INSERT_METADATA_METADATA_ADDITIONAL_ID_COLUMN = "additionalid";
 
     // Prevents this class to be accidentally instantiated.
     private BinaryDictionaryFileDumper() {
@@ -209,7 +210,7 @@
      * to the cache file name designated by its id and locale, overwriting it if already present
      * and creating it (and its containing directory) if necessary.
      */
-    private static AssetFileAddress cacheWordList(final String wordlistId, final String locale,
+    private static void cacheWordList(final String wordlistId, final String locale,
             final ContentProviderClient providerClient, final Context context) {
         final int COMPRESSED_CRYPTED_COMPRESSED = 0;
         final int CRYPTED_COMPRESSED = 1;
@@ -227,7 +228,7 @@
                     providerClient, QUERY_PATH_DATAFILE, wordlistId /* extraPath */);
         } catch (RemoteException e) {
             Log.e(TAG, "Can't communicate with the dictionary pack", e);
-            return null;
+            return;
         }
         final String finalFileName =
                 DictionaryInfoUtils.getCacheFileName(wordlistId, locale, context);
@@ -236,11 +237,11 @@
             tempFileName = BinaryDictionaryGetter.getTempFileName(wordlistId, context);
         } catch (IOException e) {
             Log.e(TAG, "Can't open the temporary file", e);
-            return null;
+            return;
         }
 
         for (int mode = MODE_MIN; mode <= MODE_MAX; ++mode) {
-            InputStream originalSourceStream = null;
+            final InputStream originalSourceStream;
             InputStream inputStream = null;
             InputStream uncompressedStream = null;
             InputStream decryptedStream = null;
@@ -253,7 +254,7 @@
                 // Open input.
                 afd = openAssetFileDescriptor(providerClient, wordListUri);
                 // If we can't open it at all, don't even try a number of times.
-                if (null == afd) return null;
+                if (null == afd) return;
                 originalSourceStream = afd.createInputStream();
                 // Open output.
                 outputFile = new File(tempFileName);
@@ -304,7 +305,7 @@
                 }
                 BinaryDictionaryGetter.removeFilesWithIdExcept(context, wordlistId, finalFile);
                 // Success! Close files (through the finally{} clause) and return.
-                return AssetFileAddress.makeFromFileName(finalFileName);
+                return;
             } catch (Exception e) {
                 if (DEBUG) {
                     Log.i(TAG, "Can't open word list in mode " + mode, e);
@@ -319,7 +320,7 @@
             } finally {
                 // Ignore exceptions while closing files.
                 try {
-                    // inputStream.close() will close afd, we should not call afd.close().
+                    if (null != afd) afd.close();
                     if (null != inputStream) inputStream.close();
                     if (null != uncompressedStream) uncompressedStream.close();
                     if (null != decryptedStream) decryptedStream.close();
@@ -349,7 +350,6 @@
         } catch (RemoteException e) {
             Log.e(TAG, "In addition, communication with the dictionary provider was cut", e);
         }
-        return null;
     }
 
     /**
@@ -358,30 +358,23 @@
      * This will query a content provider for word list data for a given locale, and copy the
      * files locally so that they can be mmap'ed. This may overwrite previously cached word lists
      * with newer versions if a newer version is made available by the content provider.
-     * @returns the addresses of the word list files, or null if no data could be obtained.
      * @throw FileNotFoundException if the provider returns non-existent data.
      * @throw IOException if the provider-returned data could not be read.
      */
-    public static List<AssetFileAddress> cacheWordListsFromContentProvider(final Locale locale,
+    public static void cacheWordListsFromContentProvider(final Locale locale,
             final Context context, final boolean hasDefaultWordList) {
         final ContentProviderClient providerClient = context.getContentResolver().
                 acquireContentProviderClient(getProviderUriBuilder("").build());
         if (null == providerClient) {
             Log.e(TAG, "Can't establish communication with the dictionary provider");
-            return CollectionUtils.newArrayList();
+            return;
         }
         try {
             final List<WordListInfo> idList = getWordListWordListInfos(locale, context,
                     hasDefaultWordList);
-            final ArrayList<AssetFileAddress> fileAddressList = CollectionUtils.newArrayList();
             for (WordListInfo id : idList) {
-                final AssetFileAddress afd =
-                        cacheWordList(id.mId, id.mLocale, providerClient, context);
-                if (null != afd) {
-                    fileAddressList.add(afd);
-                }
+                cacheWordList(id.mId, id.mLocale, providerClient, context);
             }
-            return fileAddressList;
         } finally {
             providerClient.release();
         }
@@ -423,6 +416,7 @@
     private static void reinitializeClientRecordInDictionaryContentProvider(final Context context,
             final ContentProviderClient client, final String clientId) throws RemoteException {
         final String metadataFileUri = MetadataFileUriGetter.getMetadataUri(context);
+        final String metadataAdditionalId = MetadataFileUriGetter.getMetadataAdditionalId(context);
         if (TextUtils.isEmpty(metadataFileUri)) return;
         // Tell the content provider to reset all information about this client id
         final Uri metadataContentUri = getProviderUriBuilder(clientId)
@@ -434,6 +428,7 @@
         final ContentValues metadataValues = new ContentValues();
         metadataValues.put(INSERT_METADATA_CLIENT_ID_COLUMN, clientId);
         metadataValues.put(INSERT_METADATA_METADATA_URI_COLUMN, metadataFileUri);
+        metadataValues.put(INSERT_METADATA_METADATA_ADDITIONAL_ID_COLUMN, metadataAdditionalId);
         client.insert(metadataContentUri, metadataValues);
 
         // Update the dictionary list.
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 2943128..98eadca 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -72,10 +72,16 @@
     public static String getTempFileName(final String id, final Context context)
             throws IOException {
         final String safeId = DictionaryInfoUtils.replaceFileNameDangerousCharacters(id);
+        final File directory = new File(DictionaryInfoUtils.getWordListTempDirectory(context));
+        if (!directory.exists()) {
+            if (!directory.mkdirs()) {
+                Log.e(TAG, "Could not create the temporary directory");
+            }
+        }
         // If the first argument is less than three chars, createTempFile throws a
         // RuntimeException. We don't really care about what name we get, so just
         // put a three-chars prefix makes us safe.
-        return File.createTempFile("xxx" + safeId, null).getAbsolutePath();
+        return File.createTempFile("xxx" + safeId, null, directory).getAbsolutePath();
     }
 
     /**
@@ -89,8 +95,16 @@
                     + fallbackResId);
             return null;
         }
-        return AssetFileAddress.makeFromFileNameAndOffset(
-                context.getApplicationInfo().sourceDir, afd.getStartOffset(), afd.getLength());
+        try {
+            return AssetFileAddress.makeFromFileNameAndOffset(
+                    context.getApplicationInfo().sourceDir, afd.getStartOffset(), afd.getLength());
+        } finally {
+            try {
+                afd.close();
+            } catch (IOException e) {
+                // Ignored
+            }
+        }
     }
 
     private static final class DictPackSettings {
@@ -276,9 +290,6 @@
             final Context context) {
 
         final boolean hasDefaultWordList = DictionaryFactory.isDictionaryAvailable(context, locale);
-        // cacheWordListsFromContentProvider returns the list of files it copied to local
-        // storage, but we don't really care about what was copied NOW: what we want is the
-        // list of everything we ever cached, so we ignore the return value.
         // TODO: The development-only-diagnostic version is not supported by the Dictionary Pack
         // Service yet
         if (!ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index 75c2cf2..b9db9a0 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -16,21 +16,26 @@
 
 package com.android.inputmethod.latin;
 
+import com.android.inputmethod.latin.personalization.AccountUtils;
+
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.database.Cursor;
+import android.net.Uri;
 import android.os.SystemClock;
 import android.provider.BaseColumns;
+import android.provider.ContactsContract;
 import android.provider.ContactsContract.Contacts;
 import android.text.TextUtils;
 import android.util.Log;
 
+import java.util.List;
 import java.util.Locale;
 
 public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
 
-    private static final String[] PROJECTION = {BaseColumns._ID, Contacts.DISPLAY_NAME,};
+    private static final String[] PROJECTION = {BaseColumns._ID, Contacts.DISPLAY_NAME};
     private static final String[] PROJECTION_ID_ONLY = {BaseColumns._ID};
 
     private static final String TAG = ContactsBinaryDictionary.class.getSimpleName();
@@ -102,9 +107,32 @@
 
     @Override
     public void loadDictionaryAsync() {
+        clearFusionDictionary();
+        loadDeviceAccountsEmailAddresses();
+        loadDictionaryAsyncForUri(ContactsContract.Profile.CONTENT_URI);
+        // TODO: Switch this URL to the newer ContactsContract too
+        loadDictionaryAsyncForUri(Contacts.CONTENT_URI);
+    }
+
+    private void loadDeviceAccountsEmailAddresses() {
+        final List<String> accountVocabulary =
+                AccountUtils.getDeviceAccountsEmailAddresses(mContext);
+        if (accountVocabulary == null || accountVocabulary.isEmpty()) {
+            return;
+        }
+        for (String word : accountVocabulary) {
+            if (DEBUG) {
+                Log.d(TAG, "loadAccountVocabulary: " + word);
+            }
+            super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS,
+                    false /* isNotAWord */);
+        }
+    }
+
+    private void loadDictionaryAsyncForUri(final Uri uri) {
         try {
             Cursor cursor = mContext.getContentResolver()
-                    .query(Contacts.CONTENT_URI, PROJECTION, null, null, null);
+                    .query(uri, PROJECTION, null, null, null);
             if (cursor != null) {
                 try {
                     if (cursor.moveToFirst()) {
@@ -129,7 +157,6 @@
     }
 
     private void addWords(final Cursor cursor) {
-        clearFusionDictionary();
         int count = 0;
         while (!cursor.isAfterLast() && count < MAX_CONTACT_COUNT) {
             String name = cursor.getString(INDEX_NAME);
@@ -173,6 +200,9 @@
                 // capitalization of i.
                 final int wordLen = StringUtils.codePointCount(word);
                 if (wordLen < MAX_WORD_LENGTH && wordLen > 1) {
+                    if (DEBUG) {
+                        Log.d(TAG, "addName " + name + ", " + word + ", " + prevWord);
+                    }
                     super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS,
                             false /* isNotAWord */);
                     if (!TextUtils.isEmpty(prevWord)) {
diff --git a/java/src/com/android/inputmethod/latin/DebugSettings.java b/java/src/com/android/inputmethod/latin/DebugSettings.java
index c2aade6..5969a63 100644
--- a/java/src/com/android/inputmethod/latin/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/DebugSettings.java
@@ -121,18 +121,8 @@
             return;
         }
         boolean isDebugMode = mDebugMode.isChecked();
-        String version = "";
-        try {
-            final Context context = getActivity();
-            if (context == null) {
-                return;
-            }
-            final String packageName = context.getPackageName();
-            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
-            version = "Version " + info.versionName;
-        } catch (NameNotFoundException e) {
-            Log.e(TAG, "Could not find version info.");
-        }
+        final String version = getResources().getString(
+                R.string.version_text, Utils.getVersionName(getActivity()));
         if (!isDebugMode) {
             mDebugMode.setTitle(version);
             mDebugMode.setSummary("");
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index 9691fa2..acd7c2a 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -51,18 +51,21 @@
      * @param composer the key sequence to match with coordinate info, as a WordComposer
      * @param prevWord the previous word, or null if none
      * @param proximityInfo the object for key proximity. May be ignored by some implementations.
+     * @param blockOffensiveWords whether to block potentially offensive words
      * @return the list of suggestions (possibly null if none)
      */
     // TODO: pass more context than just the previous word, to enable better suggestions (n-gram
     // and more)
     abstract public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
-            final String prevWord, final ProximityInfo proximityInfo);
+            final String prevWord, final ProximityInfo proximityInfo,
+            final boolean blockOffensiveWords);
 
     // The default implementation of this method ignores sessionId.
     // Subclasses that want to use sessionId need to override this method.
     public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer,
-            final String prevWord, final ProximityInfo proximityInfo, final int sessionId) {
-        return getSuggestions(composer, prevWord, proximityInfo);
+            final String prevWord, final ProximityInfo proximityInfo,
+            final boolean blockOffensiveWords, final int sessionId) {
+        return getSuggestions(composer, prevWord, proximityInfo, blockOffensiveWords);
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
index 2832ad4..ed2b442 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -56,18 +56,19 @@
 
     @Override
     public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
-            final String prevWord, final ProximityInfo proximityInfo) {
+            final String prevWord, final ProximityInfo proximityInfo,
+            final boolean blockOffensiveWords) {
         final CopyOnWriteArrayList<Dictionary> dictionaries = mDictionaries;
         if (dictionaries.isEmpty()) return null;
         // To avoid creating unnecessary objects, we get the list out of the first
         // dictionary and add the rest to it if not null, hence the get(0)
         ArrayList<SuggestedWordInfo> suggestions = dictionaries.get(0).getSuggestions(composer,
-                prevWord, proximityInfo);
+                prevWord, proximityInfo, blockOffensiveWords);
         if (null == suggestions) suggestions = CollectionUtils.newArrayList();
         final int length = dictionaries.size();
         for (int i = 1; i < length; ++ i) {
             final ArrayList<SuggestedWordInfo> sugg = dictionaries.get(i).getSuggestions(composer,
-                    prevWord, proximityInfo);
+                    prevWord, proximityInfo, blockOffensiveWords);
             if (null != sugg) suggestions.addAll(sugg);
         }
         return suggestions;
diff --git a/java/src/com/android/inputmethod/latin/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/DictionaryInfoUtils.java
index dcfa483..df7bad8 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryInfoUtils.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryInfoUtils.java
@@ -129,6 +129,13 @@
     }
 
     /**
+     * Helper method to get the top level temp directory.
+     */
+    public static String getWordListTempDirectory(final Context context) {
+        return context.getFilesDir() + File.separator + "tmp";
+    }
+
+    /**
      * Reverse escaping done by replaceFileNameDangerousCharacters.
      */
     public static String getWordListIdFromFileName(final String fname) {
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 4b1975a..887d657 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -200,12 +200,14 @@
 
     @Override
     public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
-            final String prevWord, final ProximityInfo proximityInfo) {
+            final String prevWord, final ProximityInfo proximityInfo,
+            final boolean blockOffensiveWords) {
         asyncReloadDictionaryIfRequired();
         if (mLocalDictionaryController.tryLock()) {
             try {
                 if (mBinaryDictionary != null) {
-                    return mBinaryDictionary.getSuggestions(composer, prevWord, proximityInfo);
+                    return mBinaryDictionary.getSuggestions(composer, prevWord, proximityInfo,
+                            blockOffensiveWords);
                 }
             } finally {
                 mLocalDictionaryController.unlock();
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index fd81d13..0dabdb8 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -253,7 +253,8 @@
 
     @Override
     public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
-            final String prevWord, final ProximityInfo proximityInfo) {
+            final String prevWord, final ProximityInfo proximityInfo,
+            final boolean blockOffensiveWords) {
         if (reloadDictionaryIfRequired()) return null;
         if (composer.size() > 1) {
             if (composer.size() >= Constants.Dictionary.MAX_WORD_LENGTH) {
diff --git a/java/src/com/android/inputmethod/latin/FeedbackUtils.java b/java/src/com/android/inputmethod/latin/FeedbackUtils.java
index 1e5260e..0582763 100644
--- a/java/src/com/android/inputmethod/latin/FeedbackUtils.java
+++ b/java/src/com/android/inputmethod/latin/FeedbackUtils.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.latin;
 
 import android.content.Context;
+import android.content.Intent;
 
 public class FeedbackUtils {
     public static boolean isFeedbackFormSupported() {
@@ -25,4 +26,12 @@
 
     public static void showFeedbackForm(Context context) {
     }
+
+    public static int getAboutKeyboardTitleResId() {
+        return 0;
+    }
+
+    public static Intent getAboutKeyboardIntent(Context context) {
+        return null;
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/JniUtils.java b/java/src/com/android/inputmethod/latin/JniUtils.java
index f930599..8aedee5 100644
--- a/java/src/com/android/inputmethod/latin/JniUtils.java
+++ b/java/src/com/android/inputmethod/latin/JniUtils.java
@@ -23,15 +23,19 @@
 public final class JniUtils {
     private static final String TAG = JniUtils.class.getSimpleName();
 
-    private JniUtils() {
-        // This utility class is not publicly instantiable.
-    }
-
-    public static void loadNativeLibrary() {
+    static {
         try {
             System.loadLibrary(JniLibName.JNI_LIB_NAME);
         } catch (UnsatisfiedLinkError ule) {
             Log.e(TAG, "Could not load native library " + JniLibName.JNI_LIB_NAME, ule);
         }
     }
+
+    private JniUtils() {
+        // This utility class is not publicly instantiable.
+    }
+
+    public static void loadNativeLibrary() {
+        // Ensures the static initializer is called
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 5ef2120..347a4c6 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -90,7 +90,7 @@
 /**
  * Input method implementation for Qwerty'ish keyboard.
  */
-public final class LatinIME extends InputMethodService implements KeyboardActionListener,
+public class LatinIME extends InputMethodService implements KeyboardActionListener,
         SuggestionStripView.Listener, TargetApplicationGetter.OnTargetApplicationKnownListener,
         Suggest.SuggestInitializationListener {
     private static final String TAG = LatinIME.class.getSimpleName();
@@ -188,6 +188,8 @@
     // Keeps track of most recently inserted text (multi-character key) for reverting
     private String mEnteredText;
 
+    // TODO: This boolean is persistent state and causes large side effects at unexpected times.
+    // Find a way to remove it for readability.
     private boolean mIsAutoCorrectionIndicatorOn;
 
     private AlertDialog mOptionsDialog;
@@ -250,6 +252,7 @@
         }
 
         public void postResumeSuggestions() {
+            removeMessages(MSG_RESUME_SUGGESTIONS);
             sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS), mDelayUpdateSuggestions);
         }
 
@@ -759,7 +762,8 @@
         }
         mSuggestedWords = SuggestedWords.EMPTY;
 
-        mConnection.resetCachesUponCursorMove(editorInfo.initialSelStart);
+        mConnection.resetCachesUponCursorMove(editorInfo.initialSelStart,
+                false /* shouldFinishComposition */);
 
         if (isDifferentTextField) {
             mainKeyboardView.closing();
@@ -900,7 +904,12 @@
         // we know for sure the cursor moved while we were composing and we should reset
         // the state.
         final boolean noComposingSpan = composingSpanStart == -1 && composingSpanEnd == -1;
-        if (!mExpectingUpdateSelection
+        // If the keyboard is not visible, we don't need to do all the housekeeping work, as it
+        // will be reset when the keyboard shows up anyway.
+        // TODO: revisit this when LatinIME supports hardware keyboards.
+        // NOTE: the test harness subclasses LatinIME and overrides isInputViewShown().
+        // TODO: find a better way to simulate actual execution.
+        if (isInputViewShown() && !mExpectingUpdateSelection
                 && !mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart)) {
             // TAKE CARE: there is a race condition when we enter this test even when the user
             // did not explicitly move the cursor. This happens when typing fast, where two keys
@@ -1148,13 +1157,14 @@
     // This will reset the whole input state to the starting state. It will clear
     // the composing word, reset the last composed word, tell the inputconnection about it.
     private void resetEntireInputState(final int newCursorPosition) {
+        final boolean shouldFinishComposition = mWordComposer.isComposingWord();
         resetComposingState(true /* alsoResetLastComposedWord */);
         if (mSettings.getCurrent().mBigramPredictionEnabled) {
             clearSuggestionStrip();
         } else {
             setSuggestedWords(mSettings.getCurrent().mSuggestPuncList, false);
         }
-        mConnection.resetCachesUponCursorMove(newCursorPosition);
+        mConnection.resetCachesUponCursorMove(newCursorPosition, shouldFinishComposition);
     }
 
     private void resetComposingState(final boolean alsoResetLastComposedWord) {
@@ -1577,21 +1587,11 @@
                 commitTyped(LastComposedWord.NOT_A_SEPARATOR);
             }
             mExpectingUpdateSelection = true;
-            // The following is necessary for the case where the user typed something but didn't
-            // manual pick it and didn't input any separator: we want to put a space between what
-            // has been entered and the coming gesture input result, so we go into phantom space
-            // state, which will be promoted to a space when the gesture result is committed. But if
-            // the current input ends in a word connector on the other hand, then we want to have
-            // the next input stick to the current input so we don't switch to phantom space state.
-            if (!mSettings.getCurrent().isWordConnector(lastChar)) {
-                mSpaceState = SPACE_STATE_PHANTOM;
-            }
-        } else {
-            final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor();
-            if (Character.isLetter(codePointBeforeCursor)
-                    || mSettings.getCurrent().isUsuallyFollowedBySpace(codePointBeforeCursor)) {
-                mSpaceState = SPACE_STATE_PHANTOM;
-            }
+        }
+        final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor();
+        if (Character.isLetterOrDigit(codePointBeforeCursor)
+                || mSettings.getCurrent().isUsuallyFollowedBySpace(codePointBeforeCursor)) {
+            mSpaceState = SPACE_STATE_PHANTOM;
         }
         mConnection.endBatchEdit();
         mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
@@ -1759,9 +1759,16 @@
 
     // Called from PointerTracker through the KeyboardActionListener interface
     @Override
+    public void onFinishSlidingInput() {
+        // User finished sliding input.
+        mKeyboardSwitcher.onFinishSlidingInput();
+    }
+
+    // Called from PointerTracker through the KeyboardActionListener interface
+    @Override
     public void onCancelInput() {
         // User released a finger outside any key
-        mKeyboardSwitcher.onCancelInput();
+        // Nothing to do so far.
     }
 
     @Override
@@ -1905,6 +1912,8 @@
             final int y, final int spaceState) {
         boolean isComposingWord = mWordComposer.isComposingWord();
 
+        // TODO: remove isWordConnector() and use isUsuallyFollowedBySpace() instead.
+        // See onStartBatchInput() to see how to do it.
         if (SPACE_STATE_PHANTOM == spaceState &&
                 !mSettings.getCurrent().isWordConnector(primaryCode)) {
             if (isComposingWord) {
@@ -2185,6 +2194,7 @@
                 mConnection.getNthPreviousWord(mSettings.getCurrent().mWordSeparators,
                 mWordComposer.isComposingWord() ? 2 : 1);
         return mSuggest.getSuggestedWords(mWordComposer, prevWord, keyboard.getProximityInfo(),
+                mSettings.getBlockPotentiallyOffensive(),
                 mSettings.getCurrent().mCorrectionEnabled, sessionId);
     }
 
@@ -2443,10 +2453,15 @@
     private void restartSuggestionsOnWordTouchedByCursor() {
         // If the cursor is not touching a word, or if there is a selection, return right away.
         if (mLastSelectionStart != mLastSelectionEnd) return;
+        // If we don't know the cursor location, return.
+        if (mLastSelectionStart < 0) return;
         if (!mConnection.isCursorTouchingWord(mSettings.getCurrent())) return;
         final Range range = mConnection.getWordRangeAtCursor(mSettings.getWordSeparators(),
                 0 /* additionalPrecedingWordsCount */);
         if (null == range) return; // Happens if we don't have an input connection at all
+        // If for some strange reason (editor bug or so) we measure the text before the cursor as
+        // longer than what the entire text is supposed to be, the safe thing to do is bail out.
+        if (range.mCharsBefore > mLastSelectionStart) return;
         final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
         final String typedWord = range.mWord.toString();
         if (range.mWord instanceof SpannableString) {
@@ -2499,7 +2514,10 @@
 
         // Note that it's very important here that suggestedWords.mWillAutoCorrect is false.
         // We never want to auto-correct on a resumed suggestion. Please refer to the three
-        // places above where suggestedWords is affected.
+        // places above where suggestedWords is affected. We also need to reset
+        // mIsAutoCorrectionIndicatorOn to avoid showSuggestionStrip touching the text to adapt it.
+        // TODO: remove mIsAutoCorrectionIndicator on (see comment on definition)
+        mIsAutoCorrectionIndicatorOn = false;
         showSuggestionStrip(suggestedWords, typedWord);
     }
 
@@ -2611,8 +2629,8 @@
     // Callback called by PointerTracker through the KeyboardActionListener. This is called when a
     // key is depressed; release matching call is onReleaseKey below.
     @Override
-    public void onPressKey(final int primaryCode) {
-        mKeyboardSwitcher.onPressKey(primaryCode);
+    public void onPressKey(final int primaryCode, final boolean isSinglePointer) {
+        mKeyboardSwitcher.onPressKey(primaryCode, isSinglePointer);
     }
 
     // Callback by PointerTracker through the KeyboardActionListener. This is called when a key
diff --git a/java/src/com/android/inputmethod/latin/MetadataFileUriGetter.java b/java/src/com/android/inputmethod/latin/MetadataFileUriGetter.java
index e6dc6db..a98ecc7 100644
--- a/java/src/com/android/inputmethod/latin/MetadataFileUriGetter.java
+++ b/java/src/com/android/inputmethod/latin/MetadataFileUriGetter.java
@@ -19,10 +19,18 @@
 import android.content.Context;
 
 /**
- * Helper class to get the metadata URI.
+ * Helper class to get the metadata URI and the additional ID.
  */
 public class MetadataFileUriGetter {
-    public static String getMetadataUri(Context context) {
+    private MetadataFileUriGetter() {
+        // This helper class is not instantiable.
+    }
+
+    public static String getMetadataUri(final Context context) {
         return context.getString(R.string.dictionary_pack_metadata_uri);
     }
+
+    public static String getMetadataAdditionalId(final Context context) {
+        return "";
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/ResourceUtils.java b/java/src/com/android/inputmethod/latin/ResourceUtils.java
index b74b979..a9fba53 100644
--- a/java/src/com/android/inputmethod/latin/ResourceUtils.java
+++ b/java/src/com/android/inputmethod/latin/ResourceUtils.java
@@ -19,14 +19,17 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.os.Build;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.TypedValue;
 
+import com.android.inputmethod.annotations.UsedForTesting;
+
+import java.util.ArrayList;
 import java.util.HashMap;
 
 public final class ResourceUtils {
     private static final String TAG = ResourceUtils.class.getSimpleName();
-    private static final boolean DEBUG = false;
 
     public static final float UNDEFINED_RATIO = -1.0f;
     public static final int UNDEFINED_DIMENSION = -1;
@@ -35,11 +38,32 @@
         // This utility class is not publicly instantiable.
     }
 
-    private static final String DEFAULT_PREFIX = "DEFAULT,";
-    private static final String HARDWARE_PREFIX = Build.HARDWARE + ",";
     private static final HashMap<String, String> sDeviceOverrideValueMap =
             CollectionUtils.newHashMap();
 
+    private static final String[] BUILD_KEYS_AND_VALUES = {
+        "HARDWARE", Build.HARDWARE,
+        "MODEL", Build.MODEL,
+        "BRAND", Build.BRAND,
+        "MANUFACTURER", Build.MANUFACTURER
+    };
+    private static final HashMap<String, String> sBuildKeyValues;
+    private static final String sBuildKeyValuesDebugString;
+
+    static {
+        sBuildKeyValues = CollectionUtils.newHashMap();
+        final ArrayList<String> keyValuePairs = CollectionUtils.newArrayList();
+        final int keyCount = BUILD_KEYS_AND_VALUES.length / 2;
+        for (int i = 0; i < keyCount; i++) {
+            final int index = i * 2;
+            final String key = BUILD_KEYS_AND_VALUES[index];
+            final String value = BUILD_KEYS_AND_VALUES[index + 1];
+            sBuildKeyValues.put(key, value);
+            keyValuePairs.add(key + '=' + value);
+        }
+        sBuildKeyValuesDebugString = "[" + TextUtils.join(" ", keyValuePairs) + "]";
+    }
+
     public static String getDeviceOverrideValue(final Resources res, final int overrideResId) {
         final int orientation = res.getConfiguration().orientation;
         final String key = overrideResId + "-" + orientation;
@@ -48,33 +72,114 @@
         }
 
         final String[] overrideArray = res.getStringArray(overrideResId);
-        final String overrideValue = StringUtils.findPrefixedString(HARDWARE_PREFIX, overrideArray);
+        final String overrideValue = findConstantForKeyValuePairs(sBuildKeyValues, overrideArray);
         // The overrideValue might be an empty string.
         if (overrideValue != null) {
-            if (DEBUG) {
-                Log.d(TAG, "Find override value:"
-                        + " resource="+ res.getResourceEntryName(overrideResId)
-                        + " Build.HARDWARE=" + Build.HARDWARE + " override=" + overrideValue);
-            }
+            Log.i(TAG, "Find override value:"
+                    + " resource="+ res.getResourceEntryName(overrideResId)
+                    + " build=" + sBuildKeyValuesDebugString
+                    + " override=" + overrideValue);
             sDeviceOverrideValueMap.put(key, overrideValue);
             return overrideValue;
         }
 
-        final String defaultValue = StringUtils.findPrefixedString(DEFAULT_PREFIX, overrideArray);
+        final String defaultValue = findDefaultConstant(overrideArray);
         // The defaultValue might be an empty string.
         if (defaultValue == null) {
             Log.w(TAG, "Couldn't find override value nor default value:"
                     + " resource="+ res.getResourceEntryName(overrideResId)
-                    + " Build.HARDWARE=" + Build.HARDWARE);
-        } else if (DEBUG) {
-            Log.d(TAG, "Found default value:"
-                + " resource="+ res.getResourceEntryName(overrideResId)
-                + " Build.HARDWARE=" + Build.HARDWARE + " default=" + defaultValue);
+                    + " build=" + sBuildKeyValuesDebugString);
+        } else {
+            Log.i(TAG, "Found default value:"
+                    + " resource="+ res.getResourceEntryName(overrideResId)
+                    + " build=" + sBuildKeyValuesDebugString
+                    + " default=" + defaultValue);
         }
         sDeviceOverrideValueMap.put(key, defaultValue);
         return defaultValue;
     }
 
+    /**
+     * Find the condition that fulfills specified key value pairs from an array of
+     * "condition,constant", and return the corresponding string constant. A condition is
+     * "pattern1[:pattern2...] (or an empty string for the default). A pattern is
+     * "key=regexp_value" string. The condition matches only if all patterns of the condition
+     * are true for the specified key value pairs.
+     *
+     * For example, "condition,constant" has the following format.
+     * (See {@link ResourceUtilsTests#testFindConstantForKeyValuePairsRegexp()})
+     *  - HARDWARE=mako,constantForNexus4
+     *  - MODEL=Nexus 4:MANUFACTURER=LGE,constantForNexus4
+     *  - ,defaultConstant
+     *
+     * @param keyValuePairs attributes to be used to look for a matched condition.
+     * @param conditionConstantArray an array of "condition,constant" elements to be searched.
+     * @return the constant part of the matched "condition,constant" element. Returns null if no
+     * condition matches.
+     */
+    @UsedForTesting
+    static String findConstantForKeyValuePairs(final HashMap<String, String> keyValuePairs,
+            final String[] conditionConstantArray) {
+        if (conditionConstantArray == null || keyValuePairs == null) {
+            return null;
+        }
+        for (final String conditionConstant : conditionConstantArray) {
+            final int posComma = conditionConstant.indexOf(',');
+            if (posComma < 0) {
+                throw new RuntimeException("Array element has no comma: " + conditionConstant);
+            }
+            final String condition = conditionConstant.substring(0, posComma);
+            if (condition.isEmpty()) {
+                // Default condition. The default condition should be searched by
+                // {@link #findConstantForDefault(String[])}.
+                continue;
+            }
+            if (fulfillsCondition(keyValuePairs, condition)) {
+                return conditionConstant.substring(posComma + 1);
+            }
+        }
+        return null;
+    }
+
+    private static boolean fulfillsCondition(final HashMap<String,String> keyValuePairs,
+            final String condition) {
+        final String[] patterns = condition.split(":");
+        // Check all patterns in a condition are true
+        for (final String pattern : patterns) {
+            final int posEqual = pattern.indexOf('=');
+            if (posEqual < 0) {
+                throw new RuntimeException("Pattern has no '=': " + condition);
+            }
+            final String key = pattern.substring(0, posEqual);
+            final String value = keyValuePairs.get(key);
+            if (value == null) {
+                throw new RuntimeException("Found unknown key: " + condition);
+            }
+            final String patternRegexpValue = pattern.substring(posEqual + 1);
+            if (!value.matches(patternRegexpValue)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @UsedForTesting
+    static String findDefaultConstant(final String[] conditionConstantArray) {
+        if (conditionConstantArray == null) {
+            return null;
+        }
+        for (final String condition : conditionConstantArray) {
+            final int posComma = condition.indexOf(',');
+            if (posComma < 0) {
+                throw new RuntimeException("Array element has no comma: " + condition);
+            }
+            if (posComma == 0) { // condition is empty.
+                return condition.substring(posComma + 1);
+            }
+        }
+        return null;
+    }
+
     public static boolean isValidFraction(final float fraction) {
         return fraction >= 0.0f;
     }
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 8ed7ab2..980215d 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -135,13 +135,14 @@
         if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
     }
 
-    public void resetCachesUponCursorMove(final int newCursorPosition) {
+    public void resetCachesUponCursorMove(final int newCursorPosition,
+            final boolean shouldFinishComposition) {
         mCurrentCursorPosition = newCursorPosition;
         mComposingText.setLength(0);
         mCommittedTextBeforeComposingText.setLength(0);
         final CharSequence textBeforeCursor = getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0);
         if (null != textBeforeCursor) mCommittedTextBeforeComposingText.append(textBeforeCursor);
-        if (null != mIC) {
+        if (null != mIC && shouldFinishComposition) {
             mIC.finishComposingText();
             if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
                 ResearchLogger.richInputConnection_finishComposingText();
diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
index e39aae9..94513e6 100644
--- a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
+++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
@@ -22,6 +22,7 @@
 import android.content.SharedPreferences;
 import android.os.IBinder;
 import android.preference.PreferenceManager;
+import android.util.Log;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
@@ -46,6 +47,8 @@
     private InputMethodManagerCompatWrapper mImmWrapper;
     private InputMethodInfo mInputMethodInfoOfThisIme;
 
+    private static final int INDEX_NOT_FOUND = -1;
+
     public static RichInputMethodManager getInstance() {
         sInstance.checkInitialized();
         return sInstance;
@@ -97,12 +100,107 @@
         throw new RuntimeException("Input method id for " + packageName + " not found.");
     }
 
+    public List<InputMethodSubtype> getMyEnabledInputMethodSubtypeList(
+            boolean allowsImplicitlySelectedSubtypes) {
+        return mImmWrapper.mImm.getEnabledInputMethodSubtypeList(
+                mInputMethodInfoOfThisIme, allowsImplicitlySelectedSubtypes);
+    }
+
     public boolean switchToNextInputMethod(final IBinder token, final boolean onlyCurrentIme) {
-        final boolean result = mImmWrapper.switchToNextInputMethod(token, onlyCurrentIme);
-        if (!result) {
-            mImmWrapper.mImm.switchToLastInputMethod(token);
+        if (mImmWrapper.switchToNextInputMethod(token, onlyCurrentIme)) {
+            return true;
+        }
+        // Was not able to call {@link InputMethodManager#switchToNextInputMethodIBinder,boolean)}
+        // because the current device is running ICS or previous and lacks the API.
+        if (switchToNextInputSubtypeInThisIme(token, onlyCurrentIme)) {
+            return true;
+        }
+        return switchToNextInputMethodAndSubtype(token);
+    }
+
+    private boolean switchToNextInputSubtypeInThisIme(final IBinder token,
+            final boolean onlyCurrentIme) {
+        final InputMethodManager imm = mImmWrapper.mImm;
+        final InputMethodSubtype currentSubtype = imm.getCurrentInputMethodSubtype();
+        final List<InputMethodSubtype> enabledSubtypes = getMyEnabledInputMethodSubtypeList(
+                true /* allowsImplicitlySelectedSubtypes */);
+        final int currentIndex = getSubtypeIndexInList(currentSubtype, enabledSubtypes);
+        if (currentIndex == INDEX_NOT_FOUND) {
+            Log.w(TAG, "Can't find current subtype in enabled subtypes: subtype="
+                    + SubtypeLocale.getSubtypeDisplayName(currentSubtype));
             return false;
         }
+        final int nextIndex = (currentIndex + 1) % enabledSubtypes.size();
+        if (nextIndex <= currentIndex && !onlyCurrentIme) {
+            // The current subtype is the last or only enabled one and it needs to switch to
+            // next IME.
+            return false;
+        }
+        final InputMethodSubtype nextSubtype = enabledSubtypes.get(nextIndex);
+        setInputMethodAndSubtype(token, nextSubtype);
+        return true;
+    }
+
+    private boolean switchToNextInputMethodAndSubtype(final IBinder token) {
+        final InputMethodManager imm = mImmWrapper.mImm;
+        final List<InputMethodInfo> enabledImis = imm.getEnabledInputMethodList();
+        final int currentIndex = getImiIndexInList(mInputMethodInfoOfThisIme, enabledImis);
+        if (currentIndex == INDEX_NOT_FOUND) {
+            Log.w(TAG, "Can't find current IME in enabled IMEs: IME package="
+                    + mInputMethodInfoOfThisIme.getPackageName());
+            return false;
+        }
+        final InputMethodInfo nextImi = getNextNonAuxiliaryIme(currentIndex, enabledImis);
+        final List<InputMethodSubtype> enabledSubtypes = imm.getEnabledInputMethodSubtypeList(
+                nextImi, true /* allowsImplicitlySelectedSubtypes */);
+        if (enabledSubtypes.isEmpty()) {
+            // The next IME has no subtype.
+            imm.setInputMethod(token, nextImi.getId());
+            return true;
+        }
+        final InputMethodSubtype firstSubtype = enabledSubtypes.get(0);
+        imm.setInputMethodAndSubtype(token, nextImi.getId(), firstSubtype);
+        return true;
+    }
+
+    private static int getImiIndexInList(final InputMethodInfo inputMethodInfo,
+            final List<InputMethodInfo> imiList) {
+        final int count = imiList.size();
+        for (int index = 0; index < count; index++) {
+            final InputMethodInfo imi = imiList.get(index);
+            if (imi.equals(inputMethodInfo)) {
+                return index;
+            }
+        }
+        return INDEX_NOT_FOUND;
+    }
+
+    // This method mimics {@link InputMethodManager#switchToNextInputMethod(IBinder,boolean)}.
+    private static InputMethodInfo getNextNonAuxiliaryIme(final int currentIndex,
+            final List<InputMethodInfo> imiList) {
+        final int count = imiList.size();
+        for (int i = 1; i < count; i++) {
+            final int nextIndex = (currentIndex + i) % count;
+            final InputMethodInfo nextImi = imiList.get(nextIndex);
+            if (!isAuxiliaryIme(nextImi)) {
+                return nextImi;
+            }
+        }
+        return imiList.get(currentIndex);
+    }
+
+    // Copied from {@link InputMethodInfo}. See how auxiliary of IME is determined.
+    private static boolean isAuxiliaryIme(final InputMethodInfo imi) {
+        final int count = imi.getSubtypeCount();
+        if (count == 0) {
+            return false;
+        }
+        for (int index = 0; index < count; index++) {
+            final InputMethodSubtype subtype = imi.getSubtypeAt(index);
+            if (!subtype.isAuxiliary()) {
+                return false;
+            }
+        }
         return true;
     }
 
@@ -122,8 +220,8 @@
             final InputMethodSubtype subtype) {
         final boolean subtypeEnabled = checkIfSubtypeBelongsToThisImeAndEnabled(subtype);
         final boolean subtypeExplicitlyEnabled = checkIfSubtypeBelongsToList(
-                subtype, mImmWrapper.mImm.getEnabledInputMethodSubtypeList(
-                        mInputMethodInfoOfThisIme, false /* allowsImplicitlySelectedSubtypes */));
+                subtype, getMyEnabledInputMethodSubtypeList(
+                        false /* allowsImplicitlySelectedSubtypes */));
         return subtypeEnabled && !subtypeExplicitlyEnabled;
     }
 
@@ -136,24 +234,35 @@
 
     private static boolean checkIfSubtypeBelongsToList(final InputMethodSubtype subtype,
             final List<InputMethodSubtype> subtypes) {
-        for (final InputMethodSubtype ims : subtypes) {
+        return getSubtypeIndexInList(subtype, subtypes) != INDEX_NOT_FOUND;
+    }
+
+    private static int getSubtypeIndexInList(final InputMethodSubtype subtype,
+            final List<InputMethodSubtype> subtypes) {
+        final int count = subtypes.size();
+        for (int index = 0; index < count; index++) {
+            final InputMethodSubtype ims = subtypes.get(index);
             if (ims.equals(subtype)) {
-                return true;
+                return index;
             }
         }
-        return false;
+        return INDEX_NOT_FOUND;
     }
 
     public boolean checkIfSubtypeBelongsToThisIme(final InputMethodSubtype subtype) {
-        final InputMethodInfo myImi = mInputMethodInfoOfThisIme;
-        final int count = myImi.getSubtypeCount();
-        for (int i = 0; i < count; i++) {
-            final InputMethodSubtype ims = myImi.getSubtypeAt(i);
+        return getSubtypeIndexInIme(subtype, mInputMethodInfoOfThisIme) != INDEX_NOT_FOUND;
+    }
+
+    private static int getSubtypeIndexInIme(final InputMethodSubtype subtype,
+            final InputMethodInfo imi) {
+        final int count = imi.getSubtypeCount();
+        for (int index = 0; index < count; index++) {
+            final InputMethodSubtype ims = imi.getSubtypeAt(index);
             if (ims.equals(subtype)) {
-                return true;
+                return index;
             }
         }
-        return false;
+        return INDEX_NOT_FOUND;
     }
 
     public InputMethodSubtype getCurrentInputMethodSubtype(
@@ -209,8 +318,7 @@
         if (filteredImisCount > 1) {
             return true;
         }
-        final List<InputMethodSubtype> subtypes =
-                mImmWrapper.mImm.getEnabledInputMethodSubtypeList(null, true);
+        final List<InputMethodSubtype> subtypes = getMyEnabledInputMethodSubtypeList(true);
         int keyboardCount = 0;
         // imm.getEnabledInputMethodSubtypeList(null, true) will return the current IME's
         // both explicitly and implicitly enabled input method subtype.
diff --git a/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java b/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java
index 9819a02..7c4156c 100644
--- a/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java
+++ b/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java
@@ -59,7 +59,7 @@
 
     public void setInterface(final ValueProxy proxy) {
         mValueProxy = proxy;
-        setSummary(getValueText(proxy.readValue(getKey())));
+        setSummary(getValueText(clipValue(proxy.readValue(getKey()))));
     }
 
     private String getValueText(final int value) {
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index 72e0870..9fefb58 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -36,6 +36,7 @@
     public static final String PREF_POPUP_ON = "popup_on";
     public static final String PREF_VOICE_MODE = "voice_mode";
     public static final String PREF_CORRECTION_SETTINGS = "correction_settings";
+    public static final String PREF_EDIT_PERSONAL_DICTIONARY = "edit_personal_dictionary";
     public static final String PREF_CONFIGURE_DICTIONARIES_KEY = "configure_dictionaries_key";
     public static final String PREF_AUTO_CORRECTION_THRESHOLD = "auto_correction_threshold";
     public static final String PREF_SHOW_SUGGESTIONS_SETTING = "show_suggestions_setting";
@@ -46,6 +47,8 @@
     public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict";
     public static final String PREF_KEY_USE_DOUBLE_SPACE_PERIOD =
             "pref_key_use_double_space_period";
+    public static final String PREF_BLOCK_POTENTIALLY_OFFENSIVE =
+            "pref_key_block_potentially_offensive";
     public static final String PREF_SHOW_LANGUAGE_SWITCH_KEY =
             "pref_show_language_switch_key";
     public static final String PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST =
@@ -78,6 +81,7 @@
             "pref_suppress_language_switch_key";
 
     public static final String PREF_SEND_FEEDBACK = "send_feedback";
+    public static final String PREF_ABOUT_KEYBOARD = "about_keyboard";
 
     private Resources mRes;
     private SharedPreferences mPrefs;
@@ -142,6 +146,10 @@
         return mCurrentLocale;
     }
 
+    public boolean getBlockPotentiallyOffensive() {
+        return mSettingsValues.mBlockPotentiallyOffensive;
+    }
+
     // Accessed from the settings interface, hence public
     public static boolean readKeypressSoundEnabled(final SharedPreferences prefs,
             final Resources res) {
@@ -163,6 +171,12 @@
         return !currentAutoCorrectionSetting.equals(autoCorrectionOff);
     }
 
+    public static boolean readBlockPotentiallyOffensive(final SharedPreferences prefs,
+            final Resources res) {
+        return prefs.getBoolean(Settings.PREF_BLOCK_POTENTIALLY_OFFENSIVE,
+                res.getBoolean(R.bool.config_block_potentially_offensive));
+    }
+
     public static boolean readFromBuildConfigIfGestureInputEnabled(final Resources res) {
         return res.getBoolean(R.bool.config_gesture_input_enabled_by_build_config);
     }
diff --git a/java/src/com/android/inputmethod/latin/SettingsFragment.java b/java/src/com/android/inputmethod/latin/SettingsFragment.java
index a96c997..835ef7b 100644
--- a/java/src/com/android/inputmethod/latin/SettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/SettingsFragment.java
@@ -16,10 +16,13 @@
 
 package com.android.inputmethod.latin;
 
+import android.app.Activity;
 import android.app.backup.BackupManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.media.AudioManager;
 import android.os.Bundle;
@@ -31,13 +34,19 @@
 import android.preference.PreferenceScreen;
 import android.view.inputmethod.InputMethodSubtype;
 
+import java.util.TreeSet;
+
 import com.android.inputmethod.dictionarypack.DictionarySettingsActivity;
 import com.android.inputmethod.latin.define.ProductionFlag;
 import com.android.inputmethod.latin.setup.LauncherIconVisibilityManager;
+import com.android.inputmethod.latin.userdictionary.UserDictionaryList;
+import com.android.inputmethod.latin.userdictionary.UserDictionarySettings;
 import com.android.inputmethodcommon.InputMethodSettingsFragment;
 
 public final class SettingsFragment extends InputMethodSettingsFragment
         implements SharedPreferences.OnSharedPreferenceChangeListener {
+    private static final boolean DBG_USE_INTERNAL_USER_DICTIONARY_SETTINGS = false;
+
     private ListPreference mVoicePreference;
     private ListPreference mShowCorrectionSuggestionsPreference;
     private ListPreference mAutoCorrectionThresholdPreference;
@@ -77,10 +86,13 @@
         final Resources res = getResources();
         final Context context = getActivity();
 
-        // When we are called from the Settings application but we are not already running, the
-        // {@link SubtypeLocale} class may not have been initialized. It is safe to call
-        // {@link SubtypeLocale#init(Context)} multiple times.
+        // When we are called from the Settings application but we are not already running, some
+        // singleton and utility classes may not have been initialized.  We have to call
+        // initialization method of these classes here. See {@link LatinIME#onCreate()}.
+        SubtypeSwitcher.init(context);
         SubtypeLocale.init(context);
+        AudioAndHapticFeedbackManager.init(context);
+
         mVoicePreference = (ListPreference) findPreference(Settings.PREF_VOICE_MODE);
         mShowCorrectionSuggestionsPreference =
                 (ListPreference) findPreference(Settings.PREF_SHOW_SUGGESTIONS_SETTING);
@@ -110,6 +122,7 @@
         }
 
         final Preference feedbackSettings = findPreference(Settings.PREF_SEND_FEEDBACK);
+        final Preference aboutSettings = findPreference(Settings.PREF_ABOUT_KEYBOARD);
         if (feedbackSettings != null) {
             if (FeedbackUtils.isFeedbackFormSupported()) {
                 feedbackSettings.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@@ -119,8 +132,11 @@
                         return true;
                     }
                 });
+                aboutSettings.setTitle(FeedbackUtils.getAboutKeyboardTitleResId());
+                aboutSettings.setIntent(FeedbackUtils.getAboutKeyboardIntent(getActivity()));
             } else {
                 miscSettings.removePreference(feedbackSettings);
+                miscSettings.removePreference(aboutSettings);
             }
         }
 
@@ -180,6 +196,15 @@
             textCorrectionGroup.removePreference(dictionaryLink);
         }
 
+        final Preference editPersonalDictionary =
+                findPreference(Settings.PREF_EDIT_PERSONAL_DICTIONARY);
+        final Intent editPersonalDictionaryIntent = editPersonalDictionary.getIntent();
+        final ResolveInfo ri = context.getPackageManager().resolveActivity(
+                editPersonalDictionaryIntent, PackageManager.MATCH_DEFAULT_ONLY);
+        if (DBG_USE_INTERNAL_USER_DICTIONARY_SETTINGS || ri == null) {
+            updateUserDictionaryPreference(editPersonalDictionary);
+        }
+
         if (!Settings.readFromBuildConfigIfGestureInputEnabled(res)) {
             removePreference(Settings.PREF_GESTURE_SETTINGS, getPreferenceScreen());
         }
@@ -386,4 +411,28 @@
             }
         });
     }
+
+    private void updateUserDictionaryPreference(Preference userDictionaryPreference) {
+        final Activity activity = getActivity();
+        final TreeSet<String> localeList = UserDictionaryList.getUserDictionaryLocalesSet(activity);
+        if (null == localeList) {
+            // The locale list is null if and only if the user dictionary service is
+            // not present or disabled. In this case we need to remove the preference.
+            getPreferenceScreen().removePreference(userDictionaryPreference);
+        } else if (localeList.size() <= 1) {
+            userDictionaryPreference.setFragment(UserDictionarySettings.class.getName());
+            // If the size of localeList is 0, we don't set the locale parameter in the
+            // extras. This will be interpreted by the UserDictionarySettings class as
+            // meaning "the current locale".
+            // Note that with the current code for UserDictionaryList#getUserDictionaryLocalesSet()
+            // the locale list always has at least one element, since it always includes the current
+            // locale explicitly. @see UserDictionaryList.getUserDictionaryLocalesSet().
+            if (localeList.size() == 1) {
+                final String locale = (String)localeList.toArray()[0];
+                userDictionaryPreference.getExtras().putString("locale", locale);
+            }
+        } else {
+            userDictionaryPreference.setFragment(UserDictionaryList.class.getName());
+        }
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index f77a928..615b2df 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -34,6 +34,9 @@
  */
 public final class SettingsValues {
     private static final String TAG = SettingsValues.class.getSimpleName();
+    // "floatNegativeInfinity" is a special marker string for Float.NEGATIVE_INFINITE
+    // currently used for auto-correction
+    private static final String FLOAT_NEGATIVE_INFINITY_MARKER_STRING = "floatNegativeInfinity";
 
     // From resources:
     public final int mDelayUpdateOldSuggestions;
@@ -54,6 +57,7 @@
     public final boolean mShowsLanguageSwitchKey;
     public final boolean mUseContactsDict;
     public final boolean mUseDoubleSpacePeriod;
+    public final boolean mBlockPotentiallyOffensive;
     // Use bigrams to predict the next word when there is no input for it yet
     public final boolean mBigramPredictionEnabled;
     public final boolean mGestureInputEnabled;
@@ -123,6 +127,7 @@
         mShowsLanguageSwitchKey = Settings.readShowsLanguageSwitchKey(prefs);
         mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true);
         mUseDoubleSpacePeriod = prefs.getBoolean(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true);
+        mBlockPotentiallyOffensive = Settings.readBlockPotentiallyOffensive(prefs, res);
         mAutoCorrectEnabled = Settings.readAutoCorrectEnabled(autoCorrectionThresholdRawValue, res);
         mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res);
 
@@ -266,8 +271,12 @@
         try {
             final int arrayIndex = Integer.valueOf(currentAutoCorrectionSetting);
             if (arrayIndex >= 0 && arrayIndex < autoCorrectionThresholdValues.length) {
-                autoCorrectionThreshold = Float.parseFloat(
-                        autoCorrectionThresholdValues[arrayIndex]);
+                final String val = autoCorrectionThresholdValues[arrayIndex];
+                if (FLOAT_NEGATIVE_INFINITY_MARKER_STRING.equals(val)) {
+                    autoCorrectionThreshold = Float.NEGATIVE_INFINITY;
+                } else {
+                    autoCorrectionThreshold = Float.parseFloat(val);
+                }
             }
         } catch (NumberFormatException e) {
             // Whenever the threshold settings are correct, never come here.
@@ -275,7 +284,7 @@
             Log.w(TAG, "Cannot load auto correction threshold setting."
                     + " currentAutoCorrectionSetting: " + currentAutoCorrectionSetting
                     + ", autoCorrectionThresholdValues: "
-                    + Arrays.toString(autoCorrectionThresholdValues));
+                    + Arrays.toString(autoCorrectionThresholdValues), e);
         }
         return autoCorrectionThreshold;
     }
diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java
index d5ee58a..ab050d7 100644
--- a/java/src/com/android/inputmethod/latin/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/StringUtils.java
@@ -65,23 +65,6 @@
     }
 
     /**
-     * Find a string that start with specified prefix from an array.
-     *
-     * @param prefix a prefix string to find.
-     * @param array an string array to be searched.
-     * @return the rest part of the string that starts with the prefix.
-     * Returns null if it couldn't be found.
-     */
-    public static String findPrefixedString(final String prefix, final String[] array) {
-        for (final String element : array) {
-            if (element.startsWith(prefix)) {
-                return element.substring(prefix.length());
-            }
-        }
-        return null;
-    }
-
-    /**
      * Remove duplicates from an array of strings.
      *
      * This method will always keep the first occurrence of all strings at their position
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 2f9e34f..282b579 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -80,6 +80,7 @@
 
     public static void init(final Context context) {
         SubtypeLocale.init(context);
+        RichInputMethodManager.init(context);
         sInstance.initialize(context);
     }
 
@@ -87,10 +88,13 @@
         // Intentional empty constructor for singleton.
     }
 
-    private void initialize(final Context service) {
-        mResources = service.getResources();
+    private void initialize(final Context context) {
+        if (mResources != null) {
+            return;
+        }
+        mResources = context.getResources();
         mRichImm = RichInputMethodManager.getInstance();
-        mConnectivityManager = (ConnectivityManager) service.getSystemService(
+        mConnectivityManager = (ConnectivityManager) context.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
         mNoLanguageSubtype = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
                 SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY);
@@ -111,7 +115,7 @@
      */
     public void updateParametersOnStartInputView() {
         final List<InputMethodSubtype> enabledSubtypesOfThisIme =
-                mRichImm.getInputMethodManager().getEnabledInputMethodSubtypeList(null, true);
+                mRichImm.getMyEnabledInputMethodSubtypeList(true);
         mNeedsToDisplayLanguage.updateEnabledSubtypeCount(enabledSubtypesOfThisIme.size());
         updateShortcutIME();
     }
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 59d0207..dc9bef2 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -174,21 +174,22 @@
 
     public SuggestedWords getSuggestedWords(final WordComposer wordComposer,
             final String prevWordForBigram, final ProximityInfo proximityInfo,
-            final boolean isCorrectionEnabled, final int sessionId) {
+            final boolean blockOffensiveWords, final boolean isCorrectionEnabled,
+            final int sessionId) {
         LatinImeLogger.onStartSuggestion(prevWordForBigram);
         if (wordComposer.isBatchMode()) {
             return getSuggestedWordsForBatchInput(
-                    wordComposer, prevWordForBigram, proximityInfo, sessionId);
+                    wordComposer, prevWordForBigram, proximityInfo, blockOffensiveWords, sessionId);
         } else {
             return getSuggestedWordsForTypingInput(wordComposer, prevWordForBigram, proximityInfo,
-                    isCorrectionEnabled);
+                    blockOffensiveWords, isCorrectionEnabled);
         }
     }
 
     // Retrieves suggestions for the typing input.
     private SuggestedWords getSuggestedWordsForTypingInput(final WordComposer wordComposer,
             final String prevWordForBigram, final ProximityInfo proximityInfo,
-            final boolean isCorrectionEnabled) {
+            final boolean blockOffensiveWords, final boolean isCorrectionEnabled) {
         final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount();
         final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
                 MAX_SUGGESTIONS);
@@ -212,7 +213,7 @@
         for (final String key : mDictionaries.keySet()) {
             final Dictionary dictionary = mDictionaries.get(key);
             suggestionsSet.addAll(dictionary.getSuggestions(
-                    wordComposerForLookup, prevWordForBigram, proximityInfo));
+                    wordComposerForLookup, prevWordForBigram, proximityInfo, blockOffensiveWords));
         }
 
         final String whitelistedWord;
@@ -301,7 +302,7 @@
     // Retrieves suggestions for the batch input.
     private SuggestedWords getSuggestedWordsForBatchInput(final WordComposer wordComposer,
             final String prevWordForBigram, final ProximityInfo proximityInfo,
-            final int sessionId) {
+            final boolean blockOffensiveWords, final int sessionId) {
         final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
                 MAX_SUGGESTIONS);
 
@@ -314,8 +315,8 @@
                 continue;
             }
             final Dictionary dictionary = mDictionaries.get(key);
-            suggestionsSet.addAll(dictionary.getSuggestionsWithSessionId(
-                    wordComposer, prevWordForBigram, proximityInfo, sessionId));
+            suggestionsSet.addAll(dictionary.getSuggestionsWithSessionId(wordComposer,
+                    prevWordForBigram, proximityInfo, blockOffensiveWords, sessionId));
         }
 
         for (SuggestedWordInfo wordInfo : suggestionsSet) {
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 616e191..dfddb0f 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -122,6 +122,7 @@
 
     public static final class SuggestedWordInfo {
         public static final int MAX_SCORE = Integer.MAX_VALUE;
+        public static final int KIND_MASK_KIND = 0xFF; // Mask to get only the kind
         public static final int KIND_TYPED = 0; // What user typed
         public static final int KIND_CORRECTION = 1; // Simple correction/suggestion
         public static final int KIND_COMPLETION = 2; // Completion (suggestion with appended chars)
@@ -132,6 +133,11 @@
         public static final int KIND_SHORTCUT = 7; // A shortcut
         public static final int KIND_PREDICTION = 8; // A prediction (== a suggestion with no input)
         public static final int KIND_RESUMED = 9; // A resumed suggestion (comes from a span)
+
+        public static final int KIND_MASK_FLAGS = 0xFFFFFF00; // Mask to get the flags
+        public static final int KIND_FLAG_POSSIBLY_OFFENSIVE = 0x80000000;
+        public static final int KIND_FLAG_EXACT_MATCH = 0x40000000;
+
         public final String mWord;
         public final int mScore;
         public final int mKind; // one of the KIND_* constants above
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
index ec4dc14..92f96c0 100644
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
@@ -33,9 +33,10 @@
 
     @Override
     public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes,
-            final String prevWordForBigrams, final ProximityInfo proximityInfo) {
+            final String prevWordForBigrams, final ProximityInfo proximityInfo,
+            final boolean blockOffensiveWords) {
         syncReloadDictionaryIfRequired();
-        return super.getSuggestions(codes, prevWordForBigrams, proximityInfo);
+        return super.getSuggestions(codes, prevWordForBigrams, proximityInfo, blockOffensiveWords);
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
index 4bdaf20..33fe896 100644
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
@@ -36,9 +36,10 @@
 
     @Override
     public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes,
-            final String prevWordForBigrams, final ProximityInfo proximityInfo) {
+            final String prevWordForBigrams, final ProximityInfo proximityInfo,
+            final boolean blockOffensiveWords) {
         syncReloadDictionaryIfRequired();
-        return super.getSuggestions(codes, prevWordForBigrams, proximityInfo);
+        return super.getSuggestions(codes, prevWordForBigrams, proximityInfo, blockOffensiveWords);
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index aff5d17..0f96c54 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.inputmethodservice.InputMethodService;
@@ -473,4 +474,18 @@
         }
         return 0;
     }
+
+    public static String getVersionName(Context context) {
+        try {
+            if (context == null) {
+                return "";
+            }
+            final String packageName = context.getPackageName();
+            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
+            return info.versionName;
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "Could not find version info.", e);
+        }
+        return "";
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 51bd901..e078f03 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -16,7 +16,6 @@
 
 package com.android.inputmethod.latin;
 
-import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.Keyboard;
 
@@ -211,9 +210,8 @@
     }
 
     /**
-     * Internal method to retrieve reasonable proximity info for a character.
+     * Add a dummy key by retrieving reasonable coordinates
      */
-    @UsedForTesting
     public void addKeyInfo(final int codePoint, final Keyboard keyboard) {
         final int x, y;
         final Key key;
diff --git a/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java b/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java
new file mode 100644
index 0000000..93687e1
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.personalization;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.content.Context;
+import android.util.Patterns;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AccountUtils {
+    private AccountUtils() {
+        // This utility class is not publicly instantiable.
+    }
+
+    private static Account[] getAccounts(final Context context) {
+        return AccountManager.get(context).getAccounts();
+    }
+
+    public static List<String> getDeviceAccountsEmailAddresses(final Context context) {
+        final ArrayList<String> retval = new ArrayList<String>();
+        for (final Account account : getAccounts(context)) {
+            final String name = account.name;
+            if (Patterns.EMAIL_ADDRESS.matcher(name).matches()) {
+                retval.add(name);
+                retval.add(name.split("@")[0]);
+            }
+        }
+        return retval;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupActivity.java
index 15d0bac..8a2de88 100644
--- a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java
+++ b/java/src/com/android/inputmethod/latin/setup/SetupActivity.java
@@ -19,172 +19,25 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Resources;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.os.Message;
 import android.provider.Settings;
-import android.view.View;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
-import android.widget.TextView;
 
-import com.android.inputmethod.compat.TextViewCompatUtils;
-import com.android.inputmethod.compat.ViewCompatUtils;
-import com.android.inputmethod.latin.CollectionUtils;
-import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.RichInputMethodManager;
-import com.android.inputmethod.latin.SettingsActivity;
-import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
-
-import java.util.HashMap;
 
 public final class SetupActivity extends Activity {
-    private SetupStepIndicatorView mStepIndicatorView;
-    private final SetupStepGroup mSetupSteps = new SetupStepGroup();
-    private static final String STATE_STEP = "step";
-    private int mStepNumber;
-    private static final int STEP_1 = 1;
-    private static final int STEP_2 = 2;
-    private static final int STEP_3 = 3;
-
-    private final SettingsPoolingHandler mHandler = new SettingsPoolingHandler(this);
-
-    static final class SettingsPoolingHandler extends StaticInnerHandlerWrapper<SetupActivity> {
-        private static final int MSG_POLLING_IME_SETTINGS = 0;
-        private static final long IME_SETTINGS_POLLING_INTERVAL = 200;
-
-        public SettingsPoolingHandler(final SetupActivity outerInstance) {
-            super(outerInstance);
-        }
-
-        @Override
-        public void handleMessage(final Message msg) {
-            final SetupActivity setupActivity = getOuterInstance();
-            if (setupActivity == null) {
-                return;
-            }
-            switch (msg.what) {
-            case MSG_POLLING_IME_SETTINGS:
-                if (SetupActivity.isThisImeEnabled(setupActivity)) {
-                    setupActivity.invokeSetupWizardOfThisIme();
-                    return;
-                }
-                startPollingImeSettings();
-                break;
-            }
-        }
-
-        public void startPollingImeSettings() {
-            sendMessageDelayed(obtainMessage(MSG_POLLING_IME_SETTINGS),
-                    IME_SETTINGS_POLLING_INTERVAL);
-        }
-
-        public void cancelPollingImeSettings() {
-            removeMessages(MSG_POLLING_IME_SETTINGS);
-        }
-    }
-
     @Override
     protected void onCreate(final Bundle savedInstanceState) {
-        setTheme(android.R.style.Theme_DeviceDefault_Light_NoActionBar);
         super.onCreate(savedInstanceState);
-
-        setContentView(R.layout.setup_wizard);
-
-        RichInputMethodManager.init(this);
-
-        if (savedInstanceState == null) {
-            mStepNumber = determineSetupStepNumber();
-        } else {
-            mStepNumber = savedInstanceState.getInt(STATE_STEP);
-        }
-
-        if (mStepNumber == STEP_3) {
-            // This IME already has been enabled and set as current IME.
-            // TODO: Implement tutorial.
-            invokeSettingsOfThisIme();
+        final Intent intent = new Intent();
+        intent.setClass(this, SetupWizardActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
+                | Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent);
+        if (!isFinishing()) {
             finish();
-            return;
         }
-
-        // TODO: Use sans-serif-thin font family depending on the system locale white list and
-        // the SDK version.
-        final TextView titleView = (TextView)findViewById(R.id.setup_title);
-        final int appName = getApplicationInfo().labelRes;
-        titleView.setText(getString(R.string.setup_title, getString(appName)));
-
-        mStepIndicatorView = (SetupStepIndicatorView)findViewById(R.id.setup_step_indicator);
-
-        final SetupStep step1 = new SetupStep(findViewById(R.id.setup_step1),
-                appName, R.string.setup_step1_title, R.string.setup_step1_instruction,
-                R.drawable.ic_settings_language, R.string.language_settings);
-        step1.setAction(new Runnable() {
-            @Override
-            public void run() {
-                invokeLanguageAndInputSettings();
-                mHandler.startPollingImeSettings();
-            }
-        });
-        mSetupSteps.addStep(STEP_1, step1);
-
-        final SetupStep step2 = new SetupStep(findViewById(R.id.setup_step2),
-                appName, R.string.setup_step2_title, R.string.setup_step2_instruction,
-                0 /* actionIcon */, R.string.select_input_method);
-        step2.setAction(new Runnable() {
-            @Override
-            public void run() {
-                // Invoke input method picker.
-                RichInputMethodManager.getInstance().getInputMethodManager()
-                        .showInputMethodPicker();
-            }
-        });
-        mSetupSteps.addStep(STEP_2, step2);
-
-        final SetupStep step3 = new SetupStep(findViewById(R.id.setup_step3),
-                appName, R.string.setup_step3_title, 0 /* instruction */,
-                R.drawable.sym_keyboard_language_switch, R.string.setup_step3_instruction);
-        step3.setAction(new Runnable() {
-            @Override
-            public void run() {
-                invokeSubtypeEnablerOfThisIme();
-            }
-        });
-        mSetupSteps.addStep(STEP_3, step3);
-    }
-
-    private void invokeSetupWizardOfThisIme() {
-        final Intent intent = new Intent();
-        intent.setClass(this, SetupActivity.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
-                | Intent.FLAG_ACTIVITY_CLEAR_TOP);
-        startActivity(intent);
-    }
-
-    private void invokeSettingsOfThisIme() {
-        final Intent intent = new Intent();
-        intent.setClass(this, SettingsActivity.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
-                | Intent.FLAG_ACTIVITY_CLEAR_TOP);
-        startActivity(intent);
-    }
-
-    private void invokeLanguageAndInputSettings() {
-        final Intent intent = new Intent();
-        intent.setAction(Settings.ACTION_INPUT_METHOD_SETTINGS);
-        intent.addCategory(Intent.CATEGORY_DEFAULT);
-        startActivity(intent);
-    }
-
-    private void invokeSubtypeEnablerOfThisIme() {
-        final InputMethodInfo imi =
-                RichInputMethodManager.getInstance().getInputMethodInfoOfThisIme();
-        final Intent intent = new Intent();
-        intent.setAction(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS);
-        intent.addCategory(Intent.CATEGORY_DEFAULT);
-        intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, imi.getId());
-        startActivity(intent);
     }
 
     /**
@@ -221,141 +74,4 @@
                 context.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
         return myImi.getId().equals(currentImeId);
     }
-
-    private int determineSetupStepNumber() {
-        mHandler.cancelPollingImeSettings();
-        if (!isThisImeEnabled(this)) {
-            return STEP_1;
-        }
-        if (!isThisImeCurrent(this)) {
-            return STEP_2;
-        }
-        return STEP_3;
-    }
-
-    @Override
-    protected void onSaveInstanceState(final Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putInt(STATE_STEP, mStepNumber);
-    }
-
-    @Override
-    protected void onRestoreInstanceState(final Bundle savedInstanceState) {
-        super.onRestoreInstanceState(savedInstanceState);
-        mStepNumber = savedInstanceState.getInt(STATE_STEP);
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        mStepNumber = determineSetupStepNumber();
-    }
-
-    @Override
-    protected void onRestart() {
-        super.onRestart();
-        mStepNumber = determineSetupStepNumber();
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        updateSetupStepView();
-    }
-
-    @Override
-    public void onWindowFocusChanged(final boolean hasFocus) {
-        super.onWindowFocusChanged(hasFocus);
-        if (!hasFocus) {
-            return;
-        }
-        mStepNumber = determineSetupStepNumber();
-        updateSetupStepView();
-    }
-
-    private void updateSetupStepView() {
-        final int layoutDirection = ViewCompatUtils.getLayoutDirection(mStepIndicatorView);
-        mStepIndicatorView.setIndicatorPosition(
-                getIndicatorPosition(mStepNumber, mSetupSteps.getTotalStep(), layoutDirection));
-        mSetupSteps.enableStep(mStepNumber);
-    }
-
-    private static float getIndicatorPosition(final int step, final int totalStep,
-            final int layoutDirection) {
-        final float pos = ((step - STEP_1) * 2 + 1) / (float)(totalStep * 2);
-        return (layoutDirection == ViewCompatUtils.LAYOUT_DIRECTION_RTL) ? 1.0f - pos : pos;
-    }
-
-    static final class SetupStep implements View.OnClickListener {
-        private final View mRootView;
-        private final TextView mActionLabel;
-        private Runnable mAction;
-
-        public SetupStep(final View rootView, final int appName, final int title,
-                final int instruction, final int actionIcon, final int actionLabel) {
-            mRootView = rootView;
-            final Resources res = rootView.getResources();
-            final String applicationName = res.getString(appName);
-
-            final TextView titleView = (TextView)rootView.findViewById(R.id.setup_step_title);
-            titleView.setText(res.getString(title, applicationName));
-
-            final TextView instructionView = (TextView)rootView.findViewById(
-                    R.id.setup_step_instruction);
-            if (instruction == 0) {
-                instructionView.setVisibility(View.GONE);
-            } else {
-                instructionView.setText(res.getString(instruction, applicationName));
-            }
-
-            mActionLabel = (TextView)rootView.findViewById(R.id.setup_step_action_label);
-            mActionLabel.setText(res.getString(actionLabel));
-            if (actionIcon == 0) {
-                final int paddingEnd = ViewCompatUtils.getPaddingEnd(mActionLabel);
-                ViewCompatUtils.setPaddingRelative(mActionLabel, paddingEnd, 0, paddingEnd, 0);
-            } else {
-                final int overrideColor = res.getColor(R.color.setup_text_action);
-                final Drawable icon = res.getDrawable(actionIcon);
-                icon.setColorFilter(overrideColor, PorterDuff.Mode.MULTIPLY);
-                icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
-                TextViewCompatUtils.setCompoundDrawablesRelative(
-                        mActionLabel, icon, null, null, null);
-            }
-        }
-
-        public void setEnabled(final boolean enabled) {
-            mRootView.setVisibility(enabled ? View.VISIBLE : View.GONE);
-        }
-
-        public void setAction(final Runnable action) {
-            mActionLabel.setOnClickListener(this);
-            mAction = action;
-        }
-
-        @Override
-        public void onClick(final View v) {
-            if (mAction != null) {
-                mAction.run();
-            }
-        }
-    }
-
-    static final class SetupStepGroup {
-        private final HashMap<Integer, SetupStep> mGroup = CollectionUtils.newHashMap();
-
-        public void addStep(final int stepNo, final SetupStep step) {
-            mGroup.put(stepNo, step);
-        }
-
-        public void enableStep(final int enableStepNo) {
-            for (final Integer stepNo : mGroup.keySet()) {
-                final SetupStep step = mGroup.get(stepNo);
-                step.setEnabled(stepNo == enableStepNo);
-            }
-        }
-
-        public int getTotalStep() {
-            return mGroup.size();
-        }
-    }
 }
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java b/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java
new file mode 100644
index 0000000..974dfdd
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.setup;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.inputmethod.compat.ViewCompatUtils;
+import com.android.inputmethod.latin.R;
+
+public final class SetupStartIndicatorView extends LinearLayout {
+    public SetupStartIndicatorView(final Context context, final AttributeSet attrs) {
+        super(context, attrs);
+        setOrientation(HORIZONTAL);
+        LayoutInflater.from(context).inflate(R.layout.setup_start_indicator_label, this);
+
+        final LabelView labelView = (LabelView)findViewById(R.id.setup_start_label);
+        labelView.setIndicatorView(findViewById(R.id.setup_start_indicator));
+    }
+
+    public static final class LabelView extends TextView {
+        private View mIndicatorView;
+
+        public LabelView(final Context context, final AttributeSet attrs) {
+            super(context, attrs);
+        }
+
+        public void setIndicatorView(final View indicatorView) {
+            mIndicatorView = indicatorView;
+        }
+
+        // TODO: Once we stop supporting ICS, uncomment {@link #setPressed(boolean)} method and
+        // remove this method.
+        @Override
+        protected void drawableStateChanged() {
+            super.drawableStateChanged();
+            for (final int state : getDrawableState()) {
+                if (state == android.R.attr.state_pressed) {
+                    updateIndicatorView(true /* pressed */);
+                    return;
+                }
+            }
+            updateIndicatorView(false /* pressed */);
+        }
+
+        // TODO: Once we stop supporting ICS, uncomment this method and remove
+        // {@link #drawableStateChanged()} method.
+//        @Override
+//        public void setPressed(final boolean pressed) {
+//            super.setPressed(pressed);
+//            updateIndicatorView(pressed);
+//        }
+
+        private void updateIndicatorView(final boolean pressed) {
+            if (mIndicatorView != null) {
+                mIndicatorView.setPressed(pressed);
+                mIndicatorView.invalidate();
+            }
+        }
+    }
+
+    public static final class IndicatorView extends View {
+        private final Path mIndicatorPath = new Path();
+        private final Paint mIndicatorPaint = new Paint();
+        private final ColorStateList mIndicatorColor;
+
+        public IndicatorView(final Context context, final AttributeSet attrs) {
+            super(context, attrs);
+            mIndicatorColor = getResources().getColorStateList(
+                    R.color.setup_step_action_background);
+            mIndicatorPaint.setStyle(Paint.Style.FILL);
+        }
+
+        @Override
+        protected void onDraw(final Canvas canvas) {
+            super.onDraw(canvas);
+            final int layoutDirection = ViewCompatUtils.getLayoutDirection(this);
+            final int width = getWidth();
+            final int height = getHeight();
+            final float halfHeight = height / 2.0f;
+            final Path path = mIndicatorPath;
+            path.rewind();
+            if (layoutDirection == ViewCompatUtils.LAYOUT_DIRECTION_RTL) {
+                // Left arrow
+                path.moveTo(width, 0.0f);
+                path.lineTo(0.0f, halfHeight);
+                path.lineTo(width, height);
+            } else { // LAYOUT_DIRECTION_LTR
+                // Right arrow
+                path.moveTo(0.0f, 0.0f);
+                path.lineTo(width, halfHeight);
+                path.lineTo(0.0f, height);
+            }
+            path.close();
+            final int[] stateSet = getDrawableState();
+            final int color = mIndicatorColor.getColorForState(stateSet, 0);
+            mIndicatorPaint.setColor(color);
+            canvas.drawPath(path, mIndicatorPaint);
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java b/java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java
index 077a217..c909507 100644
--- a/java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java
+++ b/java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java
@@ -23,6 +23,7 @@
 import android.util.AttributeSet;
 import android.view.View;
 
+import com.android.inputmethod.compat.ViewCompatUtils;
 import com.android.inputmethod.latin.R;
 
 public final class SetupStepIndicatorView extends View {
@@ -36,8 +37,13 @@
         mIndicatorPaint.setStyle(Paint.Style.FILL);
     }
 
-    public void setIndicatorPosition(final float xRatio) {
-        mXRatio = xRatio;
+    public void setIndicatorPosition(final int stepPos, final int totalStepNum) {
+        final int layoutDirection = ViewCompatUtils.getLayoutDirection(this);
+        // The indicator position is the center of the partition that is equally divided into
+        // the total step number.
+        final float partionWidth = 1.0f / totalStepNum;
+        final float pos = stepPos * partionWidth + partionWidth / 2.0f;
+        mXRatio = (layoutDirection == ViewCompatUtils.LAYOUT_DIRECTION_RTL) ? 1.0f - pos : pos;
         invalidate();
     }
 
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
new file mode 100644
index 0000000..78a6478
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
@@ -0,0 +1,493 @@
+/*
+ * 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.setup;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Message;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.View;
+import android.view.inputmethod.InputMethodInfo;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.VideoView;
+
+import com.android.inputmethod.compat.TextViewCompatUtils;
+import com.android.inputmethod.compat.ViewCompatUtils;
+import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.RichInputMethodManager;
+import com.android.inputmethod.latin.SettingsActivity;
+import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
+
+import java.util.ArrayList;
+
+// TODO: Use Fragment to implement welcome screen and setup steps.
+public final class SetupWizardActivity extends Activity implements View.OnClickListener {
+    static final String TAG = SetupWizardActivity.class.getSimpleName();
+
+    private static final boolean ENABLE_WELCOME_VIDEO = true;
+
+    private View mSetupWizard;
+    private View mWelcomeScreen;
+    private View mSetupScreen;
+    private Uri mWelcomeVideoUri;
+    private VideoView mWelcomeVideoView;
+    private ImageView mWelcomeImageView;
+    private View mActionStart;
+    private View mActionNext;
+    private TextView mStep1Bullet;
+    private TextView mActionFinish;
+    private SetupStepGroup mSetupStepGroup;
+    private static final String STATE_STEP = "step";
+    private int mStepNumber;
+    private boolean mNeedsToAdjustStepNumberToSystemState;
+    private static final int STEP_WELCOME = 0;
+    private static final int STEP_1 = 1;
+    private static final int STEP_2 = 2;
+    private static final int STEP_3 = 3;
+    private static final int STEP_LAUNCHING_IME_SETTINGS = 4;
+    private static final int STEP_BACK_FROM_IME_SETTINGS = 5;
+
+    final SettingsPoolingHandler mHandler = new SettingsPoolingHandler(this);
+
+    static final class SettingsPoolingHandler
+            extends StaticInnerHandlerWrapper<SetupWizardActivity> {
+        private static final int MSG_POLLING_IME_SETTINGS = 0;
+        private static final long IME_SETTINGS_POLLING_INTERVAL = 200;
+
+        public SettingsPoolingHandler(final SetupWizardActivity outerInstance) {
+            super(outerInstance);
+        }
+
+        @Override
+        public void handleMessage(final Message msg) {
+            final SetupWizardActivity setupWizardActivity = getOuterInstance();
+            if (setupWizardActivity == null) {
+                return;
+            }
+            switch (msg.what) {
+            case MSG_POLLING_IME_SETTINGS:
+                if (SetupActivity.isThisImeEnabled(setupWizardActivity)) {
+                    setupWizardActivity.invokeSetupWizardOfThisIme();
+                    return;
+                }
+                startPollingImeSettings();
+                break;
+            }
+        }
+
+        public void startPollingImeSettings() {
+            sendMessageDelayed(obtainMessage(MSG_POLLING_IME_SETTINGS),
+                    IME_SETTINGS_POLLING_INTERVAL);
+        }
+
+        public void cancelPollingImeSettings() {
+            removeMessages(MSG_POLLING_IME_SETTINGS);
+        }
+    }
+
+    @Override
+    protected void onCreate(final Bundle savedInstanceState) {
+        setTheme(android.R.style.Theme_Translucent_NoTitleBar);
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.setup_wizard);
+        mSetupWizard = findViewById(R.id.setup_wizard);
+
+        RichInputMethodManager.init(this);
+
+        if (savedInstanceState == null) {
+            mStepNumber = determineSetupStepNumberFromLauncher();
+        } else {
+            mStepNumber = savedInstanceState.getInt(STATE_STEP);
+        }
+
+        final String applicationName = getResources().getString(getApplicationInfo().labelRes);
+        mWelcomeScreen = findViewById(R.id.setup_welcome_screen);
+        final TextView welcomeTitle = (TextView)findViewById(R.id.setup_welcome_title);
+        welcomeTitle.setText(getString(R.string.setup_welcome_title, applicationName));
+
+        mSetupScreen = findViewById(R.id.setup_steps_screen);
+        final TextView stepsTitle = (TextView)findViewById(R.id.setup_title);
+        stepsTitle.setText(getString(R.string.setup_steps_title, applicationName));
+
+        final SetupStepIndicatorView indicatorView =
+                (SetupStepIndicatorView)findViewById(R.id.setup_step_indicator);
+        mSetupStepGroup = new SetupStepGroup(indicatorView);
+
+        mStep1Bullet = (TextView)findViewById(R.id.setup_step1_bullet);
+        mStep1Bullet.setOnClickListener(this);
+        final SetupStep step1 = new SetupStep(STEP_1, applicationName,
+                mStep1Bullet, findViewById(R.id.setup_step1),
+                R.string.setup_step1_title, R.string.setup_step1_instruction,
+                R.string.setup_step1_finished_instruction, R.drawable.ic_setup_step1,
+                R.string.setup_step1_action);
+        step1.setAction(new Runnable() {
+            @Override
+            public void run() {
+                invokeLanguageAndInputSettings();
+                mHandler.startPollingImeSettings();
+            }
+        });
+        mSetupStepGroup.addStep(step1);
+
+        final SetupStep step2 = new SetupStep(STEP_2, applicationName,
+                (TextView)findViewById(R.id.setup_step2_bullet), findViewById(R.id.setup_step2),
+                R.string.setup_step2_title, R.string.setup_step2_instruction,
+                0 /* finishedInstruction */, R.drawable.ic_setup_step2,
+                R.string.setup_step2_action);
+        step2.setAction(new Runnable() {
+            @Override
+            public void run() {
+                invokeInputMethodPicker();
+            }
+        });
+        mSetupStepGroup.addStep(step2);
+
+        final SetupStep step3 = new SetupStep(STEP_3, applicationName,
+                (TextView)findViewById(R.id.setup_step3_bullet), findViewById(R.id.setup_step3),
+                R.string.setup_step3_title, R.string.setup_step3_instruction,
+                0 /* finishedInstruction */, R.drawable.ic_setup_step3,
+                R.string.setup_step3_action);
+        step3.setAction(new Runnable() {
+            @Override
+            public void run() {
+                invokeSubtypeEnablerOfThisIme();
+            }
+        });
+        mSetupStepGroup.addStep(step3);
+
+        mWelcomeVideoUri = new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
+                .authority(getPackageName())
+                .path(Integer.toString(R.raw.setup_welcome_video))
+                .build();
+        final VideoView welcomeVideoView = (VideoView)findViewById(R.id.setup_welcome_video);
+        welcomeVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+            @Override
+            public void onPrepared(final MediaPlayer mp) {
+                // Now VideoView has been laid-out and ready to play, remove background of it to
+                // reveal the video.
+                welcomeVideoView.setBackgroundResource(0);
+                mp.setLooping(true);
+            }
+        });
+        welcomeVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+            @Override
+            public boolean onError(final MediaPlayer mp, final int what, final int extra) {
+                Log.e(TAG, "Playing welcome video causes error: what=" + what + " extra=" + extra);
+                hideWelcomeVideoAndShowWelcomeImage();
+                return true;
+            }
+        });
+        mWelcomeVideoView = welcomeVideoView;
+        mWelcomeImageView = (ImageView)findViewById(R.id.setup_welcome_image);
+
+        mActionStart = findViewById(R.id.setup_start_label);
+        mActionStart.setOnClickListener(this);
+        mActionNext = findViewById(R.id.setup_next);
+        mActionNext.setOnClickListener(this);
+        mActionFinish = (TextView)findViewById(R.id.setup_finish);
+        TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(mActionFinish,
+                getResources().getDrawable(R.drawable.ic_setup_finish), null, null, null);
+        mActionFinish.setOnClickListener(this);
+    }
+
+    @Override
+    public void onClick(final View v) {
+        if (v == mActionFinish) {
+            finish();
+            return;
+        }
+        final int currentStep = determineSetupStepNumber();
+        final int nextStep;
+        if (v == mActionStart) {
+            nextStep = STEP_1;
+        } else if (v == mActionNext) {
+            nextStep = mStepNumber + 1;
+        } else if (v == mStep1Bullet && currentStep == STEP_2) {
+            nextStep = STEP_1;
+        } else {
+            nextStep = mStepNumber;
+        }
+        if (mStepNumber != nextStep) {
+            mStepNumber = nextStep;
+            updateSetupStepView();
+        }
+    }
+
+    void invokeSetupWizardOfThisIme() {
+        final Intent intent = new Intent();
+        intent.setClass(this, SetupWizardActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+                | Intent.FLAG_ACTIVITY_SINGLE_TOP
+                | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        startActivity(intent);
+        mNeedsToAdjustStepNumberToSystemState = true;
+    }
+
+    private void invokeSettingsOfThisIme() {
+        final Intent intent = new Intent();
+        intent.setClass(this, SettingsActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+                | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        startActivity(intent);
+    }
+
+    void invokeLanguageAndInputSettings() {
+        final Intent intent = new Intent();
+        intent.setAction(Settings.ACTION_INPUT_METHOD_SETTINGS);
+        intent.addCategory(Intent.CATEGORY_DEFAULT);
+        startActivity(intent);
+        mNeedsToAdjustStepNumberToSystemState = true;
+    }
+
+    void invokeInputMethodPicker() {
+        // Invoke input method picker.
+        RichInputMethodManager.getInstance().getInputMethodManager()
+                .showInputMethodPicker();
+        mNeedsToAdjustStepNumberToSystemState = true;
+    }
+
+    void invokeSubtypeEnablerOfThisIme() {
+        final InputMethodInfo imi =
+                RichInputMethodManager.getInstance().getInputMethodInfoOfThisIme();
+        final Intent intent = new Intent();
+        intent.setAction(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS);
+        intent.addCategory(Intent.CATEGORY_DEFAULT);
+        intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, imi.getId());
+        startActivity(intent);
+    }
+
+    private int determineSetupStepNumberFromLauncher() {
+        final int stepNumber = determineSetupStepNumber();
+        if (stepNumber == STEP_1) {
+            return STEP_WELCOME;
+        }
+        if (stepNumber == STEP_3) {
+            return STEP_LAUNCHING_IME_SETTINGS;
+        }
+        return stepNumber;
+    }
+
+    private int determineSetupStepNumber() {
+        mHandler.cancelPollingImeSettings();
+        if (!SetupActivity.isThisImeEnabled(this)) {
+            return STEP_1;
+        }
+        if (!SetupActivity.isThisImeCurrent(this)) {
+            return STEP_2;
+        }
+        return STEP_3;
+    }
+
+    @Override
+    protected void onSaveInstanceState(final Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putInt(STATE_STEP, mStepNumber);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(final Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+        mStepNumber = savedInstanceState.getInt(STATE_STEP);
+    }
+
+    private static boolean isInSetupSteps(final int stepNumber) {
+        return stepNumber >= STEP_1 && stepNumber <= STEP_3;
+    }
+
+    @Override
+    protected void onRestart() {
+        super.onRestart();
+        // Probably the setup wizard has been invoked from "Recent" menu. The setup step number
+        // needs to be adjusted to system state, because the state (IME is enabled and/or current)
+        // may have been changed.
+        if (isInSetupSteps(mStepNumber)) {
+            mStepNumber = determineSetupStepNumber();
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (mStepNumber == STEP_LAUNCHING_IME_SETTINGS) {
+            // Prevent white screen flashing while launching settings activity.
+            mSetupWizard.setVisibility(View.INVISIBLE);
+            invokeSettingsOfThisIme();
+            mStepNumber = STEP_BACK_FROM_IME_SETTINGS;
+            return;
+        }
+        if (mStepNumber == STEP_BACK_FROM_IME_SETTINGS) {
+            finish();
+            return;
+        }
+        updateSetupStepView();
+    }
+
+    @Override
+    public void onBackPressed() {
+        if (mStepNumber == STEP_1) {
+            mStepNumber = STEP_WELCOME;
+            updateSetupStepView();
+            return;
+        }
+        super.onBackPressed();
+    }
+
+    void hideWelcomeVideoAndShowWelcomeImage() {
+        mWelcomeVideoView.setVisibility(View.GONE);
+        mWelcomeImageView.setImageResource(R.raw.setup_welcome_image);
+        mWelcomeImageView.setVisibility(View.VISIBLE);
+    }
+
+    private void showAndStartWelcomeVideo() {
+        mWelcomeVideoView.setVisibility(View.VISIBLE);
+        mWelcomeVideoView.setVideoURI(mWelcomeVideoUri);
+        mWelcomeVideoView.start();
+    }
+
+    private void hideAndStopWelcomeVideo() {
+        mWelcomeVideoView.stopPlayback();
+        mWelcomeVideoView.setVisibility(View.GONE);
+    }
+
+    @Override
+    protected void onPause() {
+        hideAndStopWelcomeVideo();
+        super.onPause();
+    }
+
+    @Override
+    public void onWindowFocusChanged(final boolean hasFocus) {
+        super.onWindowFocusChanged(hasFocus);
+        if (hasFocus && mNeedsToAdjustStepNumberToSystemState) {
+            mNeedsToAdjustStepNumberToSystemState = false;
+            mStepNumber = determineSetupStepNumber();
+            updateSetupStepView();
+        }
+    }
+
+    private void updateSetupStepView() {
+        mSetupWizard.setVisibility(View.VISIBLE);
+        final boolean welcomeScreen = (mStepNumber == STEP_WELCOME);
+        mWelcomeScreen.setVisibility(welcomeScreen ? View.VISIBLE : View.GONE);
+        mSetupScreen.setVisibility(welcomeScreen ? View.GONE : View.VISIBLE);
+        if (welcomeScreen) {
+            if (ENABLE_WELCOME_VIDEO) {
+                showAndStartWelcomeVideo();
+            } else {
+                hideWelcomeVideoAndShowWelcomeImage();
+            }
+            return;
+        }
+        hideAndStopWelcomeVideo();
+        final boolean isStepActionAlreadyDone = mStepNumber < determineSetupStepNumber();
+        mSetupStepGroup.enableStep(mStepNumber, isStepActionAlreadyDone);
+        mActionNext.setVisibility(isStepActionAlreadyDone ? View.VISIBLE : View.GONE);
+        mActionFinish.setVisibility((mStepNumber == STEP_3) ? View.VISIBLE : View.GONE);
+    }
+
+    static final class SetupStep implements View.OnClickListener {
+        public final int mStepNo;
+        private final View mStepView;
+        private final TextView mBulletView;
+        private final int mActivatedColor;
+        private final int mDeactivatedColor;
+        private final String mInstruction;
+        private final String mFinishedInstruction;
+        private final TextView mActionLabel;
+        private Runnable mAction;
+
+        public SetupStep(final int stepNo, final String applicationName, final TextView bulletView,
+                final View stepView, final int title, final int instruction,
+                final int finishedInstruction, final int actionIcon, final int actionLabel) {
+            mStepNo = stepNo;
+            mStepView = stepView;
+            mBulletView = bulletView;
+            final Resources res = stepView.getResources();
+            mActivatedColor = res.getColor(R.color.setup_text_action);
+            mDeactivatedColor = res.getColor(R.color.setup_text_dark);
+
+            final TextView titleView = (TextView)mStepView.findViewById(R.id.setup_step_title);
+            titleView.setText(res.getString(title, applicationName));
+            mInstruction = (instruction == 0) ? null
+                    : res.getString(instruction, applicationName);
+            mFinishedInstruction = (finishedInstruction == 0) ? null
+                    : res.getString(finishedInstruction, applicationName);
+
+            mActionLabel = (TextView)mStepView.findViewById(R.id.setup_step_action_label);
+            mActionLabel.setText(res.getString(actionLabel));
+            if (actionIcon == 0) {
+                final int paddingEnd = ViewCompatUtils.getPaddingEnd(mActionLabel);
+                ViewCompatUtils.setPaddingRelative(mActionLabel, paddingEnd, 0, paddingEnd, 0);
+            } else {
+                TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(
+                        mActionLabel, res.getDrawable(actionIcon), null, null, null);
+            }
+        }
+
+        public void setEnabled(final boolean enabled, final boolean isStepActionAlreadyDone) {
+            mStepView.setVisibility(enabled ? View.VISIBLE : View.GONE);
+            mBulletView.setTextColor(enabled ? mActivatedColor : mDeactivatedColor);
+            final TextView instructionView = (TextView)mStepView.findViewById(
+                    R.id.setup_step_instruction);
+            instructionView.setText(isStepActionAlreadyDone ? mFinishedInstruction : mInstruction);
+            mActionLabel.setVisibility(isStepActionAlreadyDone ? View.GONE : View.VISIBLE);
+        }
+
+        public void setAction(final Runnable action) {
+            mActionLabel.setOnClickListener(this);
+            mAction = action;
+        }
+
+        @Override
+        public void onClick(final View v) {
+            if (v == mActionLabel && mAction != null) {
+                mAction.run();
+                return;
+            }
+        }
+    }
+
+    static final class SetupStepGroup {
+        private final SetupStepIndicatorView mIndicatorView;
+        private final ArrayList<SetupStep> mGroup = CollectionUtils.newArrayList();
+
+        public SetupStepGroup(final SetupStepIndicatorView indicatorView) {
+            mIndicatorView = indicatorView;
+        }
+
+        public void addStep(final SetupStep step) {
+            mGroup.add(step);
+        }
+
+        public void enableStep(final int enableStepNo, final boolean isStepActionAlreadyDone) {
+            for (final SetupStep step : mGroup) {
+                step.setEnabled(step.mStepNo == enableStepNo, isStepActionAlreadyDone);
+            }
+            mIndicatorView.setIndicatorPosition(enableStepNo - STEP_1, mGroup.size());
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index 1667059..13fcaf4 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -23,7 +23,7 @@
 import android.util.Log;
 import android.view.textservice.SuggestionsInfo;
 
-import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.keyboard.KeyboardLayoutSet;
 import com.android.inputmethod.latin.BinaryDictionary;
 import com.android.inputmethod.latin.CollectionUtils;
 import com.android.inputmethod.latin.ContactsBinaryDictionary;
@@ -64,8 +64,6 @@
             CollectionUtils.newSynchronizedTreeMap();
     private ContactsBinaryDictionary mContactsDictionary;
 
-    // The threshold for a candidate to be offered as a suggestion.
-    private float mSuggestionThreshold;
     // The threshold for a suggestion to be considered "recommended".
     private float mRecommendedThreshold;
     // Whether to use the contacts dictionary
@@ -112,8 +110,6 @@
 
     @Override public void onCreate() {
         super.onCreate();
-        mSuggestionThreshold =
-                Float.parseFloat(getString(R.string.spellchecker_suggestion_threshold_value));
         mRecommendedThreshold =
                 Float.parseFloat(getString(R.string.spellchecker_recommended_threshold_value));
         final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
@@ -130,6 +126,19 @@
         return script;
     }
 
+    private static String getKeyboardLayoutNameForScript(final int script) {
+        switch (script) {
+        case AndroidSpellCheckerService.SCRIPT_LATIN:
+            return "qwerty";
+        case AndroidSpellCheckerService.SCRIPT_CYRILLIC:
+            return "east_slavic";
+        case AndroidSpellCheckerService.SCRIPT_GREEK:
+            return "greek";
+        default:
+            throw new RuntimeException("Wrong script supplied: " + script);
+        }
+    }
+
     @Override
     public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
         if (!PREF_USE_CONTACTS_KEY.equals(key)) return;
@@ -198,8 +207,7 @@
     }
 
     public SuggestionsGatherer newSuggestionsGatherer(final String text, int maxLength) {
-        return new SuggestionsGatherer(
-                text, mSuggestionThreshold, mRecommendedThreshold, maxLength);
+        return new SuggestionsGatherer(text, mRecommendedThreshold, maxLength);
     }
 
     // TODO: remove this class and replace it by storage local to the session.
@@ -217,7 +225,6 @@
         private final ArrayList<String> mSuggestions;
         private final int[] mScores;
         private final String mOriginalText;
-        private final float mSuggestionThreshold;
         private final float mRecommendedThreshold;
         private final int mMaxLength;
         private int mLength = 0;
@@ -227,10 +234,9 @@
         private String mBestSuggestion = null;
         private int mBestScore = Integer.MIN_VALUE; // As small as possible
 
-        SuggestionsGatherer(final String originalText, final float suggestionThreshold,
-                final float recommendedThreshold, final int maxLength) {
+        SuggestionsGatherer(final String originalText, final float recommendedThreshold,
+                final int maxLength) {
             mOriginalText = originalText;
-            mSuggestionThreshold = suggestionThreshold;
             mRecommendedThreshold = recommendedThreshold;
             mMaxLength = maxLength;
             mSuggestions = CollectionUtils.newArrayList(maxLength + 1);
@@ -392,9 +398,13 @@
         return pool;
     }
 
-    public DictAndProximity createDictAndProximity(final Locale locale) {
+    public DictAndKeyboard createDictAndKeyboard(final Locale locale) {
         final int script = getScriptFromLocale(locale);
-        final ProximityInfo proximityInfo = new SpellCheckerProximityInfo(script);
+        final String keyboardLayoutName = getKeyboardLayoutNameForScript(script);
+        final KeyboardLayoutSet keyboardLayoutSet =
+                KeyboardLayoutSet.createKeyboardSetForSpellChecker(this, locale.toString(),
+                        keyboardLayoutName);
+
         final DictionaryCollection dictionaryCollection =
                 DictionaryFactory.createMainDictionaryFromManager(this, locale,
                         true /* useFullEditDistance */);
@@ -419,6 +429,6 @@
             mDictionaryCollectionsList.add(
                     new WeakReference<DictionaryCollection>(dictionaryCollection));
         }
-        return new DictAndProximity(dictionaryCollection, proximityInfo);
+        return new DictAndKeyboard(dictionaryCollection, keyboardLayoutSet);
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
index da86572..16e9fb7 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -257,7 +257,7 @@
             }
 
             if (shouldFilterOut(inText, mScript)) {
-                DictAndProximity dictInfo = null;
+                DictAndKeyboard dictInfo = null;
                 try {
                     dictInfo = mDictionaryPool.pollWithDefaultTimeout();
                     if (!DictionaryPool.isAValidDictionary(dictInfo)) {
@@ -283,32 +283,26 @@
             //suggestionsLimit);
             final SuggestionsGatherer suggestionsGatherer = mService.newSuggestionsGatherer(
                     text, suggestionsLimit);
-            final WordComposer composer = new WordComposer();
-            final int length = text.length();
-            for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) {
-                final int codePoint = text.codePointAt(i);
-                // The getXYForCodePointAndScript method returns (Y << 16) + X
-                final int xy = SpellCheckerProximityInfo.getXYForCodePointAndScript(
-                        codePoint, mScript);
-                if (SpellCheckerProximityInfo.NOT_A_COORDINATE_PAIR == xy) {
-                    composer.add(codePoint,
-                            Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
-                } else {
-                    composer.add(codePoint, xy & 0xFFFF, xy >> 16);
-                }
-            }
 
             final int capitalizeType = StringUtils.getCapitalizationType(text);
             boolean isInDict = true;
-            DictAndProximity dictInfo = null;
+            DictAndKeyboard dictInfo = null;
             try {
                 dictInfo = mDictionaryPool.pollWithDefaultTimeout();
                 if (!DictionaryPool.isAValidDictionary(dictInfo)) {
                     return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
                 }
+                final WordComposer composer = new WordComposer();
+                final int length = text.length();
+                for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) {
+                    final int codePoint = text.codePointAt(i);
+                    composer.addKeyInfo(codePoint, dictInfo.getKeyboard(codePoint));
+                }
+                // TODO: make a spell checker option to block offensive words or not
                 final ArrayList<SuggestedWordInfo> suggestions =
                         dictInfo.mDictionary.getSuggestions(composer, prevWord,
-                                dictInfo.mProximityInfo);
+                                dictInfo.getProximityInfo(),
+                                true /* blockOffensiveWords */);
                 for (final SuggestedWordInfo suggestion : suggestions) {
                     final String suggestionStr = suggestion.mWord;
                     suggestionsGatherer.addWord(suggestionStr.toCharArray(), null, 0,
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictAndKeyboard.java b/java/src/com/android/inputmethod/latin/spellcheck/DictAndKeyboard.java
new file mode 100644
index 0000000..b77f3e2
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/spellcheck/DictAndKeyboard.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.spellcheck;
+
+import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.KeyboardLayoutSet;
+import com.android.inputmethod.keyboard.ProximityInfo;
+
+/**
+ * A container for a Dictionary and a Keyboard.
+ */
+public final class DictAndKeyboard {
+    public final Dictionary mDictionary;
+    private final Keyboard mKeyboard;
+    private final Keyboard mManualShiftedKeyboard;
+
+    public DictAndKeyboard(
+            final Dictionary dictionary, final KeyboardLayoutSet keyboardLayoutSet) {
+        mDictionary = dictionary;
+        if (keyboardLayoutSet == null) {
+            mKeyboard = null;
+            mManualShiftedKeyboard = null;
+            return;
+        }
+        mKeyboard = keyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
+        mManualShiftedKeyboard =
+                keyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED);
+    }
+
+    public Keyboard getKeyboard(final int codePoint) {
+        if (mKeyboard == null) {
+            return null;
+        }
+        return mKeyboard.getKey(codePoint) != null ? mKeyboard : mManualShiftedKeyboard;
+    }
+
+    public ProximityInfo getProximityInfo() {
+        return mKeyboard == null ? null : mKeyboard.getProximityInfo();
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictAndProximity.java b/java/src/com/android/inputmethod/latin/spellcheck/DictAndProximity.java
deleted file mode 100644
index 017a4f5..0000000
--- a/java/src/com/android/inputmethod/latin/spellcheck/DictAndProximity.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.spellcheck;
-
-import com.android.inputmethod.latin.Dictionary;
-import com.android.inputmethod.keyboard.ProximityInfo;
-
-/**
- * A simple container for both a Dictionary and a ProximityInfo.
- */
-public final class DictAndProximity {
-    public final Dictionary mDictionary;
-    public final ProximityInfo mProximityInfo;
-    public DictAndProximity(final Dictionary dictionary, final ProximityInfo proximityInfo) {
-        mDictionary = dictionary;
-        mProximityInfo = proximityInfo;
-    }
-}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
index 81dd92d..a20e09e 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
@@ -36,7 +36,7 @@
  * the client code, but may help with sloppy clients.
  */
 @SuppressWarnings("serial")
-public final class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> {
+public final class DictionaryPool extends LinkedBlockingQueue<DictAndKeyboard> {
     private final static String TAG = DictionaryPool.class.getSimpleName();
     // How many seconds we wait for a dictionary to become available. Past this delay, we give up in
     // fear some bug caused a deadlock, and reset the whole pool.
@@ -47,11 +47,12 @@
     private int mSize;
     private volatile boolean mClosed;
     final static ArrayList<SuggestedWordInfo> noSuggestions = CollectionUtils.newArrayList();
-    private final static DictAndProximity dummyDict = new DictAndProximity(
+    private final static DictAndKeyboard dummyDict = new DictAndKeyboard(
             new Dictionary(Dictionary.TYPE_MAIN) {
                 @Override
                 public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
-                        final String prevWord, final ProximityInfo proximityInfo) {
+                        final String prevWord, final ProximityInfo proximityInfo,
+                        final boolean blockOffensiveWords) {
                     return noSuggestions;
                 }
                 @Override
@@ -63,7 +64,7 @@
                 }
             }, null);
 
-    static public boolean isAValidDictionary(final DictAndProximity dictInfo) {
+    static public boolean isAValidDictionary(final DictAndKeyboard dictInfo) {
         return null != dictInfo && dummyDict != dictInfo;
     }
 
@@ -78,32 +79,32 @@
     }
 
     @Override
-    public DictAndProximity poll(final long timeout, final TimeUnit unit)
+    public DictAndKeyboard poll(final long timeout, final TimeUnit unit)
             throws InterruptedException {
-        final DictAndProximity dict = poll();
+        final DictAndKeyboard dict = poll();
         if (null != dict) return dict;
         synchronized(this) {
             if (mSize >= mMaxSize) {
                 // Our pool is already full. Wait until some dictionary is ready, or TIMEOUT
                 // expires to avoid a deadlock.
-                final DictAndProximity result = super.poll(timeout, unit);
+                final DictAndKeyboard result = super.poll(timeout, unit);
                 if (null == result) {
                     Log.e(TAG, "Deadlock detected ! Resetting dictionary pool");
                     clear();
                     mSize = 1;
-                    return mService.createDictAndProximity(mLocale);
+                    return mService.createDictAndKeyboard(mLocale);
                 } else {
                     return result;
                 }
             } else {
                 ++mSize;
-                return mService.createDictAndProximity(mLocale);
+                return mService.createDictAndKeyboard(mLocale);
             }
         }
     }
 
     // Convenience method
-    public DictAndProximity pollWithDefaultTimeout() {
+    public DictAndKeyboard pollWithDefaultTimeout() {
         try {
             return poll(TIMEOUT, TimeUnit.SECONDS);
         } catch (InterruptedException e) {
@@ -114,7 +115,7 @@
     public void close() {
         synchronized(this) {
             mClosed = true;
-            for (DictAndProximity dict : this) {
+            for (DictAndKeyboard dict : this) {
                 dict.mDictionary.close();
             }
             clear();
@@ -122,7 +123,7 @@
     }
 
     @Override
-    public boolean offer(final DictAndProximity dict) {
+    public boolean offer(final DictAndKeyboard dict) {
         if (mClosed) {
             dict.mDictionary.close();
             return super.offer(dummyDict);
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
deleted file mode 100644
index 0c480ea..0000000
--- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.spellcheck;
-
-import android.util.SparseIntArray;
-
-import com.android.inputmethod.keyboard.ProximityInfo;
-import com.android.inputmethod.latin.Constants;
-
-public final class SpellCheckerProximityInfo extends ProximityInfo {
-    public SpellCheckerProximityInfo(final int script) {
-        super(getProximityForScript(script), PROXIMITY_GRID_WIDTH, PROXIMITY_GRID_HEIGHT);
-    }
-
-    private static final int NUL = Constants.NOT_A_CODE;
-
-    // This must be the same as MAX_PROXIMITY_CHARS_SIZE else it will not work inside
-    // native code - this value is passed at creation of the binary object and reused
-    // as the size of the passed array afterwards so they can't be different.
-    private static final int ROW_SIZE = ProximityInfo.MAX_PROXIMITY_CHARS_SIZE;
-
-    // The number of keys in a row of the grid used by the spell checker.
-    private static final int PROXIMITY_GRID_WIDTH = 11;
-    // The number of rows in the grid used by the spell checker.
-    private static final int PROXIMITY_GRID_HEIGHT = 3;
-
-    private static final int NOT_AN_INDEX = -1;
-    public static final int NOT_A_COORDINATE_PAIR = -1;
-
-    // Helper methods
-    static void buildProximityIndices(final int[] proximity, final int rowSize,
-            final SparseIntArray indices) {
-        for (int i = 0; i < proximity.length; i += rowSize) {
-            if (NUL != proximity[i]) indices.put(proximity[i], i / rowSize);
-        }
-    }
-
-    private static final class Latin {
-        // The proximity here is the union of
-        // - the proximity for a QWERTY keyboard.
-        // - the proximity for an AZERTY keyboard.
-        // - the proximity for a QWERTZ keyboard.
-        // ...plus, add all characters in the ('a', 'e', 'i', 'o', 'u') set to each other.
-        //
-        // The reasoning behind this construction is, almost any alphabetic text we may want
-        // to spell check has been entered with one of the keyboards above. Also, specifically
-        // to English, many spelling errors consist of the last vowel of the word being wrong
-        // because in English vowels tend to merge with each other in pronunciation.
-        /*
-        The Qwerty layout this represents looks like the following:
-            q w e r t y u i o p
-             a s d f g h j k l
-               z x c v b n m
-        */
-        static final int[] PROXIMITY = {
-            // Proximity for row 1. This must have exactly ROW_SIZE entries for each letter,
-            // and exactly PROXIMITY_GRID_WIDTH letters for a row. Pad with NUL's.
-            // The number of rows must be exactly PROXIMITY_GRID_HEIGHT.
-            'q', 'w', 's', 'a', 'z', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'w', 'q', 'a', 's', 'd', 'e', 'x', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'e', 'w', 's', 'd', 'f', 'r', 'a', 'i', 'o', 'u', NUL, NUL, NUL, NUL, NUL, NUL,
-            'r', 'e', 'd', 'f', 'g', 't', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            't', 'r', 'f', 'g', 'h', 'y', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'y', 't', 'g', 'h', 'j', 'u', 'a', 's', 'd', 'x', NUL, NUL, NUL, NUL, NUL, NUL,
-            'u', 'y', 'h', 'j', 'k', 'i', 'a', 'e', 'o', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'i', 'u', 'j', 'k', 'l', 'o', 'a', 'e', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'o', 'i', 'k', 'l', 'p', 'a', 'e', 'u', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'p', 'o', 'l', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-
-            // Proximity for row 2. See comment above about size.
-            'a', 'z', 'x', 's', 'w', 'q', 'e', 'i', 'o', 'u', NUL, NUL, NUL, NUL, NUL, NUL,
-            's', 'q', 'a', 'z', 'x', 'c', 'd', 'e', 'w', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'd', 'w', 's', 'x', 'c', 'v', 'f', 'r', 'e', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'f', 'e', 'd', 'c', 'v', 'b', 'g', 't', 'r', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'g', 'r', 'f', 'v', 'b', 'n', 'h', 'y', 't', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'h', 't', 'g', 'b', 'n', 'm', 'j', 'u', 'y', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'j', 'y', 'h', 'n', 'm', 'k', 'i', 'u', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'k', 'u', 'j', 'm', 'l', 'o', 'i', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'l', 'i', 'k', 'p', 'o', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-
-            // Proximity for row 3. See comment above about size.
-            'z', 'a', 's', 'd', 'x', 't', 'g', 'h', 'j', 'u', 'q', 'e', NUL, NUL, NUL, NUL,
-            'x', 'z', 'a', 's', 'd', 'c', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'c', 'x', 's', 'd', 'f', 'v', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'v', 'c', 'd', 'f', 'g', 'b', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'b', 'v', 'f', 'g', 'h', 'n', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'n', 'b', 'g', 'h', 'j', 'm', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'm', 'n', 'h', 'j', 'k', 'l', 'o', 'p', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-        };
-
-        // This is a mapping array from the code point to the index in the PROXIMITY array.
-        // When we check the spelling of a word, we need to pass (x,y) coordinates to the native
-        // code for each letter of the word. These are most easily computed from the index in the
-        // PROXIMITY array. Since we'll need to do that very often, the index lookup from the code
-        // point needs to be as fast as possible, and a map is probably the best way to do this.
-        // To avoid unnecessary boxing conversion to Integer, here we use SparseIntArray.
-        static final SparseIntArray INDICES = new SparseIntArray(PROXIMITY.length / ROW_SIZE);
-
-        static {
-            buildProximityIndices(PROXIMITY, ROW_SIZE, INDICES);
-        }
-    }
-
-    private static final class Cyrillic {
-        // TODO: The following table is solely based on the keyboard layout. Consult with Russian
-        // speakers on commonly misspelled words/letters.
-        /*
-        The Russian layout this represents looks like the following:
-            й ц у к е н г ш щ з х
-            ф ы в а п р о л д ж э
-              я ч с м и т ь б ю
-
-        This gives us the following table:
-            'й', 'ц', 'ф', 'ы', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'ц', 'й', 'ф', 'ы', 'в', 'у', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'у', 'ц', 'ы', 'в', 'а', 'к', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'к', 'у', 'в', 'а', 'п', 'е', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'е', 'к', 'а', 'п', 'р', 'н', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'н', 'е', 'п', 'р', 'о', 'г', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'г', 'н', 'р', 'о', 'л', 'ш', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'ш', 'г', 'о', 'л', 'д', 'щ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'щ', 'ш', 'л', 'д', 'ж', 'з', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'з', 'щ', 'д', 'ж', 'э', 'х', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'х', 'з', 'ж', 'э', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-
-            'ф', 'й', 'ц', 'ы', 'я', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'ы', 'й', 'ц', 'у', 'ф', 'в', 'я', 'ч', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'в', 'ц', 'у', 'к', 'ы', 'а', 'я', 'ч', 'с', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'а', 'у', 'к', 'е', 'в', 'п', 'ч', 'с', 'м', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'п', 'к', 'е', 'н', 'а', 'р', 'с', 'м', 'и', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'р', 'е', 'н', 'г', 'п', 'о', 'м', 'и', 'т', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'о', 'н', 'г', 'ш', 'р', 'л', 'и', 'т', 'ь', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'л', 'г', 'ш', 'щ', 'о', 'д', 'т', 'ь', 'б', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'д', 'ш', 'щ', 'з', 'л', 'ж', 'ь', 'б', 'ю', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'ж', 'щ', 'з', 'х', 'д', 'э', 'б', 'ю', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'э', 'з', 'х', 'ю', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-
-            'я', 'ф', 'ы', 'в', 'ч', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'ч', 'ы', 'в', 'а', 'я', 'с', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'с', 'в', 'а', 'п', 'ч', 'м', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'м', 'а', 'п', 'р', 'с', 'и', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'и', 'п', 'р', 'о', 'м', 'т', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'т', 'р', 'о', 'л', 'и', 'ь', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'ь', 'о', 'л', 'д', 'т', 'б', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'б', 'л', 'д', 'ж', 'ь', 'ю', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'ю', 'д', 'ж', 'э', 'б', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-
-        Using the following characters:
-        */
-        private static final int CY_SHORT_I = '\u0439'; // й
-        private static final int CY_TSE = '\u0446'; // ц
-        private static final int CY_U = '\u0443'; // у
-        private static final int CY_KA = '\u043A'; // к
-        private static final int CY_IE = '\u0435'; // е
-        private static final int CY_EN = '\u043D'; // н
-        private static final int CY_GHE = '\u0433'; // г
-        private static final int CY_SHA = '\u0448'; // ш
-        private static final int CY_SHCHA = '\u0449'; // щ
-        private static final int CY_ZE = '\u0437'; // з
-        private static final int CY_HA = '\u0445'; // х
-        private static final int CY_EF = '\u0444'; // ф
-        private static final int CY_YERU = '\u044B'; // ы
-        private static final int CY_VE = '\u0432'; // в
-        private static final int CY_A = '\u0430'; // а
-        private static final int CY_PE = '\u043F'; // п
-        private static final int CY_ER = '\u0440'; // р
-        private static final int CY_O = '\u043E'; // о
-        private static final int CY_EL = '\u043B'; // л
-        private static final int CY_DE = '\u0434'; // д
-        private static final int CY_ZHE = '\u0436'; // ж
-        private static final int CY_E = '\u044D'; // э
-        private static final int CY_YA = '\u044F'; // я
-        private static final int CY_CHE = '\u0447'; // ч
-        private static final int CY_ES = '\u0441'; // с
-        private static final int CY_EM = '\u043C'; // м
-        private static final int CY_I = '\u0438'; // и
-        private static final int CY_TE = '\u0442'; // т
-        private static final int CY_SOFT_SIGN = '\u044C'; // ь
-        private static final int CY_BE = '\u0431'; // б
-        private static final int CY_YU = '\u044E'; // ю
-        static final int[] PROXIMITY = {
-            // Proximity for row 1. This must have exactly ROW_SIZE entries for each letter,
-            // and exactly PROXIMITY_GRID_WIDTH letters for a row. Pad with NUL's.
-            // The number of rows must be exactly PROXIMITY_GRID_HEIGHT.
-            CY_SHORT_I, CY_TSE, CY_EF, CY_YERU, NUL, NUL, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_TSE, CY_SHORT_I, CY_EF, CY_YERU, CY_VE, CY_U, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_U, CY_TSE, CY_YERU, CY_VE, CY_A, CY_KA, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_KA, CY_U, CY_VE, CY_A, CY_PE, CY_IE, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_IE, CY_KA, CY_A, CY_PE, CY_ER, CY_EN, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_EN, CY_IE, CY_PE, CY_ER, CY_O, CY_GHE, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_GHE, CY_EN, CY_ER, CY_O, CY_EL, CY_SHA, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_SHA, CY_GHE, CY_O, CY_EL, CY_DE, CY_SHCHA, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_SHCHA, CY_SHA, CY_EL, CY_DE, CY_ZHE, CY_ZE, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_ZE, CY_SHCHA, CY_DE, CY_ZHE, CY_E, CY_HA, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_HA, CY_ZE, CY_ZHE, CY_E, NUL, NUL, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-
-            // Proximity for row 2. See comment above about size.
-            CY_EF, CY_SHORT_I, CY_TSE, CY_YERU, CY_YA, NUL, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_YERU, CY_SHORT_I, CY_TSE, CY_U, CY_EF, CY_VE, CY_YA, CY_CHE,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_VE, CY_TSE, CY_U, CY_KA, CY_YERU, CY_A, CY_YA, CY_CHE,
-                    CY_ES, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_A, CY_U, CY_KA, CY_IE, CY_VE, CY_PE, CY_CHE, CY_ES,
-                    CY_EM, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_PE, CY_KA, CY_IE, CY_EN, CY_A, CY_ER, CY_ES, CY_EM,
-                    CY_I, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_ER, CY_IE, CY_EN, CY_GHE, CY_PE, CY_O, CY_EM, CY_I,
-                    CY_TE, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_O, CY_EN, CY_GHE, CY_SHA, CY_ER, CY_EL, CY_I, CY_TE,
-                    CY_SOFT_SIGN, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_EL, CY_GHE, CY_SHA, CY_SHCHA, CY_O, CY_DE, CY_TE, CY_SOFT_SIGN,
-                    CY_BE, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_DE, CY_SHA, CY_SHCHA, CY_ZE, CY_EL, CY_ZHE, CY_SOFT_SIGN, CY_BE,
-                    CY_YU, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_ZHE, CY_SHCHA, CY_ZE, CY_HA, CY_DE, CY_E, CY_BE, CY_YU,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_E, CY_ZE, CY_HA, CY_YU, NUL, NUL, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-
-            // Proximity for row 3. See comment above about size.
-            CY_YA, CY_EF, CY_YERU, CY_VE, CY_CHE, NUL, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_CHE, CY_YERU, CY_VE, CY_A, CY_YA, CY_ES, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_ES, CY_VE, CY_A, CY_PE, CY_CHE, CY_EM, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_EM, CY_A, CY_PE, CY_ER, CY_ES, CY_I, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_I, CY_PE, CY_ER, CY_O, CY_EM, CY_TE, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_TE, CY_ER, CY_O, CY_EL, CY_I, CY_SOFT_SIGN, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_SOFT_SIGN, CY_O, CY_EL, CY_DE, CY_TE, CY_BE, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_BE, CY_EL, CY_DE, CY_ZHE, CY_SOFT_SIGN, CY_YU, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            CY_YU, CY_DE, CY_ZHE, CY_E, CY_BE, NUL, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-        };
-
-        static final SparseIntArray INDICES = new SparseIntArray(PROXIMITY.length / ROW_SIZE);
-
-        static {
-            buildProximityIndices(PROXIMITY, ROW_SIZE, INDICES);
-        }
-    }
-
-    private static final class Greek {
-        // TODO: The following table is solely based on the keyboard layout. Consult with Greek
-        // speakers on commonly misspelled words/letters.
-        /*
-        The Greek layout this represents looks like the following:
-            ; ς ε ρ τ υ θ ι ο π
-             α σ δ φ γ η ξ κ λ
-               ζ χ ψ ω β ν μ
-
-        This gives us the following table:
-            'ς', 'ε', 'α', 'σ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'ε', 'ς', 'ρ', 'σ', 'δ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'ρ', 'ε', 'τ', 'δ', 'φ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'τ', 'ρ', 'υ', 'φ', 'γ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'υ', 'τ', 'θ', 'γ', 'η', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'θ', 'υ', 'ι', 'η', 'ξ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'ι', 'θ', 'ο', 'ξ', 'κ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'ο', 'ι', 'π', 'κ', 'λ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'π', 'ο', 'λ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-
-            'α', 'ς', 'σ', 'ζ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'σ', 'ς', 'ε', 'α', 'δ', 'ζ', 'χ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'δ', 'ε', 'ρ', 'σ', 'φ', 'ζ', 'χ', 'ψ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'φ', 'ρ', 'τ', 'δ', 'γ', 'χ', 'ψ', 'ω', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'γ', 'τ', 'υ', 'φ', 'η', 'ψ', 'ω', 'β', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'η', 'υ', 'θ', 'γ', 'ξ', 'ω', 'β', 'ν', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'ξ', 'θ', 'ι', 'η', 'κ', 'β', 'ν', 'μ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'κ', 'ι', 'ο', 'ξ', 'λ', 'ν', 'μ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'λ', 'ο', 'π', 'κ', 'μ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-
-            'ζ', 'α', 'σ', 'δ', 'χ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'χ', 'σ', 'δ', 'φ', 'ζ', 'ψ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'ψ', 'δ', 'φ', 'γ', 'χ', 'ω', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'ω', 'φ', 'γ', 'η', 'ψ', 'β', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'β', 'γ', 'η', 'ξ', 'ω', 'ν', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'ν', 'η', 'ξ', 'κ', 'β', 'μ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            'μ', 'ξ', 'κ', 'λ', 'ν', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-
-        Using the following characters:
-        */
-        private static final int GR_FINAL_SIGMA = '\u03C2'; // ς
-        private static final int GR_EPSILON = '\u03B5'; // ε
-        private static final int GR_RHO = '\u03C1'; // ρ
-        private static final int GR_TAU = '\u03C4'; // τ
-        private static final int GR_UPSILON = '\u03C5'; // υ
-        private static final int GR_THETA = '\u03B8'; // θ
-        private static final int GR_IOTA = '\u03B9'; // ι
-        private static final int GR_OMICRON = '\u03BF'; // ο
-        private static final int GR_PI = '\u03C0'; // π
-        private static final int GR_ALPHA = '\u03B1'; // α
-        private static final int GR_SIGMA = '\u03C3'; // σ
-        private static final int GR_DELTA = '\u03B4'; // δ
-        private static final int GR_PHI = '\u03C6'; // φ
-        private static final int GR_GAMMA = '\u03B3'; // γ
-        private static final int GR_ETA = '\u03B7'; // η
-        private static final int GR_XI = '\u03BE'; // ξ
-        private static final int GR_KAPPA = '\u03BA'; // κ
-        private static final int GR_LAMDA = '\u03BB'; // λ
-        private static final int GR_ZETA = '\u03B6'; // ζ
-        private static final int GR_CHI = '\u03C7'; // χ
-        private static final int GR_PSI = '\u03C8'; // ψ
-        private static final int GR_OMEGA = '\u03C9'; // ω
-        private static final int GR_BETA = '\u03B2'; // β
-        private static final int GR_NU = '\u03BD'; // ν
-        private static final int GR_MU = '\u03BC'; // μ
-        static final int[] PROXIMITY = {
-            // Proximity for row 1. This must have exactly ROW_SIZE entries for each letter,
-            // and exactly PROXIMITY_GRID_WIDTH letters for a row. Pad with NUL's.
-            // The number of rows must be exactly PROXIMITY_GRID_HEIGHT.
-            GR_FINAL_SIGMA, GR_EPSILON, GR_ALPHA, GR_SIGMA, NUL, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_EPSILON, GR_FINAL_SIGMA, GR_RHO, GR_SIGMA, GR_DELTA, NUL, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_RHO, GR_EPSILON, GR_TAU, GR_DELTA, GR_PHI, NUL, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_TAU, GR_RHO, GR_UPSILON, GR_PHI, GR_GAMMA, NUL, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_UPSILON, GR_TAU, GR_THETA, GR_GAMMA, GR_ETA, NUL, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_THETA, GR_UPSILON, GR_IOTA, GR_ETA, GR_XI, NUL, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_IOTA, GR_THETA, GR_OMICRON, GR_XI, GR_KAPPA, NUL, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_OMICRON, GR_IOTA, GR_PI, GR_KAPPA, GR_LAMDA, NUL, NUL, NUL, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_PI, GR_OMICRON, GR_LAMDA, NUL, NUL, NUL, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-
-            GR_ALPHA, GR_FINAL_SIGMA, GR_SIGMA, GR_ZETA, NUL, NUL, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_SIGMA, GR_FINAL_SIGMA, GR_EPSILON, GR_ALPHA, GR_DELTA, GR_ZETA, GR_CHI, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_DELTA, GR_EPSILON, GR_RHO, GR_SIGMA, GR_PHI, GR_ZETA, GR_CHI, GR_PSI,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_PHI, GR_RHO, GR_TAU, GR_DELTA, GR_GAMMA, GR_CHI, GR_PSI, GR_OMEGA,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_GAMMA, GR_TAU, GR_UPSILON, GR_PHI, GR_ETA, GR_PSI, GR_OMEGA, GR_BETA,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_ETA, GR_UPSILON, GR_THETA, GR_GAMMA, GR_XI, GR_OMEGA, GR_BETA, GR_NU,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_XI, GR_THETA, GR_IOTA, GR_ETA, GR_KAPPA, GR_BETA, GR_NU, GR_MU,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_KAPPA, GR_IOTA, GR_OMICRON, GR_XI, GR_LAMDA, GR_NU, GR_MU, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_LAMDA, GR_OMICRON, GR_PI, GR_KAPPA, GR_MU, NUL, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-
-            GR_ZETA, GR_ALPHA, GR_SIGMA, GR_DELTA, GR_CHI, NUL, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_CHI, GR_SIGMA, GR_DELTA, GR_PHI, GR_ZETA, GR_PSI, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_PSI, GR_DELTA, GR_PHI, GR_GAMMA, GR_CHI, GR_OMEGA, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_OMEGA, GR_PHI, GR_GAMMA, GR_ETA, GR_PSI, GR_BETA, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_BETA, GR_GAMMA, GR_ETA, GR_XI, GR_OMEGA, GR_NU, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_NU, GR_ETA, GR_XI, GR_KAPPA, GR_BETA, GR_MU, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            GR_MU, GR_XI, GR_KAPPA, GR_LAMDA, GR_NU, NUL, NUL, NUL,
-                    NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-            NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
-        };
-
-        static final SparseIntArray INDICES = new SparseIntArray(PROXIMITY.length / ROW_SIZE);
-
-        static {
-            buildProximityIndices(PROXIMITY, ROW_SIZE, INDICES);
-        }
-    }
-
-    private static int[] getProximityForScript(final int script) {
-        switch (script) {
-        case AndroidSpellCheckerService.SCRIPT_LATIN:
-            return Latin.PROXIMITY;
-        case AndroidSpellCheckerService.SCRIPT_CYRILLIC:
-            return Cyrillic.PROXIMITY;
-        case AndroidSpellCheckerService.SCRIPT_GREEK:
-            return Greek.PROXIMITY;
-        default:
-            throw new RuntimeException("Wrong script supplied: " + script);
-        }
-    }
-
-    private static int getIndexOfCodeForScript(final int codePoint, final int script) {
-        switch (script) {
-        case AndroidSpellCheckerService.SCRIPT_LATIN:
-            return Latin.INDICES.get(codePoint, NOT_AN_INDEX);
-        case AndroidSpellCheckerService.SCRIPT_CYRILLIC:
-            return Cyrillic.INDICES.get(codePoint, NOT_AN_INDEX);
-        case AndroidSpellCheckerService.SCRIPT_GREEK:
-            return Greek.INDICES.get(codePoint, NOT_AN_INDEX);
-        default:
-            throw new RuntimeException("Wrong script supplied: " + script);
-        }
-    }
-
-    // Returns (Y << 16) + X to avoid creating a temporary object. This is okay because
-    // X and Y are limited to PROXIMITY_GRID_WIDTH resp. PROXIMITY_GRID_HEIGHT which is very
-    // inferior to 1 << 16
-    // As an exception, this returns NOT_A_COORDINATE_PAIR if the key is not on the grid
-    public static int getXYForCodePointAndScript(final int codePoint, final int script) {
-        final int index = getIndexOfCodeForScript(codePoint, script);
-        if (NOT_AN_INDEX == index) return NOT_A_COORDINATE_PAIR;
-        final int y = index / PROXIMITY_GRID_WIDTH;
-        final int x = index % PROXIMITY_GRID_WIDTH;
-        if (y > PROXIMITY_GRID_HEIGHT) {
-            // Safety check, should be entirely useless
-            throw new RuntimeException("Wrong y coordinate in spell checker proximity");
-        }
-        return (y << 16) + x;
-    }
-}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
index 3037669..09f81d4 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
@@ -23,19 +23,28 @@
 
 import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardActionListener;
 import com.android.inputmethod.keyboard.TypefaceUtils;
 import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
 import com.android.inputmethod.keyboard.internal.KeyboardParams;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.Utils;
 
 public final class MoreSuggestions extends Keyboard {
     public static final int SUGGESTION_CODE_BASE = 1024;
 
-    MoreSuggestions(final MoreSuggestionsParam params) {
+    public final SuggestedWords mSuggestedWords;
+
+    public static abstract class MoreSuggestionsListener extends KeyboardActionListener.Adapter {
+        public abstract void onSuggestionSelected(final int index, final SuggestedWordInfo info);
+    }
+
+    MoreSuggestions(final MoreSuggestionsParam params, final SuggestedWords suggestedWords) {
         super(params);
+        mSuggestedWords = suggestedWords;
     }
 
     private static final class MoreSuggestionsParam extends KeyboardParams {
@@ -52,8 +61,9 @@
             super();
         }
 
-        public int layout(final SuggestedWords suggestions, final int fromPos, final int maxWidth,
-                final int minWidth, final int maxRow, final Paint paint, final Resources res) {
+        public int layout(final SuggestedWords suggestedWords, final int fromPos,
+                final int maxWidth, final int minWidth, final int maxRow, final Paint paint,
+                final Resources res) {
             clearKeys();
             mDivider = res.getDrawable(R.drawable.more_suggestions_divider);
             mDividerWidth = mDivider.getIntrinsicWidth();
@@ -61,9 +71,9 @@
 
             int row = 0;
             int pos = fromPos, rowStartPos = fromPos;
-            final int size = Math.min(suggestions.size(), SuggestionStripView.MAX_SUGGESTIONS);
+            final int size = Math.min(suggestedWords.size(), SuggestionStripView.MAX_SUGGESTIONS);
             while (pos < size) {
-                final String word = suggestions.getWord(pos);
+                final String word = suggestedWords.getWord(pos);
                 // TODO: Should take care of text x-scaling.
                 mWidths[pos] = (int)(TypefaceUtils.getLabelWidth(word, paint) + padding);
                 final int numColumn = pos - rowStartPos + 1;
@@ -163,7 +173,7 @@
 
     public static final class Builder extends KeyboardBuilder<MoreSuggestionsParam> {
         private final MoreSuggestionsView mPaneView;
-        private SuggestedWords mSuggestions;
+        private SuggestedWords mSuggestedWords;
         private int mFromPos;
         private int mToPos;
 
@@ -172,7 +182,7 @@
             mPaneView = paneView;
         }
 
-        public Builder layout(final SuggestedWords suggestions, final int fromPos,
+        public Builder layout(final SuggestedWords suggestedWords, final int fromPos,
                 final int maxWidth, final int minWidth, final int maxRow,
                 final Keyboard parentKeyboard) {
             final int xmlId = R.xml.kbd_suggestions_pane_template;
@@ -180,11 +190,11 @@
             mParams.mVerticalGap = mParams.mTopPadding = parentKeyboard.mVerticalGap / 2;
 
             mPaneView.updateKeyboardGeometry(mParams.mDefaultRowHeight);
-            final int count = mParams.layout(suggestions, fromPos, maxWidth, minWidth, maxRow,
+            final int count = mParams.layout(suggestedWords, fromPos, maxWidth, minWidth, maxRow,
                     mPaneView.newLabelPaint(null /* key */), mResources);
             mFromPos = fromPos;
             mToPos = fromPos + count;
-            mSuggestions = suggestions;
+            mSuggestedWords = suggestedWords;
             return this;
         }
 
@@ -195,8 +205,8 @@
                 final int x = params.getX(pos);
                 final int y = params.getY(pos);
                 final int width = params.getWidth(pos);
-                final String word = mSuggestions.getWord(pos).toString();
-                final String info = Utils.getDebugInfo(mSuggestions, pos);
+                final String word = mSuggestedWords.getWord(pos);
+                final String info = Utils.getDebugInfo(mSuggestedWords, pos);
                 final int index = pos + SUGGESTION_CODE_BASE;
                 final Key key = new Key(
                         params, word, info, KeyboardIconsSet.ICON_UNDEFINED, index, null, x, y,
@@ -211,7 +221,7 @@
                     params.onAddKey(divider);
                 }
             }
-            return new MoreSuggestions(params);
+            return new MoreSuggestions(params, mSuggestedWords);
         }
     }
 
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
index 6509f39..d585b5c 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
@@ -18,15 +18,21 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.util.Log;
 
+import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.MoreKeysKeyboardView;
 import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestionsListener;
 
 /**
  * A view that renders a virtual {@link MoreSuggestions}. It handles rendering of keys and detecting
  * key presses and touch movements.
  */
 public final class MoreSuggestionsView extends MoreKeysKeyboardView {
+    private static final String TAG = MoreSuggestionsView.class.getSimpleName();
+
     public MoreSuggestionsView(final Context context, final AttributeSet attrs) {
         this(context, attrs, R.attr.moreSuggestionsViewStyle);
     }
@@ -54,9 +60,24 @@
 
     @Override
     public void onCodeInput(final int code, final int x, final int y) {
-        final int index = code - MoreSuggestions.SUGGESTION_CODE_BASE;
-        if (index >= 0 && index < SuggestionStripView.MAX_SUGGESTIONS) {
-            mListener.onCustomRequest(index);
+        final Keyboard keyboard = getKeyboard();
+        if (!(keyboard instanceof MoreSuggestions)) {
+            Log.e(TAG, "Expected keyboard is MoreSuggestions, but found "
+                    + keyboard.getClass().getName());
+            return;
         }
+        final SuggestedWords suggestedWords = ((MoreSuggestions)keyboard).mSuggestedWords;
+        final int index = code - MoreSuggestions.SUGGESTION_CODE_BASE;
+        if (index < 0 || index >= suggestedWords.size()) {
+            Log.e(TAG, "Selected suggestion has an illegal index: " + index);
+            return;
+        }
+        if (!(mListener instanceof MoreSuggestionsListener)) {
+            Log.e(TAG, "Expected mListener is MoreSuggestionsListener, but found "
+                    + mListener.getClass().getName());
+            return;
+        }
+        ((MoreSuggestionsListener)mListener).onSuggestionSelected(
+                index, suggestedWords.getInfo(index));
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index 2a21ec2..ad350a0 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -50,7 +50,6 @@
 import android.widget.TextView;
 
 import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardActionListener;
 import com.android.inputmethod.keyboard.KeyboardSwitcher;
 import com.android.inputmethod.keyboard.MainKeyboardView;
 import com.android.inputmethod.keyboard.MoreKeysPanel;
@@ -65,6 +64,7 @@
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.Utils;
 import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestionsListener;
 import com.android.inputmethod.research.ResearchLogger;
 
 import java.util.ArrayList;
@@ -93,7 +93,7 @@
     private final ArrayList<View> mDividers = CollectionUtils.newArrayList();
 
     Listener mListener;
-    SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
+    private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
 
     private final SuggestionStripViewParams mParams;
     private static final float MIN_TEXT_XSCALE = 0.70f;
@@ -652,15 +652,11 @@
         dismissMoreSuggestions();
     }
 
-    private final KeyboardActionListener mMoreSuggestionsListener =
-            new KeyboardActionListener.Adapter() {
+    private final MoreSuggestionsListener mMoreSuggestionsListener = new MoreSuggestionsListener() {
         @Override
-        public boolean onCustomRequest(final int requestCode) {
-            final int index = requestCode;
-            final SuggestedWordInfo wordInfo = mSuggestedWords.getInfo(index);
+        public void onSuggestionSelected(final int index, final SuggestedWordInfo wordInfo) {
             mListener.pickSuggestionManually(index, wordInfo);
             dismissMoreSuggestions();
-            return true;
         }
 
         @Override
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java
new file mode 100644
index 0000000..2b6fda3
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.userdictionary;
+
+import com.android.inputmethod.compat.UserDictionaryCompatUtils;
+import com.android.inputmethod.latin.LocaleUtils;
+import com.android.inputmethod.latin.R;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.provider.UserDictionary;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.EditText;
+
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.TreeSet;
+
+// Caveat: This class is basically taken from
+// packages/apps/Settings/src/com/android/settings/inputmethod/UserDictionaryAddWordContents.java
+// in order to deal with some devices that have issues with the user dictionary handling
+
+/**
+ * A container class to factor common code to UserDictionaryAddWordFragment
+ * and UserDictionaryAddWordActivity.
+ */
+public class UserDictionaryAddWordContents {
+    public static final String EXTRA_MODE = "mode";
+    public static final String EXTRA_WORD = "word";
+    public static final String EXTRA_SHORTCUT = "shortcut";
+    public static final String EXTRA_LOCALE = "locale";
+    public static final String EXTRA_ORIGINAL_WORD = "originalWord";
+    public static final String EXTRA_ORIGINAL_SHORTCUT = "originalShortcut";
+
+    public static final int MODE_EDIT = 0;
+    public static final int MODE_INSERT = 1;
+
+    /* package */ static final int CODE_WORD_ADDED = 0;
+    /* package */ static final int CODE_CANCEL = 1;
+    /* package */ static final int CODE_ALREADY_PRESENT = 2;
+
+    private static final int FREQUENCY_FOR_USER_DICTIONARY_ADDS = 250;
+
+    private final int mMode; // Either MODE_EDIT or MODE_INSERT
+    private final EditText mWordEditText;
+    private final EditText mShortcutEditText;
+    private String mLocale;
+    private final String mOldWord;
+    private final String mOldShortcut;
+
+    /* package */ UserDictionaryAddWordContents(final View view, final Bundle args) {
+        mWordEditText = (EditText)view.findViewById(R.id.user_dictionary_add_word_text);
+        mShortcutEditText = (EditText)view.findViewById(R.id.user_dictionary_add_shortcut);
+        if (!UserDictionarySettings.IS_SHORTCUT_API_SUPPORTED) {
+            mShortcutEditText.setVisibility(View.GONE);
+            view.findViewById(R.id.user_dictionary_add_shortcut_label).setVisibility(View.GONE);
+        }
+        final String word = args.getString(EXTRA_WORD);
+        if (null != word) {
+            mWordEditText.setText(word);
+            mWordEditText.setSelection(word.length());
+        }
+        final String shortcut;
+        if (UserDictionarySettings.IS_SHORTCUT_API_SUPPORTED) {
+            shortcut = args.getString(EXTRA_SHORTCUT);
+            if (null != shortcut && null != mShortcutEditText) {
+                mShortcutEditText.setText(shortcut);
+            }
+            mOldShortcut = args.getString(EXTRA_SHORTCUT);
+        } else {
+            shortcut = null;
+            mOldShortcut = null;
+        }
+        mMode = args.getInt(EXTRA_MODE); // default return value for #getInt() is 0 = MODE_EDIT
+        mOldWord = args.getString(EXTRA_WORD);
+        updateLocale(args.getString(EXTRA_LOCALE));
+    }
+
+    // locale may be null, this means default locale
+    // It may also be the empty string, which means "all locales"
+    /* package */ void updateLocale(final String locale) {
+        mLocale = null == locale ? Locale.getDefault().toString() : locale;
+    }
+
+    /* package */ void saveStateIntoBundle(final Bundle outState) {
+        outState.putString(EXTRA_WORD, mWordEditText.getText().toString());
+        outState.putString(EXTRA_ORIGINAL_WORD, mOldWord);
+        if (null != mShortcutEditText) {
+            outState.putString(EXTRA_SHORTCUT, mShortcutEditText.getText().toString());
+        }
+        if (null != mOldShortcut) {
+            outState.putString(EXTRA_ORIGINAL_SHORTCUT, mOldShortcut);
+        }
+        outState.putString(EXTRA_LOCALE, mLocale);
+    }
+
+    /* package */ void delete(final Context context) {
+        if (MODE_EDIT == mMode && !TextUtils.isEmpty(mOldWord)) {
+            // Mode edit: remove the old entry.
+            final ContentResolver resolver = context.getContentResolver();
+            UserDictionarySettings.deleteWord(mOldWord, mOldShortcut, resolver);
+        }
+        // If we are in add mode, nothing was added, so we don't need to do anything.
+    }
+
+    /* package */
+    int apply(final Context context, final Bundle outParameters) {
+        if (null != outParameters) saveStateIntoBundle(outParameters);
+        final ContentResolver resolver = context.getContentResolver();
+        if (MODE_EDIT == mMode && !TextUtils.isEmpty(mOldWord)) {
+            // Mode edit: remove the old entry.
+            UserDictionarySettings.deleteWord(mOldWord, mOldShortcut, resolver);
+        }
+        final String newWord = mWordEditText.getText().toString();
+        final String newShortcut;
+        if (!UserDictionarySettings.IS_SHORTCUT_API_SUPPORTED) {
+            newShortcut = null;
+        } else if (null == mShortcutEditText) {
+            newShortcut = null;
+        } else {
+            final String tmpShortcut = mShortcutEditText.getText().toString();
+            if (TextUtils.isEmpty(tmpShortcut)) {
+                newShortcut = null;
+            } else {
+                newShortcut = tmpShortcut;
+            }
+        }
+        if (TextUtils.isEmpty(newWord)) {
+            // If the word is somehow empty, don't insert it.
+            return CODE_CANCEL;
+        }
+        // If there is no shortcut, and the word already exists in the database, then we
+        // should not insert, because either A. the word exists with no shortcut, in which
+        // case the exact same thing we want to insert is already there, or B. the word
+        // exists with at least one shortcut, in which case it has priority on our word.
+        if (hasWord(newWord, context)) return CODE_ALREADY_PRESENT;
+
+        // Disallow duplicates. If the same word with no shortcut is defined, remove it; if
+        // the same word with the same shortcut is defined, remove it; but we don't mind if
+        // there is the same word with a different, non-empty shortcut.
+        UserDictionarySettings.deleteWord(newWord, null, resolver);
+        if (!TextUtils.isEmpty(newShortcut)) {
+            // If newShortcut is empty we just deleted this, no need to do it again
+            UserDictionarySettings.deleteWord(newWord, newShortcut, resolver);
+        }
+
+        // In this class we use the empty string to represent 'all locales' and mLocale cannot
+        // be null. However the addWord method takes null to mean 'all locales'.
+        UserDictionaryCompatUtils.addWord(context, newWord.toString(),
+                FREQUENCY_FOR_USER_DICTIONARY_ADDS, newShortcut, TextUtils.isEmpty(mLocale) ?
+                        null : LocaleUtils.constructLocaleFromString(mLocale));
+
+        return CODE_WORD_ADDED;
+    }
+
+    private static final String[] HAS_WORD_PROJECTION = { UserDictionary.Words.WORD };
+    private static final String HAS_WORD_SELECTION_ONE_LOCALE = UserDictionary.Words.WORD
+            + "=? AND " + UserDictionary.Words.LOCALE + "=?";
+    private static final String HAS_WORD_SELECTION_ALL_LOCALES = UserDictionary.Words.WORD
+            + "=? AND " + UserDictionary.Words.LOCALE + " is null";
+    private boolean hasWord(final String word, final Context context) {
+        final Cursor cursor;
+        // mLocale == "" indicates this is an entry for all languages. Here, mLocale can't
+        // be null at all (it's ensured by the updateLocale method).
+        if ("".equals(mLocale)) {
+            cursor = context.getContentResolver().query(UserDictionary.Words.CONTENT_URI,
+                      HAS_WORD_PROJECTION, HAS_WORD_SELECTION_ALL_LOCALES,
+                      new String[] { word }, null /* sort order */);
+        } else {
+            cursor = context.getContentResolver().query(UserDictionary.Words.CONTENT_URI,
+                      HAS_WORD_PROJECTION, HAS_WORD_SELECTION_ONE_LOCALE,
+                      new String[] { word, mLocale }, null /* sort order */);
+        }
+        try {
+            if (null == cursor) return false;
+            return cursor.getCount() > 0;
+        } finally {
+            if (null != cursor) cursor.close();
+        }
+    }
+
+    public static class LocaleRenderer {
+        private final String mLocaleString;
+        private final String mDescription;
+        // LocaleString may NOT be null.
+        public LocaleRenderer(final Context context, final String localeString) {
+            mLocaleString = localeString;
+            if (null == localeString) {
+                mDescription = context.getString(R.string.user_dict_settings_more_languages);
+            } else if ("".equals(localeString)) {
+                mDescription = context.getString(R.string.user_dict_settings_all_languages);
+            } else {
+                mDescription = LocaleUtils.constructLocaleFromString(localeString).getDisplayName();
+            }
+        }
+        @Override
+        public String toString() {
+            return mDescription;
+        }
+        public String getLocaleString() {
+            return mLocaleString;
+        }
+        // "More languages..." is null ; "All languages" is the empty string.
+        public boolean isMoreLanguages() {
+            return null == mLocaleString;
+        }
+    }
+
+    private static void addLocaleDisplayNameToList(final Context context,
+            final ArrayList<LocaleRenderer> list, final String locale) {
+        if (null != locale) {
+            list.add(new LocaleRenderer(context, locale));
+        }
+    }
+
+    // Helper method to get the list of locales to display for this word
+    public ArrayList<LocaleRenderer> getLocalesList(final Activity activity) {
+        final TreeSet<String> locales = UserDictionaryList.getUserDictionaryLocalesSet(activity);
+        // Remove our locale if it's in, because we're always gonna put it at the top
+        locales.remove(mLocale); // mLocale may not be null
+        final String systemLocale = Locale.getDefault().toString();
+        // The system locale should be inside. We want it at the 2nd spot.
+        locales.remove(systemLocale); // system locale may not be null
+        locales.remove(""); // Remove the empty string if it's there
+        final ArrayList<LocaleRenderer> localesList = new ArrayList<LocaleRenderer>();
+        // Add the passed locale, then the system locale at the top of the list. Add an
+        // "all languages" entry at the bottom of the list.
+        addLocaleDisplayNameToList(activity, localesList, mLocale);
+        if (!systemLocale.equals(mLocale)) {
+            addLocaleDisplayNameToList(activity, localesList, systemLocale);
+        }
+        for (final String l : locales) {
+            // TODO: sort in unicode order
+            addLocaleDisplayNameToList(activity, localesList, l);
+        }
+        if (!"".equals(mLocale)) {
+            // If mLocale is "", then we already inserted the "all languages" item, so don't do it
+            addLocaleDisplayNameToList(activity, localesList, ""); // meaning: all languages
+        }
+        localesList.add(new LocaleRenderer(activity, null)); // meaning: select another locale
+        return localesList;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java
new file mode 100644
index 0000000..58c8f26
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java
@@ -0,0 +1,162 @@
+/*
+ * 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.userdictionary;
+
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.userdictionary.UserDictionaryAddWordContents.LocaleRenderer;
+import com.android.inputmethod.latin.userdictionary.UserDictionaryLocalePicker.LocationChangedListener;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+// Caveat: This class is basically taken from
+// packages/apps/Settings/src/com/android/settings/inputmethod/UserDictionaryAddWordFragment.java
+// in order to deal with some devices that have issues with the user dictionary handling
+
+/**
+ * Fragment to add a word/shortcut to the user dictionary.
+ *
+ * As opposed to the UserDictionaryActivity, this is only invoked within Settings
+ * from the UserDictionarySettings.
+ */
+public class UserDictionaryAddWordFragment extends Fragment
+        implements AdapterView.OnItemSelectedListener, LocationChangedListener {
+
+    private static final int OPTIONS_MENU_ADD = Menu.FIRST;
+    private static final int OPTIONS_MENU_DELETE = Menu.FIRST + 1;
+
+    private UserDictionaryAddWordContents mContents;
+    private View mRootView;
+    private boolean mIsDeleting = false;
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        setHasOptionsMenu(true);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+        mRootView = inflater.inflate(R.layout.user_dictionary_add_word_fullscreen, null);
+        mIsDeleting = false;
+        if (null == mContents) {
+            mContents = new UserDictionaryAddWordContents(mRootView, getArguments());
+        }
+        return mRootView;
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        final MenuItem actionItemAdd = menu.add(0, OPTIONS_MENU_ADD, 0,
+                R.string.user_dict_settings_add_menu_title).setIcon(R.drawable.ic_menu_add);
+        actionItemAdd.setShowAsAction(
+                MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+        final MenuItem actionItemDelete = menu.add(0, OPTIONS_MENU_DELETE, 0,
+                R.string.user_dict_settings_delete).setIcon(android.R.drawable.ic_menu_delete);
+        actionItemDelete.setShowAsAction(
+                MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+    }
+
+    /**
+     * Callback for the framework when a menu option is pressed.
+     *
+     * @param MenuItem the item that was pressed
+     * @return false to allow normal menu processing to proceed, true to consume it here
+     */
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == OPTIONS_MENU_ADD) {
+            // added the entry in "onPause"
+            getActivity().onBackPressed();
+            return true;
+        }
+        if (item.getItemId() == OPTIONS_MENU_DELETE) {
+            mContents.delete(getActivity());
+            mIsDeleting = true;
+            getActivity().onBackPressed();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        // We are being shown: display the word
+        updateSpinner();
+    }
+
+    private void updateSpinner() {
+        final ArrayList<LocaleRenderer> localesList = mContents.getLocalesList(getActivity());
+
+        final Spinner localeSpinner =
+                (Spinner)mRootView.findViewById(R.id.user_dictionary_add_locale);
+        final ArrayAdapter<LocaleRenderer> adapter = new ArrayAdapter<LocaleRenderer>(getActivity(),
+                android.R.layout.simple_spinner_item, localesList);
+        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        localeSpinner.setAdapter(adapter);
+        localeSpinner.setOnItemSelectedListener(this);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        // We are being hidden: commit changes to the user dictionary, unless we were deleting it
+        if (!mIsDeleting) {
+            mContents.apply(getActivity(), null);
+        }
+    }
+
+    @Override
+    public void onItemSelected(final AdapterView<?> parent, final View view, final int pos,
+            final long id) {
+        final LocaleRenderer locale = (LocaleRenderer)parent.getItemAtPosition(pos);
+        if (locale.isMoreLanguages()) {
+            PreferenceActivity preferenceActivity = (PreferenceActivity)getActivity();
+            preferenceActivity.startPreferenceFragment(new UserDictionaryLocalePicker(), true);
+        } else {
+            mContents.updateLocale(locale.getLocaleString());
+        }
+    }
+
+    @Override
+    public void onNothingSelected(final AdapterView<?> parent) {
+        // I'm not sure we can come here, but if we do, that's the right thing to do.
+        final Bundle args = getArguments();
+        mContents.updateLocale(args.getString(UserDictionaryAddWordContents.EXTRA_LOCALE));
+    }
+
+    // Called by the locale picker
+    @Override
+    public void onLocaleSelected(final Locale locale) {
+        mContents.updateLocale(locale.toString());
+        getActivity().onBackPressed();
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java
new file mode 100644
index 0000000..6e64882
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java
@@ -0,0 +1,127 @@
+/*
+ * 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.userdictionary;
+
+import com.android.inputmethod.latin.LocaleUtils;
+import com.android.inputmethod.latin.R;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceGroup;
+import android.provider.UserDictionary;
+import android.text.TextUtils;
+
+import java.util.Locale;
+import java.util.TreeSet;
+
+// Caveat: This class is basically taken from
+// packages/apps/Settings/src/com/android/settings/inputmethod/UserDictionaryList.java
+// in order to deal with some devices that have issues with the user dictionary handling
+
+public class UserDictionaryList extends PreferenceFragment {
+
+    public static final String USER_DICTIONARY_SETTINGS_INTENT_ACTION =
+            "android.settings.USER_DICTIONARY_SETTINGS";
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getActivity()));
+    }
+
+    public static TreeSet<String> getUserDictionaryLocalesSet(Activity activity) {
+        @SuppressWarnings("deprecation")
+        final Cursor cursor = activity.managedQuery(UserDictionary.Words.CONTENT_URI,
+                new String[] { UserDictionary.Words.LOCALE },
+                null, null, null);
+        final TreeSet<String> localeList = new TreeSet<String>();
+        boolean addedAllLocale = false;
+        if (null == cursor) {
+            // The user dictionary service is not present or disabled. Return null.
+            return null;
+        } else if (cursor.moveToFirst()) {
+            final int columnIndex = cursor.getColumnIndex(UserDictionary.Words.LOCALE);
+            do {
+                final String locale = cursor.getString(columnIndex);
+                final boolean allLocale = TextUtils.isEmpty(locale);
+                localeList.add(allLocale ? "" : locale);
+                if (allLocale) {
+                    addedAllLocale = true;
+                }
+            } while (cursor.moveToNext());
+        }
+        if (!UserDictionarySettings.IS_SHORTCUT_API_SUPPORTED && !addedAllLocale) {
+            // For ICS, we need to show "For all languages" in case that the keyboard locale
+            // is different from the system locale
+            localeList.add("");
+        }
+        localeList.add(Locale.getDefault().toString());
+        return localeList;
+    }
+
+    /**
+     * Creates the entries that allow the user to go into the user dictionary for each locale.
+     * @param userDictGroup The group to put the settings in.
+     */
+    protected void createUserDictSettings(PreferenceGroup userDictGroup) {
+        final Activity activity = getActivity();
+        userDictGroup.removeAll();
+        final TreeSet<String> localeList =
+                UserDictionaryList.getUserDictionaryLocalesSet(activity);
+
+        if (localeList.isEmpty()) {
+            userDictGroup.addPreference(createUserDictionaryPreference(null, activity));
+        } else {
+            for (String locale : localeList) {
+                userDictGroup.addPreference(createUserDictionaryPreference(locale, activity));
+            }
+        }
+    }
+
+    /**
+     * Create a single User Dictionary Preference object, with its parameters set.
+     * @param locale The locale for which this user dictionary is for.
+     * @return The corresponding preference.
+     */
+    protected Preference createUserDictionaryPreference(String locale, Activity activity) {
+        final Preference newPref = new Preference(getActivity());
+        final Intent intent = new Intent(USER_DICTIONARY_SETTINGS_INTENT_ACTION);
+        if (null == locale) {
+            newPref.setTitle(Locale.getDefault().getDisplayName());
+        } else {
+            if ("".equals(locale))
+                newPref.setTitle(getString(R.string.user_dict_settings_all_languages));
+            else
+                newPref.setTitle(LocaleUtils.constructLocaleFromString(locale).getDisplayName());
+            intent.putExtra("locale", locale);
+            newPref.getExtras().putString("locale", locale);
+        }
+        newPref.setIntent(intent);
+        newPref.setFragment(UserDictionarySettings.class.getName());
+        return newPref;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        createUserDictSettings(getPreferenceScreen());
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryLocalePicker.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryLocalePicker.java
new file mode 100644
index 0000000..58d3fb9
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryLocalePicker.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.userdictionary;
+
+import android.app.Fragment;
+
+import java.util.Locale;
+
+// Caveat: This class is basically taken from
+// packages/apps/Settings/src/com/android/settings/inputmethod/UserDictionaryLocalePicker.java
+// in order to deal with some devices that have issues with the user dictionary handling
+
+public class UserDictionaryLocalePicker extends Fragment {
+    public UserDictionaryLocalePicker() {
+        super();
+        // TODO: implement
+    }
+
+    public interface LocationChangedListener {
+        public void onLocaleSelected(Locale locale);
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java
new file mode 100644
index 0000000..50dda96
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java
@@ -0,0 +1,334 @@
+/**
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.android.inputmethod.latin.userdictionary;
+
+import com.android.inputmethod.latin.R;
+
+import android.app.ListFragment;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.UserDictionary;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AlphabetIndexer;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.SectionIndexer;
+import android.widget.SimpleCursorAdapter;
+import android.widget.TextView;
+
+import java.util.Locale;
+
+// Caveat: This class is basically taken from
+// packages/apps/Settings/src/com/android/settings/inputmethod/UserDictionarySettings.java
+// in order to deal with some devices that have issues with the user dictionary handling
+
+public class UserDictionarySettings extends ListFragment {
+
+    public static final boolean IS_SHORTCUT_API_SUPPORTED =
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
+
+    private static final String[] QUERY_PROJECTION_SHORTCUT_UNSUPPORTED =
+            { UserDictionary.Words._ID, UserDictionary.Words.WORD};
+    private static final String[] QUERY_PROJECTION_SHORTCUT_SUPPORTED =
+            { UserDictionary.Words._ID, UserDictionary.Words.WORD, UserDictionary.Words.SHORTCUT};
+    private static final String[] QUERY_PROJECTION =
+            IS_SHORTCUT_API_SUPPORTED ?
+                    QUERY_PROJECTION_SHORTCUT_SUPPORTED : QUERY_PROJECTION_SHORTCUT_UNSUPPORTED;
+
+    // The index of the shortcut in the above array.
+    private static final int INDEX_SHORTCUT = 2;
+
+    private static final String[] ADAPTER_FROM_SHORTCUT_UNSUPPORTED = {
+        UserDictionary.Words.WORD,
+    };
+
+    private static final String[] ADAPTER_FROM_SHORTCUT_SUPPORTED = {
+        UserDictionary.Words.WORD, UserDictionary.Words.SHORTCUT
+    };
+
+    private static final String[] ADAPTER_FROM = IS_SHORTCUT_API_SUPPORTED ?
+            ADAPTER_FROM_SHORTCUT_SUPPORTED : ADAPTER_FROM_SHORTCUT_UNSUPPORTED;
+
+    private static final int[] ADAPTER_TO_SHORTCUT_UNSUPPORTED = {
+        android.R.id.text1,
+    };
+
+    private static final int[] ADAPTER_TO_SHORTCUT_SUPPORTED = {
+        android.R.id.text1, android.R.id.text2
+    };
+
+    private static final int[] ADAPTER_TO = IS_SHORTCUT_API_SUPPORTED ?
+            ADAPTER_TO_SHORTCUT_SUPPORTED : ADAPTER_TO_SHORTCUT_UNSUPPORTED;
+
+    // Either the locale is empty (means the word is applicable to all locales)
+    // or the word equals our current locale
+    private static final String QUERY_SELECTION =
+            UserDictionary.Words.LOCALE + "=?";
+    private static final String QUERY_SELECTION_ALL_LOCALES =
+            UserDictionary.Words.LOCALE + " is null";
+
+    private static final String DELETE_SELECTION_WITH_SHORTCUT = UserDictionary.Words.WORD
+            + "=? AND " + UserDictionary.Words.SHORTCUT + "=?";
+    private static final String DELETE_SELECTION_WITHOUT_SHORTCUT = UserDictionary.Words.WORD
+            + "=? AND " + UserDictionary.Words.SHORTCUT + " is null OR "
+            + UserDictionary.Words.SHORTCUT + "=''";
+    private static final String DELETE_SELECTION_SHORTCUT_UNSUPPORTED =
+            UserDictionary.Words.WORD + "=?";
+
+    private static final int OPTIONS_MENU_ADD = Menu.FIRST;
+
+    private Cursor mCursor;
+
+    protected String mLocale;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getActivity().getActionBar().setTitle(R.string.edit_personal_dictionary);
+    }
+
+    @Override
+    public View onCreateView(
+            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        return inflater.inflate(
+                R.layout.user_dictionary_preference_list_fragment, container, false);
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        final Intent intent = getActivity().getIntent();
+        final String localeFromIntent =
+                null == intent ? null : intent.getStringExtra("locale");
+
+        final Bundle arguments = getArguments();
+        final String localeFromArguments =
+                null == arguments ? null : arguments.getString("locale");
+
+        final String locale;
+        if (null != localeFromArguments) {
+            locale = localeFromArguments;
+        } else if (null != localeFromIntent) {
+            locale = localeFromIntent;
+        } else {
+            locale = null;
+        }
+
+        mLocale = locale;
+        mCursor = createCursor(locale);
+        TextView emptyView = (TextView) getView().findViewById(android.R.id.empty);
+        emptyView.setText(R.string.user_dict_settings_empty_text);
+
+        final ListView listView = getListView();
+        listView.setAdapter(createAdapter());
+        listView.setFastScrollEnabled(true);
+        listView.setEmptyView(emptyView);
+
+        setHasOptionsMenu(true);
+
+    }
+
+    @SuppressWarnings("deprecation")
+    private Cursor createCursor(final String locale) {
+        // Locale can be any of:
+        // - The string representation of a locale, as returned by Locale#toString()
+        // - The empty string. This means we want a cursor returning words valid for all locales.
+        // - null. This means we want a cursor for the current locale, whatever this is.
+        // Note that this contrasts with the data inside the database, where NULL means "all
+        // locales" and there should never be an empty string. The confusion is called by the
+        // historical use of null for "all locales".
+        // TODO: it should be easy to make this more readable by making the special values
+        // human-readable, like "all_locales" and "current_locales" strings, provided they
+        // can be guaranteed not to match locales that may exist.
+        if ("".equals(locale)) {
+            // Case-insensitive sort
+            return getActivity().managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION,
+                    QUERY_SELECTION_ALL_LOCALES, null,
+                    "UPPER(" + UserDictionary.Words.WORD + ")");
+        } else {
+            final String queryLocale = null != locale ? locale : Locale.getDefault().toString();
+            return getActivity().managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION,
+                    QUERY_SELECTION, new String[] { queryLocale },
+                    "UPPER(" + UserDictionary.Words.WORD + ")");
+        }
+    }
+
+    private ListAdapter createAdapter() {
+        return new MyAdapter(getActivity(), R.layout.user_dictionary_item, mCursor,
+                ADAPTER_FROM, ADAPTER_TO, this);
+    }
+
+    @Override
+    public void onListItemClick(ListView l, View v, int position, long id) {
+        final String word = getWord(position);
+        final String shortcut = getShortcut(position);
+        if (word != null) {
+            showAddOrEditDialog(word, shortcut);
+        }
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        if (!UserDictionarySettings.IS_SHORTCUT_API_SUPPORTED) {
+            final Locale systemLocale = getResources().getConfiguration().locale;
+            if (!TextUtils.isEmpty(mLocale) && !mLocale.equals(systemLocale.toString())) {
+                // Hide the add button for ICS because it doesn't support specifying a locale
+                // for an entry. This new "locale"-aware API has been added in conjunction
+                // with the shortcut API.
+                return;
+            }
+        }
+        MenuItem actionItem =
+                menu.add(0, OPTIONS_MENU_ADD, 0, R.string.user_dict_settings_add_menu_title)
+                .setIcon(R.drawable.ic_menu_add);
+        actionItem.setShowAsAction(
+                MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == OPTIONS_MENU_ADD) {
+            showAddOrEditDialog(null, null);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Add or edit a word. If editingWord is null, it's an add; otherwise, it's an edit.
+     * @param editingWord the word to edit, or null if it's an add.
+     * @param editingShortcut the shortcut for this entry, or null if none.
+     */
+    private void showAddOrEditDialog(final String editingWord, final String editingShortcut) {
+        final Bundle args = new Bundle();
+        args.putInt(UserDictionaryAddWordContents.EXTRA_MODE, null == editingWord
+                ? UserDictionaryAddWordContents.MODE_INSERT
+                : UserDictionaryAddWordContents.MODE_EDIT);
+        args.putString(UserDictionaryAddWordContents.EXTRA_WORD, editingWord);
+        args.putString(UserDictionaryAddWordContents.EXTRA_SHORTCUT, editingShortcut);
+        args.putString(UserDictionaryAddWordContents.EXTRA_LOCALE, mLocale);
+        android.preference.PreferenceActivity pa =
+                (android.preference.PreferenceActivity)getActivity();
+        pa.startPreferencePanel(UserDictionaryAddWordFragment.class.getName(),
+                args, R.string.user_dict_settings_add_dialog_title, null, null, 0);
+    }
+
+    private String getWord(final int position) {
+        if (null == mCursor) return null;
+        mCursor.moveToPosition(position);
+        // Handle a possible race-condition
+        if (mCursor.isAfterLast()) return null;
+
+        return mCursor.getString(
+                mCursor.getColumnIndexOrThrow(UserDictionary.Words.WORD));
+    }
+
+    private String getShortcut(final int position) {
+        if (!IS_SHORTCUT_API_SUPPORTED) return null;
+        if (null == mCursor) return null;
+        mCursor.moveToPosition(position);
+        // Handle a possible race-condition
+        if (mCursor.isAfterLast()) return null;
+
+        return mCursor.getString(
+                mCursor.getColumnIndexOrThrow(UserDictionary.Words.SHORTCUT));
+    }
+
+    public static void deleteWord(final String word, final String shortcut,
+            final ContentResolver resolver) {
+        if (!IS_SHORTCUT_API_SUPPORTED) {
+            resolver.delete(UserDictionary.Words.CONTENT_URI, DELETE_SELECTION_SHORTCUT_UNSUPPORTED,
+                    new String[] { word });
+        } else if (TextUtils.isEmpty(shortcut)) {
+            resolver.delete(
+                    UserDictionary.Words.CONTENT_URI, DELETE_SELECTION_WITHOUT_SHORTCUT,
+                    new String[] { word });
+        } else {
+            resolver.delete(
+                    UserDictionary.Words.CONTENT_URI, DELETE_SELECTION_WITH_SHORTCUT,
+                    new String[] { word, shortcut });
+        }
+    }
+
+    private static class MyAdapter extends SimpleCursorAdapter implements SectionIndexer {
+
+        private AlphabetIndexer mIndexer;
+
+        private ViewBinder mViewBinder = new ViewBinder() {
+
+            @Override
+            public boolean setViewValue(View v, Cursor c, int columnIndex) {
+                if (!IS_SHORTCUT_API_SUPPORTED) {
+                    // just let SimpleCursorAdapter set the view values
+                    return false;
+                }
+                if (columnIndex == INDEX_SHORTCUT) {
+                    final String shortcut = c.getString(INDEX_SHORTCUT);
+                    if (TextUtils.isEmpty(shortcut)) {
+                        v.setVisibility(View.GONE);
+                    } else {
+                        ((TextView)v).setText(shortcut);
+                        v.setVisibility(View.VISIBLE);
+                    }
+                    v.invalidate();
+                    return true;
+                }
+
+                return false;
+            }
+        };
+
+        @SuppressWarnings("deprecation")
+        public MyAdapter(Context context, int layout, Cursor c, String[] from, int[] to,
+                UserDictionarySettings settings) {
+            super(context, layout, c, from, to);
+
+            if (null != c) {
+                final String alphabet = context.getString(R.string.user_dict_fast_scroll_alphabet);
+                final int wordColIndex = c.getColumnIndexOrThrow(UserDictionary.Words.WORD);
+                mIndexer = new AlphabetIndexer(c, wordColIndex, alphabet);
+            }
+            setViewBinder(mViewBinder);
+        }
+
+        @Override
+        public int getPositionForSection(int section) {
+            return null == mIndexer ? 0 : mIndexer.getPositionForSection(section);
+        }
+
+        @Override
+        public int getSectionForPosition(int position) {
+            return null == mIndexer ? 0 : mIndexer.getSectionForPosition(position);
+        }
+
+        @Override
+        public Object[] getSections() {
+            return null == mIndexer ? null : mIndexer.getSections();
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/research/FeedbackFragment.java b/java/src/com/android/inputmethod/research/FeedbackFragment.java
index 39f9c87..a073829 100644
--- a/java/src/com/android/inputmethod/research/FeedbackFragment.java
+++ b/java/src/com/android/inputmethod/research/FeedbackFragment.java
@@ -65,12 +65,10 @@
         mCancelButton.setOnClickListener(this);
 
         if (savedInstanceState != null) {
-            Log.d(TAG, "restoring from savedInstanceState");
             restoreState(savedInstanceState);
         } else {
             final Bundle bundle = getActivity().getIntent().getExtras();
             if (bundle != null) {
-                Log.d(TAG, "restoring from getArguments()");
                 restoreState(bundle);
             }
         }
@@ -81,10 +79,7 @@
     public void onClick(final View view) {
         final ResearchLogger researchLogger = ResearchLogger.getInstance();
         if (view == mIncludingUserRecordingCheckBox) {
-            if (hasUserRecording()) {
-                // Remove the recording
-                setHasUserRecording(false);
-            } else {
+            if (mIncludingUserRecordingCheckBox.isChecked()) {
                 final Bundle bundle = new Bundle();
                 onSaveInstanceState(bundle);
 
@@ -103,9 +98,9 @@
                         R.string.research_feedback_empty_feedback_error_message,
                         Toast.LENGTH_LONG).show();
             } else {
-                final boolean isIncludingAccountName = isIncludingAccountName();
-                researchLogger.sendFeedback(feedbackContents,
-                        false /* isIncludingHistory */, isIncludingAccountName, hasUserRecording());
+                final boolean isIncludingAccountName = mIncludingAccountNameCheckBox.isChecked();
+                researchLogger.sendFeedback(feedbackContents, false /* isIncludingHistory */,
+                        isIncludingAccountName, mIncludingUserRecordingCheckBox.isChecked());
                 getActivity().finish();
                 researchLogger.setFeedbackDialogBundle(null);
                 researchLogger.onLeavingSendFeedbackDialog();
@@ -125,29 +120,13 @@
         final String savedFeedbackString = mEditText.getText().toString();
 
         bundle.putString(KEY_FEEDBACK_STRING, savedFeedbackString);
-        bundle.putBoolean(KEY_INCLUDE_ACCOUNT_NAME, isIncludingAccountName());
-        bundle.putBoolean(KEY_HAS_USER_RECORDING, hasUserRecording());
+        bundle.putBoolean(KEY_INCLUDE_ACCOUNT_NAME, mIncludingAccountNameCheckBox.isChecked());
+        bundle.putBoolean(KEY_HAS_USER_RECORDING, mIncludingUserRecordingCheckBox.isChecked());
     }
 
-    public void restoreState(final Bundle bundle) {
+    private void restoreState(final Bundle bundle) {
         mEditText.setText(bundle.getString(KEY_FEEDBACK_STRING));
-        setIsIncludingAccountName(bundle.getBoolean(KEY_INCLUDE_ACCOUNT_NAME));
-        setHasUserRecording(bundle.getBoolean(KEY_HAS_USER_RECORDING));
-    }
-
-    private boolean hasUserRecording() {
-        return mIncludingUserRecordingCheckBox.isChecked();
-    }
-
-    private void setHasUserRecording(final boolean hasRecording) {
-        mIncludingUserRecordingCheckBox.setChecked(hasRecording);
-    }
-
-    private boolean isIncludingAccountName() {
-        return mIncludingAccountNameCheckBox.isChecked();
-    }
-
-    private void setIsIncludingAccountName(final boolean isIncludingAccountName) {
-        mIncludingAccountNameCheckBox.setChecked(isIncludingAccountName);
+        mIncludingAccountNameCheckBox.setChecked(bundle.getBoolean(KEY_INCLUDE_ACCOUNT_NAME));
+        mIncludingUserRecordingCheckBox.setChecked(bundle.getBoolean(KEY_HAS_USER_RECORDING));
     }
 }
diff --git a/java/src/com/android/inputmethod/research/FixedLogBuffer.java b/java/src/com/android/inputmethod/research/FixedLogBuffer.java
index 78dc595..8b64de8 100644
--- a/java/src/com/android/inputmethod/research/FixedLogBuffer.java
+++ b/java/src/com/android/inputmethod/research/FixedLogBuffer.java
@@ -51,38 +51,36 @@
         mNumActualWords = 0;
     }
 
-    protected int getNumActualWords() {
-        return mNumActualWords;
-    }
-
     /**
      * Adds a new LogUnit to the front of the LIFO queue, evicting existing LogUnit's
      * (oldest first) if word capacity is reached.
      */
     @Override
     public void shiftIn(final LogUnit newLogUnit) {
-        if (!newLogUnit.hasWord()) {
-            // This LogUnit isn't a word, so it doesn't count toward the word-limit.
+        if (!newLogUnit.hasOneOrMoreWords()) {
+            // This LogUnit doesn't contain any word, so it doesn't count toward the word-limit.
             super.shiftIn(newLogUnit);
             return;
         }
+        final int numWordsIncoming = newLogUnit.getNumWords();
         if (mNumActualWords >= mWordCapacity) {
             // Give subclass a chance to handle the buffer full condition by shifting out logUnits.
+            // TODO: Tell onBufferFull() how much space it needs to make to avoid forced eviction.
             onBufferFull();
             // If still full, evict.
             if (mNumActualWords >= mWordCapacity) {
-                shiftOutWords(1);
+                shiftOutWords(numWordsIncoming);
             }
         }
         super.shiftIn(newLogUnit);
-        mNumActualWords++; // Must be a word, or we wouldn't be here.
+        mNumActualWords += numWordsIncoming;
     }
 
     @Override
     public LogUnit unshiftIn() {
         final LogUnit logUnit = super.unshiftIn();
-        if (logUnit != null && logUnit.hasWord()) {
-            mNumActualWords--;
+        if (logUnit != null && logUnit.hasOneOrMoreWords()) {
+            mNumActualWords -= logUnit.getNumWords();
         }
         return logUnit;
     }
@@ -113,18 +111,28 @@
     @Override
     public LogUnit shiftOut() {
         final LogUnit logUnit = super.shiftOut();
-        if (logUnit != null && logUnit.hasWord()) {
-            mNumActualWords--;
+        if (logUnit != null && logUnit.hasOneOrMoreWords()) {
+            mNumActualWords -= logUnit.getNumWords();
         }
         return logUnit;
     }
 
-    protected void shiftOutWords(final int numWords) {
-        final int targetNumWords = mNumActualWords - numWords;
-        final LinkedList<LogUnit> logUnits = getLogUnits();
-        while (mNumActualWords > targetNumWords && !logUnits.isEmpty()) {
-            shiftOut();
-        }
+    /**
+     * Remove LogUnits from the front of the LogBuffer until {@code numWords} have been removed.
+     *
+     * If there are less than {@code numWords} in the buffer, shifts out all {@code LogUnit}s.
+     *
+     * @param numWords the minimum number of words in {@link LogUnit}s to shift out
+     * @return the number of actual words LogUnit}s shifted out
+     */
+    protected int shiftOutWords(final int numWords) {
+        int numWordsShiftedOut = 0;
+        do {
+            final LogUnit logUnit = shiftOut();
+            if (logUnit == null) break;
+            numWordsShiftedOut += logUnit.getNumWords();
+        } while (numWordsShiftedOut < numWords);
+        return numWordsShiftedOut;
     }
 
     public void shiftOutAll() {
@@ -136,27 +144,31 @@
     }
 
     /**
-     * Returns a list of {@link LogUnit}s at the front of the buffer that have associated words.  No
-     * more than {@code n} LogUnits will have words associated with them.  If there are not enough
-     * LogUnits in the buffer to meet the word requirement, returns the all LogUnits.
+     * Returns a list of {@link LogUnit}s at the front of the buffer that have words associated with
+     * them.
+     *
+     * There will be no more than {@code n} words in the returned list.  So if 2 words are
+     * requested, and the first LogUnit has 3 words, it is not returned.  If 2 words are requested,
+     * and the first LogUnit has only 1 word, and the next LogUnit 2 words, only the first LogUnit
+     * is returned.  If the first LogUnit has no words associated with it, and the second LogUnit
+     * has three words, then only the first LogUnit (which has no associated words) is returned.  If
+     * there are not enough LogUnits in the buffer to meet the word requirement, then all LogUnits
+     * will be returned.
      *
      * @param n The maximum number of {@link LogUnit}s with words to return.
      * @return The list of the {@link LogUnit}s containing the first n words
      */
     public ArrayList<LogUnit> peekAtFirstNWords(int n) {
         final LinkedList<LogUnit> logUnits = getLogUnits();
-        final int length = logUnits.size();
         // Allocate space for n*2 logUnits.  There will be at least n, one for each word, and
         // there may be additional for punctuation, between-word commands, etc.  This should be
         // enough that reallocation won't be necessary.
-        final ArrayList<LogUnit> list = new ArrayList<LogUnit>(n * 2);
-        for (int i = 0; i < length && n > 0; i++) {
-            final LogUnit logUnit = logUnits.get(i);
-            list.add(logUnit);
-            if (logUnit.hasWord()) {
-                n--;
-            }
+        final ArrayList<LogUnit> resultList = new ArrayList<LogUnit>(n * 2);
+        for (final LogUnit logUnit : logUnits) {
+            n -= logUnit.getNumWords();
+            if (n < 0) break;
+            resultList.add(logUnit);
         }
-        return list;
+        return resultList;
     }
 }
diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java
index 1c01675..cf1388f 100644
--- a/java/src/com/android/inputmethod/research/LogUnit.java
+++ b/java/src/com/android/inputmethod/research/LogUnit.java
@@ -26,9 +26,10 @@
 import com.android.inputmethod.latin.define.ProductionFlag;
 
 import java.io.IOException;
-import java.io.StringWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.regex.Pattern;
 
 /**
  * A group of log statements related to each other.
@@ -49,27 +50,45 @@
     private static final boolean DEBUG = false
             && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG;
 
+    private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s+");
+    private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
     private final ArrayList<LogStatement> mLogStatementList;
     private final ArrayList<Object[]> mValuesList;
     // Assume that mTimeList is sorted in increasing order.  Do not insert null values into
     // mTimeList.
     private final ArrayList<Long> mTimeList;
-    // Word that this LogUnit generates.  Should be null if the LogUnit does not generate a genuine
-    // word (i.e. separators alone do not count as a word).  Should never be empty.
-    private String mWord;
+    // Words that this LogUnit generates.  Should be null if the data in the LogUnit does not
+    // generate a genuine word (i.e. separators alone do not count as a word).  Should never be
+    // empty.  Note that if the user types spaces explicitly, then normally mWords should contain
+    // only a single word; it will only contain space-separate multiple words if the user does not
+    // enter a space, and the system enters one automatically.
+    private String mWords;
+    private String[] mWordArray = EMPTY_STRING_ARRAY;
     private boolean mMayContainDigit;
     private boolean mIsPartOfMegaword;
     private boolean mContainsCorrection;
 
-    // mCorrectionType indicates whether the word was corrected at all, and if so, whether it was
-    // to a different word or just a "typo" correction.  It is considered a "typo" if the final
-    // word was listed in the suggestions available the first time the word was gestured or
-    // tapped.
+    // mCorrectionType indicates whether the word was corrected at all, and if so, the nature of the
+    // correction.
     private int mCorrectionType;
+    // LogUnits start in this state.  If a word is entered without being corrected, it will have
+    // this CorrectiontType.
     public static final int CORRECTIONTYPE_NO_CORRECTION = 0;
+    // The LogUnit was corrected manually by the user in an unspecified way.
     public static final int CORRECTIONTYPE_CORRECTION = 1;
+    // The LogUnit was corrected manually by the user to a word not in the list of suggestions of
+    // the first word typed here.  (Note: this is a heuristic value, it may be incorrect, for
+    // example, if the user repositions the cursor).
     public static final int CORRECTIONTYPE_DIFFERENT_WORD = 2;
+    // The LogUnit was corrected manually by the user to a word that was in the list of suggestions
+    // of the first word typed here.  (Again, a heuristic).  It is probably a typo correction.
     public static final int CORRECTIONTYPE_TYPO = 3;
+    // TODO: Rather than just tracking the current state, keep a historical record of the LogUnit's
+    // state and statistics.  This should include how many times it has been corrected, whether
+    // other LogUnit edits were done between edits to this LogUnit, etc.  Also track when a LogUnit
+    // previously contained a word, but was corrected to empty (because it was deleted, and there is
+    // no known replacement).
 
     private SuggestedWords mSuggestedWords;
 
@@ -117,9 +136,11 @@
      * @param researchLog where to publish the contents of this {@code LogUnit}
      * @param canIncludePrivateData whether the private data in this {@code LogUnit} should be
      * included
+     *
+     * @throws IOException if publication to the log file is not possible
      */
     public synchronized void publishTo(final ResearchLog researchLog,
-            final boolean canIncludePrivateData) {
+            final boolean canIncludePrivateData) throws IOException {
         // Write out any logStatement that passes the privacy filter.
         final int size = mLogStatementList.size();
         if (size != 0) {
@@ -166,7 +187,7 @@
         final LogStatement logStatement;
         if (canIncludePrivateData) {
             LOGSTATEMENT_LOG_UNIT_BEGIN_WITH_PRIVATE_DATA.outputToLocked(jsonWriter,
-                    SystemClock.uptimeMillis(), getWord(), getCorrectionType());
+                    SystemClock.uptimeMillis(), getWordsAsString(), getCorrectionType());
         } else {
             LOGSTATEMENT_LOG_UNIT_BEGIN_WITHOUT_PRIVATE_DATA.outputToLocked(jsonWriter,
                     SystemClock.uptimeMillis());
@@ -181,22 +202,22 @@
     }
 
     /**
-     * Mark the current logUnit as containing data to generate {@code word}.
+     * Mark the current logUnit as containing data to generate {@code newWords}.
      *
      * If {@code setWord()} was previously called for this LogUnit, then the method will try to
      * determine what kind of correction it is, and update its internal state of the correctionType
      * accordingly.
      *
-     * @param word The word this LogUnit generates.  Caller should not pass null or the empty
+     * @param newWords The words this LogUnit generates.  Caller should not pass null or the empty
      * string.
      */
-    public void setWord(final String word) {
-        if (hasWord()) {
+    public void setWords(final String newWords) {
+        if (hasOneOrMoreWords()) {
             // The word was already set once, and it is now being changed.  See if the new word
             // is close to the old word.  If so, then the change is probably a typo correction.
             // If not, the user may have decided to enter a different word, so flag it.
             if (mSuggestedWords != null) {
-                if (isInSuggestedWords(word, mSuggestedWords)) {
+                if (isInSuggestedWords(newWords, mSuggestedWords)) {
                     mCorrectionType = CORRECTIONTYPE_TYPO;
                 } else {
                     mCorrectionType = CORRECTIONTYPE_DIFFERENT_WORD;
@@ -206,38 +227,71 @@
                 // Mark it as a generic correction.
                 mCorrectionType = CORRECTIONTYPE_CORRECTION;
             }
+        } else {
+            mCorrectionType = CORRECTIONTYPE_NO_CORRECTION;
         }
-        mWord = word;
+        mWords = newWords;
+
+        // Update mWordArray
+        mWordArray = (TextUtils.isEmpty(mWords)) ? EMPTY_STRING_ARRAY
+                : WHITESPACE_PATTERN.split(mWords);
+        if (mWordArray.length > 0 && TextUtils.isEmpty(mWordArray[0])) {
+            // Empty string at beginning of array.  Must have been whitespace at the start of the
+            // word.  Remove the empty string.
+            mWordArray = Arrays.copyOfRange(mWordArray, 1, mWordArray.length);
+        }
     }
 
-    public String getWord() {
-        return mWord;
+    public String getWordsAsString() {
+        return mWords;
     }
 
-    public boolean hasWord() {
-        return mWord != null && !TextUtils.isEmpty(mWord.trim());
+    /**
+     * Retuns the words generated by the data in this LogUnit.
+     *
+     * The first word may be an empty string, if the data in the LogUnit started by generating
+     * whitespace.
+     *
+     * @return the array of words. an empty list of there are no words associated with this LogUnit.
+     */
+    public String[] getWordsAsStringArray() {
+        return mWordArray;
     }
 
+    public boolean hasOneOrMoreWords() {
+        return mWordArray.length >= 1;
+    }
+
+    public int getNumWords() {
+        return mWordArray.length;
+    }
+
+    // TODO: Refactor to eliminate getter/setters
     public void setMayContainDigit() {
         mMayContainDigit = true;
     }
 
+    // TODO: Refactor to eliminate getter/setters
     public boolean mayContainDigit() {
         return mMayContainDigit;
     }
 
+    // TODO: Refactor to eliminate getter/setters
     public void setContainsCorrection() {
         mContainsCorrection = true;
     }
 
+    // TODO: Refactor to eliminate getter/setters
     public boolean containsCorrection() {
         return mContainsCorrection;
     }
 
+    // TODO: Refactor to eliminate getter/setters
     public void setCorrectionType(final int correctionType) {
         mCorrectionType = correctionType;
     }
 
+    // TODO: Refactor to eliminate getter/setters
     public int getCorrectionType() {
         return mCorrectionType;
     }
@@ -267,7 +321,7 @@
                         new ArrayList<Object[]>(laterValues),
                         new ArrayList<Long>(laterTimes),
                         true /* isPartOfMegaword */);
-                newLogUnit.mWord = null;
+                newLogUnit.mWords = null;
                 newLogUnit.mMayContainDigit = mMayContainDigit;
                 newLogUnit.mContainsCorrection = mContainsCorrection;
 
@@ -287,9 +341,9 @@
         mLogStatementList.addAll(logUnit.mLogStatementList);
         mValuesList.addAll(logUnit.mValuesList);
         mTimeList.addAll(logUnit.mTimeList);
-        mWord = null;
-        if (logUnit.mWord != null) {
-            setWord(logUnit.mWord);
+        mWords = null;
+        if (logUnit.mWords != null) {
+            setWords(logUnit.mWords);
         }
         mMayContainDigit = mMayContainDigit || logUnit.mMayContainDigit;
         mContainsCorrection = mContainsCorrection || logUnit.mContainsCorrection;
diff --git a/java/src/com/android/inputmethod/research/MainLogBuffer.java b/java/src/com/android/inputmethod/research/MainLogBuffer.java
index 3303d2b..9aa3499 100644
--- a/java/src/com/android/inputmethod/research/MainLogBuffer.java
+++ b/java/src/com/android/inputmethod/research/MainLogBuffer.java
@@ -23,9 +23,9 @@
 import com.android.inputmethod.latin.Suggest;
 import com.android.inputmethod.latin.define.ProductionFlag;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.LinkedList;
-import java.util.Random;
 
 /**
  * MainLogBuffer is a FixedLogBuffer that tracks the state of LogUnits to make privacy guarantees.
@@ -100,10 +100,6 @@
         return mSuggest.getMainDictionary();
     }
 
-    public void resetWordCounter() {
-        mNumWordsUntilSafeToSample = mNumWordsBetweenNGrams;
-    }
-
     public void setIsStopping() {
         mIsStopping = true;
     }
@@ -131,10 +127,7 @@
             final int length = logUnits.size();
             for (int i = 0; i < length; i++) {
                 final LogUnit logUnit = logUnits.get(i);
-                final String word = logUnit.getWord();
-                if (word != null) {
-                    numWordsInLogUnitList++;
-                }
+                numWordsInLogUnitList += logUnit.getNumWords();
             }
             return numWordsInLogUnitList >= minNGramSize;
         }
@@ -158,32 +151,34 @@
         // the complete buffer contents in detail.
         int numWordsInLogUnitList = 0;
         final int length = logUnits.size();
-        for (int i = 0; i < length; i++) {
-            final LogUnit logUnit = logUnits.get(i);
-            if (!logUnit.hasWord()) {
+        for (final LogUnit logUnit : logUnits) {
+            if (!logUnit.hasOneOrMoreWords()) {
                 // Digits outside words are a privacy threat.
                 if (logUnit.mayContainDigit()) {
                     return false;
                 }
             } else {
-                numWordsInLogUnitList++;
-                final String word = logUnit.getWord();
-                // Words not in the dictionary are a privacy threat.
-                if (ResearchLogger.hasLetters(word) && !(dictionary.isValidWord(word))) {
-                    if (DEBUG) {
-                        Log.d(TAG, "NOT SAFE!: hasLetters: " + ResearchLogger.hasLetters(word)
-                                + ", isValid: " + (dictionary.isValidWord(word)));
+                numWordsInLogUnitList += logUnit.getNumWords();
+                final String[] words = logUnit.getWordsAsStringArray();
+                for (final String word : words) {
+                    // Words not in the dictionary are a privacy threat.
+                    if (ResearchLogger.hasLetters(word) && !(dictionary.isValidWord(word))) {
+                        if (DEBUG) {
+                            Log.d(TAG, "\"" + word + "\" NOT SAFE!: hasLetters: "
+                                    + ResearchLogger.hasLetters(word)
+                                    + ", isValid: " + (dictionary.isValidWord(word)));
+                        }
+                        return false;
                     }
-                    return false;
                 }
             }
         }
 
-        // Finally, only return true if the minNGramSize is met.
-        return numWordsInLogUnitList >= minNGramSize;
+        // Finally, only return true if the ngram is the right size.
+        return numWordsInLogUnitList == minNGramSize;
     }
 
-    public void shiftAndPublishAll() {
+    public void shiftAndPublishAll() throws IOException {
         final LinkedList<LogUnit> logUnits = getLogUnits();
         while (!logUnits.isEmpty()) {
             publishLogUnitsAtFrontOfBuffer();
@@ -192,23 +187,40 @@
 
     @Override
     protected final void onBufferFull() {
-        publishLogUnitsAtFrontOfBuffer();
+        try {
+            publishLogUnitsAtFrontOfBuffer();
+        } catch (final IOException e) {
+            if (DEBUG) {
+                Log.w(TAG, "IOException when publishing front of LogBuffer", e);
+            }
+        }
     }
 
-    protected final void publishLogUnitsAtFrontOfBuffer() {
+    protected final void publishLogUnitsAtFrontOfBuffer() throws IOException {
+        // TODO: Refactor this method to require fewer passes through the LogUnits.  Should really
+        // require only one pass.
         ArrayList<LogUnit> logUnits = peekAtFirstNWords(N_GRAM_SIZE);
         if (isSafeNGram(logUnits, N_GRAM_SIZE)) {
             // Good n-gram at the front of the buffer.  Publish it, disclosing details.
             publish(logUnits, true /* canIncludePrivateData */);
             shiftOutWords(N_GRAM_SIZE);
-            resetWordCounter();
-        } else {
-            // No good n-gram at front, and buffer is full.  Shift out the first word (or if there
-            // is none, the existing logUnits).
-            logUnits = peekAtFirstNWords(1);
-            publish(logUnits, false /* canIncludePrivateData */);
-            shiftOutWords(1);
+            mNumWordsUntilSafeToSample = mNumWordsBetweenNGrams;
+            return;
         }
+        // No good n-gram at front, and buffer is full.  Shift out up through the first logUnit
+        // with associated words (or if there is none, all the existing logUnits).
+        logUnits.clear();
+        LogUnit logUnit = shiftOut();
+        while (logUnit != null) {
+            logUnits.add(logUnit);
+            final int numWords = logUnit.getNumWords();
+            if (numWords > 0) {
+                mNumWordsUntilSafeToSample = Math.max(0, mNumWordsUntilSafeToSample - numWords);
+                break;
+            }
+            logUnit = shiftOut();
+        }
+        publish(logUnits, false /* canIncludePrivateData */);
     }
 
     /**
@@ -219,18 +231,19 @@
      * @param logUnits The list of logUnits to be published.
      * @param canIncludePrivateData Whether the private data in the logUnits can be included in
      * publication.
+     *
+     * @throws IOException if publication to the log file is not possible
      */
     protected abstract void publish(final ArrayList<LogUnit> logUnits,
-            final boolean canIncludePrivateData);
+            final boolean canIncludePrivateData) throws IOException;
 
     @Override
-    protected void shiftOutWords(final int numWords) {
-        final int oldNumActualWords = getNumActualWords();
-        super.shiftOutWords(numWords);
-        final int numWordsShifted = oldNumActualWords - getNumActualWords();
-        mNumWordsUntilSafeToSample -= numWordsShifted;
+    protected int shiftOutWords(final int numWords) {
+        final int numWordsShiftedOut = super.shiftOutWords(numWords);
+        mNumWordsUntilSafeToSample = Math.max(0, mNumWordsUntilSafeToSample - numWordsShiftedOut);
         if (DEBUG) {
             Log.d(TAG, "wordsUntilSafeToSample now at " + mNumWordsUntilSafeToSample);
         }
+        return numWordsShiftedOut;
     }
 }
diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java
index 35a491f..3e82139 100644
--- a/java/src/com/android/inputmethod/research/ResearchLog.java
+++ b/java/src/com/android/inputmethod/research/ResearchLog.java
@@ -25,6 +25,7 @@
 
 import java.io.BufferedWriter;
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
@@ -61,7 +62,11 @@
     /* package */ final File mFile;
     private final Context mContext;
 
-    private JsonWriter mJsonWriter = NULL_JSON_WRITER;
+    // Earlier implementations used a dummy JsonWriter that just swallowed what it was given, but
+    // this was tricky to do well, because JsonWriter throws an exception if it is passed more than
+    // one top-level object.
+    private JsonWriter mJsonWriter = null;
+
     // true if at least one byte of data has been written out to the log file.  This must be
     // remembered because JsonWriter requires that calls matching calls to beginObject and
     // endObject, as well as beginArray and endArray, and the file is opened lazily, only when
@@ -69,26 +74,6 @@
     // could be caught, but this might suppress other errors.
     private boolean mHasWrittenData = false;
 
-    private static final JsonWriter NULL_JSON_WRITER = new JsonWriter(
-            new OutputStreamWriter(new NullOutputStream()));
-    private static class NullOutputStream extends OutputStream {
-        /** {@inheritDoc} */
-        @Override
-        public void write(byte[] buffer, int offset, int count) {
-            // nop
-        }
-
-        /** {@inheritDoc} */
-        @Override
-        public void write(byte[] buffer) {
-            // nop
-        }
-
-        @Override
-        public void write(int oneByte) {
-        }
-    }
-
     public ResearchLog(final File outputFile, final Context context) {
         mExecutor = Executors.newSingleThreadScheduledExecutor();
         mFile = outputFile;
@@ -108,16 +93,21 @@
             @Override
             public Object call() throws Exception {
                 try {
-                    if (mHasWrittenData) {
-                        mJsonWriter.endArray();
-                        mHasWrittenData = false;
+                    if (mJsonWriter == null) return null;
+                    // TODO: This is necessary to avoid an exception.  Better would be to not even
+                    // open the JsonWriter if the file is not even opened unless there is valid data
+                    // to write.
+                    if (!mHasWrittenData) {
+                        mJsonWriter.beginArray();
                     }
+                    mJsonWriter.endArray();
+                    mHasWrittenData = false;
                     mJsonWriter.flush();
                     mJsonWriter.close();
                     if (DEBUG) {
-                        Log.d(TAG, "wrote log to " + mFile);
+                        Log.d(TAG, "closed " + mFile);
                     }
-                } catch (Exception e) {
+                } catch (final Exception e) {
                     Log.d(TAG, "error when closing ResearchLog:", e);
                 } finally {
                     // Marking the file as read-only signals that this log file is ready to be
@@ -158,7 +148,14 @@
             @Override
             public Object call() throws Exception {
                 try {
+                    if (mJsonWriter == null) return null;
                     if (mHasWrittenData) {
+                        // TODO: This is necessary to avoid an exception.  Better would be to not
+                        // even open the JsonWriter if the file is not even opened unless there is
+                        // valid data to write.
+                        if (!mHasWrittenData) {
+                            mJsonWriter.beginArray();
+                        }
                         mJsonWriter.endArray();
                         mJsonWriter.close();
                         mHasWrittenData = false;
@@ -207,7 +204,7 @@
     private final Callable<Object> mFlushCallable = new Callable<Object>() {
         @Override
         public Object call() throws Exception {
-            mJsonWriter.flush();
+            if (mJsonWriter != null) mJsonWriter.flush();
             return null;
         }
     };
@@ -253,30 +250,29 @@
     /**
      * Return a JsonWriter for this ResearchLog.  It is initialized the first time this method is
      * called.  The cached value is returned in future calls.
+     *
+     * @throws IOException if opening the JsonWriter is not possible
      */
-    public JsonWriter getInitializedJsonWriterLocked() {
-        if (mJsonWriter != NULL_JSON_WRITER || mFile == null) return mJsonWriter;
+    public JsonWriter getInitializedJsonWriterLocked() throws IOException {
+        if (mJsonWriter != null) return mJsonWriter;
+        if (mFile == null) throw new FileNotFoundException();
         try {
             final JsonWriter jsonWriter = createJsonWriter(mContext, mFile);
-            if (jsonWriter != null) {
-                jsonWriter.beginArray();
-                mJsonWriter = jsonWriter;
-                mHasWrittenData = true;
-            }
+            if (jsonWriter == null) throw new IOException("Could not create JsonWriter");
+
+            jsonWriter.beginArray();
+            mJsonWriter = jsonWriter;
+            mHasWrittenData = true;
+            return mJsonWriter;
         } catch (final IOException e) {
-            Log.w(TAG, "Error in JsonWriter; disabling logging", e);
-            try {
-                mJsonWriter.close();
-            } catch (final IllegalStateException e1) {
-                // Assume that this is just the json not being terminated properly.
-                // Ignore
-            } catch (final IOException e1) {
-                Log.w(TAG, "Error in closing JsonWriter; disabling logging", e1);
-            } finally {
-                mJsonWriter = NULL_JSON_WRITER;
+            if (DEBUG) {
+                Log.w(TAG, "Exception when creating JsonWriter", e);
+                Log.w(TAG, "Closing JsonWriter");
             }
+            if (mJsonWriter != null) mJsonWriter.close();
+            mJsonWriter = null;
+            throw e;
         }
-        return mJsonWriter;
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/research/ResearchLogDirectory.java b/java/src/com/android/inputmethod/research/ResearchLogDirectory.java
index 291dea5..d156068 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogDirectory.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogDirectory.java
@@ -97,15 +97,17 @@
         }
     }
 
-    public File getLogFilePath(final long time) {
-        return new File(mFilesDir, getUniqueFilename(LOG_FILENAME_PREFIX, time));
+    public File getLogFilePath(final long time, final long nanoTime) {
+        return new File(mFilesDir, getUniqueFilename(LOG_FILENAME_PREFIX, time, nanoTime));
     }
 
-    public File getUserRecordingFilePath(final long time) {
-        return new File(mFilesDir, getUniqueFilename(USER_RECORDING_FILENAME_PREFIX, time));
+    public File getUserRecordingFilePath(final long time, final long nanoTime) {
+        return new File(mFilesDir, getUniqueFilename(USER_RECORDING_FILENAME_PREFIX, time,
+                nanoTime));
     }
 
-    private static String getUniqueFilename(final String prefix, final long time) {
-        return prefix + "-" + time + FILENAME_SUFFIX;
+    private static String getUniqueFilename(final String prefix, final long time,
+            final long nanoTime) {
+        return prefix + "-" + time + "-" + nanoTime + FILENAME_SUFFIX;
     }
 }
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
index 7a23ddb..8b8ea21 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -118,7 +118,6 @@
     private static final boolean FEEDBACK_DIALOG_SHOULD_PRESERVE_TEXT_FIELD = false;
     /* package */ static boolean sIsLogging = false;
     private static final int OUTPUT_FORMAT_VERSION = 5;
-    private static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
     // Whether all words should be recorded, leaving unsampled word between bigrams.  Useful for
     // testing.
     /* package for test */ static final boolean IS_LOGGING_EVERYTHING = false
@@ -150,24 +149,18 @@
     private static final ResearchLogger sInstance = new ResearchLogger();
     private static String sAccountType = null;
     private static String sAllowedAccountDomain = null;
-    /* package */ ResearchLog mMainResearchLog;
+    private ResearchLog mMainResearchLog; // always non-null after init() is called
     // mFeedbackLog records all events for the session, private or not (excepting
     // passwords).  It is written to permanent storage only if the user explicitly commands
     // the system to do so.
     // LogUnits are queued in the LogBuffers and published to the ResearchLogs when words are
     // complete.
-    /* package */ MainLogBuffer mMainLogBuffer;
-    // TODO: Remove the feedback log.  The feedback log continuously captured user data in case the
-    // user wanted to submit it.  We now use the mUserRecordingLogBuffer to allow the user to
-    // explicitly reproduce a problem.
-    /* package */ ResearchLog mFeedbackLog;
-    /* package */ LogBuffer mFeedbackLogBuffer;
+    /* package for test */ MainLogBuffer mMainLogBuffer; // always non-null after init() is called
     /* package */ ResearchLog mUserRecordingLog;
     /* package */ LogBuffer mUserRecordingLogBuffer;
     private File mUserRecordingFile = null;
 
     private boolean mIsPasswordView = false;
-    private boolean mIsLoggingSuspended = false;
     private SharedPreferences mPrefs;
 
     // digits entered by the user are replaced with this codepoint.
@@ -202,15 +195,6 @@
     private long mSavedDownEventTime;
     private Bundle mFeedbackDialogBundle = null;
     private boolean mInFeedbackDialog = false;
-    // The feedback dialog causes stop() to be called for the keyboard connected to the original
-    // window.  This is because the feedback dialog must present its own EditText box that displays
-    // a keyboard.  stop() normally causes mFeedbackLogBuffer, which contains the user's data, to be
-    // cleared, and causes mFeedbackLog, which is ready to collect information in case the user
-    // wants to upload, to be closed.  This is good because we don't need to log information about
-    // what the user is typing in the feedback dialog, but bad because this data must be uploaded.
-    // Here we save the LogBuffer and Log so the feedback dialog can later access their data.
-    private LogBuffer mSavedFeedbackLogBuffer;
-    private ResearchLog mSavedFeedbackLog;
     private Handler mUserRecordingTimeoutHandler;
     private static final long USER_RECORDING_TIMEOUT_MS = 30L * DateUtils.SECOND_IN_MILLIS;
 
@@ -241,6 +225,9 @@
         mResearchLogDirectory = new ResearchLogDirectory(mLatinIME);
         cleanLogDirectoryIfNeeded(mResearchLogDirectory, System.currentTimeMillis());
 
+        // Initialize log buffers
+        resetLogBuffers();
+
         // Initialize external services
         mUploadIntent = new Intent(mLatinIME, UploaderService.class);
         mUploadNowIntent = new Intent(mLatinIME, UploaderService.class);
@@ -252,6 +239,35 @@
         mReplayer.setKeyboardSwitcher(keyboardSwitcher);
     }
 
+    private void resetLogBuffers() {
+        mMainResearchLog = new ResearchLog(mResearchLogDirectory.getLogFilePath(
+                System.currentTimeMillis(), System.nanoTime()), mLatinIME);
+        final int numWordsToIgnore = new Random().nextInt(NUMBER_OF_WORDS_BETWEEN_SAMPLES + 1);
+        mMainLogBuffer = new MainLogBuffer(NUMBER_OF_WORDS_BETWEEN_SAMPLES, numWordsToIgnore,
+                mSuggest) {
+            @Override
+            protected void publish(final ArrayList<LogUnit> logUnits,
+                    boolean canIncludePrivateData) {
+                canIncludePrivateData |= IS_LOGGING_EVERYTHING;
+                for (final LogUnit logUnit : logUnits) {
+                    if (DEBUG) {
+                        final String wordsString = logUnit.getWordsAsString();
+                        Log.d(TAG, "onPublish: '" + wordsString
+                                + "', hc: " + logUnit.containsCorrection()
+                                + ", cipd: " + canIncludePrivateData);
+                    }
+                    for (final String word : logUnit.getWordsAsStringArray()) {
+                        final Dictionary dictionary = getDictionary();
+                        mStatistics.recordWordEntered(
+                                dictionary != null && dictionary.isValidWord(word),
+                                logUnit.containsCorrection());
+                    }
+                }
+                publishLogUnits(logUnits, mMainResearchLog, canIncludePrivateData);
+            }
+        };
+    }
+
     private void cleanLogDirectoryIfNeeded(final ResearchLogDirectory researchLogDirectory,
             final long now) {
         final long lastCleanupTime = ResearchSettings.readResearchLastDirCleanupTime(mPrefs);
@@ -376,52 +392,9 @@
             Log.d(TAG, "start called");
         }
         maybeShowSplashScreen();
-        updateSuspendedState();
         requestIndicatorRedraw();
         mStatistics.reset();
         checkForEmptyEditor();
-        if (mFeedbackLogBuffer == null) {
-            resetFeedbackLogging();
-        }
-        if (!isAllowedToLog()) {
-            // Log.w(TAG, "not in usability mode; not logging");
-            return;
-        }
-        if (mMainLogBuffer == null) {
-            mMainResearchLog = new ResearchLog(mResearchLogDirectory.getLogFilePath(
-                    System.currentTimeMillis()), mLatinIME);
-            final int numWordsToIgnore = new Random().nextInt(NUMBER_OF_WORDS_BETWEEN_SAMPLES + 1);
-            mMainLogBuffer = new MainLogBuffer(NUMBER_OF_WORDS_BETWEEN_SAMPLES, numWordsToIgnore,
-                    mSuggest) {
-                @Override
-                protected void publish(final ArrayList<LogUnit> logUnits,
-                        boolean canIncludePrivateData) {
-                    canIncludePrivateData |= IS_LOGGING_EVERYTHING;
-                    final int length = logUnits.size();
-                    for (int i = 0; i < length; i++) {
-                        final LogUnit logUnit = logUnits.get(i);
-                        final String word = logUnit.getWord();
-                        if (word != null && word.length() > 0 && hasLetters(word)) {
-                            Log.d(TAG, "onPublish: " + word + ", hc: "
-                                    + logUnit.containsCorrection());
-                            final Dictionary dictionary = getDictionary();
-                            mStatistics.recordWordEntered(
-                                    dictionary != null && dictionary.isValidWord(word),
-                                    logUnit.containsCorrection());
-                        }
-                    }
-                    if (mMainResearchLog != null) {
-                        publishLogUnits(logUnits, mMainResearchLog, canIncludePrivateData);
-                    }
-                }
-            };
-        }
-    }
-
-    private void resetFeedbackLogging() {
-        mFeedbackLog = new ResearchLog(mResearchLogDirectory.getLogFilePath(
-                System.currentTimeMillis()), mLatinIME);
-        mFeedbackLogBuffer = new FixedLogBuffer(FEEDBACK_WORD_BUFFER_SIZE);
     }
 
     /* package */ void stop() {
@@ -431,35 +404,32 @@
         // Commit mCurrentLogUnit before closing.
         commitCurrentLogUnit();
 
-        if (mMainLogBuffer != null) {
+        try {
             mMainLogBuffer.shiftAndPublishAll();
-            logStatistics();
-            commitCurrentLogUnit();
-            mMainLogBuffer.setIsStopping();
+        } catch (final IOException e) {
+            Log.w(TAG, "IOException when publishing LogBuffer", e);
+        }
+        logStatistics();
+        commitCurrentLogUnit();
+        mMainLogBuffer.setIsStopping();
+        try {
             mMainLogBuffer.shiftAndPublishAll();
-            mMainResearchLog.blockingClose(RESEARCHLOG_CLOSE_TIMEOUT_IN_MS);
-            mMainLogBuffer = null;
+        } catch (final IOException e) {
+            Log.w(TAG, "IOException when publishing LogBuffer", e);
         }
-        if (mFeedbackLogBuffer != null) {
-            mFeedbackLog.blockingClose(RESEARCHLOG_CLOSE_TIMEOUT_IN_MS);
-            mFeedbackLogBuffer = null;
-        }
+        mMainResearchLog.blockingClose(RESEARCHLOG_CLOSE_TIMEOUT_IN_MS);
+
+        resetLogBuffers();
     }
 
     public void abort() {
         if (DEBUG) {
             Log.d(TAG, "abort called");
         }
-        if (mMainLogBuffer != null) {
-            mMainLogBuffer.clear();
-            mMainResearchLog.blockingAbort(RESEARCHLOG_ABORT_TIMEOUT_IN_MS);
-            mMainLogBuffer = null;
-        }
-        if (mFeedbackLogBuffer != null) {
-            mFeedbackLogBuffer.clear();
-            mFeedbackLog.blockingAbort(RESEARCHLOG_ABORT_TIMEOUT_IN_MS);
-            mFeedbackLogBuffer = null;
-        }
+        mMainLogBuffer.clear();
+        mMainResearchLog.blockingAbort(RESEARCHLOG_ABORT_TIMEOUT_IN_MS);
+
+        resetLogBuffers();
     }
 
     private void restart() {
@@ -467,23 +437,11 @@
         start();
     }
 
-    private long mResumeTime = 0L;
-    private void updateSuspendedState() {
-        final long time = System.currentTimeMillis();
-        if (time > mResumeTime) {
-            mIsLoggingSuspended = false;
-        }
-    }
-
     @Override
     public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
         if (key == null || prefs == null) {
             return;
         }
-        sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
-        if (sIsLogging == false) {
-            abort();
-        }
         requestIndicatorRedraw();
         mPrefs = prefs;
         prefsChanged(prefs);
@@ -503,12 +461,6 @@
             saveRecording();
         }
         mInFeedbackDialog = true;
-        mSavedFeedbackLogBuffer = mFeedbackLogBuffer;
-        mSavedFeedbackLog = mFeedbackLog;
-        // Set the non-saved versions to null so that the stop() caused by switching to the
-        // Feedback dialog will not close them.
-        mFeedbackLogBuffer = null;
-        mFeedbackLog = null;
 
         final Intent intent = new Intent();
         intent.setClass(mLatinIME, FeedbackActivity.class);
@@ -545,7 +497,7 @@
             mUserRecordingLog.blockingAbort(RESEARCHLOG_ABORT_TIMEOUT_IN_MS);
         }
         mUserRecordingFile = mResearchLogDirectory.getUserRecordingFilePath(
-                System.currentTimeMillis());
+                System.currentTimeMillis(), System.nanoTime());
         mUserRecordingLog = new ResearchLog(mUserRecordingFile, mLatinIME);
         mUserRecordingLogBuffer = new LogBuffer();
         resetRecordingTimer();
@@ -666,12 +618,6 @@
             new LogStatement("UserFeedback", false, false, "contents", "accountName", "recording");
     public void sendFeedback(final String feedbackContents, final boolean includeHistory,
             final boolean isIncludingAccountName, final boolean isIncludingRecording) {
-        if (mSavedFeedbackLogBuffer == null) {
-            return;
-        }
-        if (!includeHistory) {
-            mSavedFeedbackLogBuffer.clear();
-        }
         String recording = "";
         if (isIncludingRecording) {
             // Try to read recording from recently written json file
@@ -703,9 +649,13 @@
         final String accountName = isIncludingAccountName ? getAccountName() : "";
         feedbackLogUnit.addLogStatement(LOGSTATEMENT_FEEDBACK, SystemClock.uptimeMillis(),
                 feedbackContents, accountName, recording);
-        mFeedbackLogBuffer.shiftIn(feedbackLogUnit);
-        publishLogBuffer(mFeedbackLogBuffer, mSavedFeedbackLog, true /* isIncludingPrivateData */);
-        mSavedFeedbackLog.blockingClose(RESEARCHLOG_CLOSE_TIMEOUT_IN_MS);
+
+        final ResearchLog feedbackLog = new ResearchLog(mResearchLogDirectory.getLogFilePath(
+                System.currentTimeMillis(), System.nanoTime()), mLatinIME);
+        final LogBuffer feedbackLogBuffer = new LogBuffer();
+        feedbackLogBuffer.shiftIn(feedbackLogUnit);
+        publishLogBuffer(feedbackLogBuffer, feedbackLog, true /* isIncludingPrivateData */);
+        feedbackLog.blockingClose(RESEARCHLOG_CLOSE_TIMEOUT_IN_MS);
         uploadNow();
 
         if (isIncludingRecording && DEBUG_REPLAY_AFTER_FEEDBACK) {
@@ -744,8 +694,8 @@
 
     public void initSuggest(final Suggest suggest) {
         mSuggest = suggest;
-        // MainLogBuffer has out-of-date Suggest object.  Need to close it down and create a new
-        // one.
+        // MainLogBuffer now has an out-of-date Suggest object.  Close down MainLogBuffer and create
+        // a new one.
         if (mMainLogBuffer != null) {
             stop();
             start();
@@ -764,7 +714,7 @@
     }
 
     private boolean isAllowedToLog() {
-        return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging && !mInFeedbackDialog;
+        return !mIsPasswordView && sIsLogging && !mInFeedbackDialog;
     }
 
     public void requestIndicatorRedraw() {
@@ -813,7 +763,7 @@
                 // enabled.  The dot is actually a zero-width, zero-height rectangle, placed at the
                 // lower-right corner of the canvas, painted with a non-zero border width.
                 paint.setStrokeWidth(3);
-                canvas.drawRect(width, height, width, height, paint);
+                canvas.drawRect(width - 1, height - 1, width, height, paint);
             }
             paint.setColor(savedColor);
             paint.setStyle(savedStyle);
@@ -852,16 +802,11 @@
 
     /* package for test */ void commitCurrentLogUnit() {
         if (DEBUG) {
-            Log.d(TAG, "commitCurrentLogUnit" + (mCurrentLogUnit.hasWord() ?
-                    ": " + mCurrentLogUnit.getWord() : ""));
+            Log.d(TAG, "commitCurrentLogUnit" + (mCurrentLogUnit.hasOneOrMoreWords() ?
+                    ": " + mCurrentLogUnit.getWordsAsString() : ""));
         }
         if (!mCurrentLogUnit.isEmpty()) {
-            if (mMainLogBuffer != null) {
-                mMainLogBuffer.shiftIn(mCurrentLogUnit);
-            }
-            if (mFeedbackLogBuffer != null) {
-                mFeedbackLogBuffer.shiftIn(mCurrentLogUnit);
-            }
+            mMainLogBuffer.shiftIn(mCurrentLogUnit);
             if (mUserRecordingLogBuffer != null) {
                 mUserRecordingLogBuffer.shiftIn(mCurrentLogUnit);
             }
@@ -886,15 +831,12 @@
         //
         // Note that we don't use mLastLogUnit here, because it only goes one word back and is only
         // needed for reverts, which only happen one back.
-        if (mMainLogBuffer == null) {
-            return;
-        }
         final LogUnit oldLogUnit = mMainLogBuffer.peekLastLogUnit();
 
         // Check that expected word matches.
         if (oldLogUnit != null) {
-            final String oldLogUnitWord = oldLogUnit.getWord();
-            if (!oldLogUnitWord.equals(expectedWord)) {
+            final String oldLogUnitWords = oldLogUnit.getWordsAsString();
+            if (oldLogUnitWords != null && !oldLogUnitWords.equals(expectedWord)) {
                 return;
             }
         }
@@ -910,13 +852,11 @@
         } else {
             mCurrentLogUnit = oldLogUnit;
         }
-        if (mFeedbackLogBuffer != null) {
-            mFeedbackLogBuffer.unshiftIn();
-        }
         enqueueEvent(LOGSTATEMENT_UNCOMMIT_CURRENT_LOGUNIT);
         if (DEBUG) {
             Log.d(TAG, "uncommitCurrentLogUnit (dump=" + dumpCurrentLogUnit + ") back to "
-                    + (mCurrentLogUnit.hasWord() ? ": '" + mCurrentLogUnit.getWord() + "'" : ""));
+                    + (mCurrentLogUnit.hasOneOrMoreWords() ? ": '"
+                        + mCurrentLogUnit.getWordsAsString() + "'" : ""));
         }
     }
 
@@ -941,6 +881,7 @@
             final ResearchLog researchLog, final boolean canIncludePrivateData) {
         final LogUnit openingLogUnit = new LogUnit();
         if (logUnits.isEmpty()) return;
+        if (!isAllowedToLog()) return;
         // LogUnits not containing private data, such as contextual data for the log, do not require
         // logSegment boundary statements.
         if (canIncludePrivateData) {
@@ -950,8 +891,9 @@
         }
         for (LogUnit logUnit : logUnits) {
             if (DEBUG) {
-                Log.d(TAG, "publishLogBuffer: " + (logUnit.hasWord() ? logUnit.getWord()
-                        : "<wordless>") + ", correction?: " + logUnit.containsCorrection());
+                Log.d(TAG, "publishLogBuffer: " + (logUnit.hasOneOrMoreWords()
+                        ? logUnit.getWordsAsString() : "<wordless>")
+                        + ", correction?: " + logUnit.containsCorrection());
             }
             researchLog.publish(logUnit, canIncludePrivateData);
         }
@@ -986,7 +928,7 @@
             return;
         }
         if (word.length() > 0 && hasLetters(word)) {
-            mCurrentLogUnit.setWord(word);
+            mCurrentLogUnit.setWords(word);
         }
         final LogUnit newLogUnit = mCurrentLogUnit.splitByTime(maxTime);
         enqueueCommitText(word, isBatchMode);
@@ -1107,7 +1049,7 @@
             packageInfo = mLatinIME.getPackageManager().getPackageInfo(mLatinIME.getPackageName(),
                     0);
             final String versionName = packageInfo.versionName;
-            return !(developerBuildRegex.matcher(versionName).find());
+            return developerBuildRegex.matcher(versionName).find();
         } catch (final NameNotFoundException e) {
             Log.e(TAG, "Could not determine package name", e);
             return false;
@@ -1373,11 +1315,7 @@
     public static void latinIME_promotePhantomSpace() {
         final ResearchLogger researchLogger = getInstance();
         final LogUnit logUnit;
-        if (researchLogger.mMainLogBuffer == null) {
-            logUnit = researchLogger.mCurrentLogUnit;
-        } else {
-            logUnit = researchLogger.mMainLogBuffer.peekLastLogUnit();
-        }
+        logUnit = researchLogger.mMainLogBuffer.peekLastLogUnit();
         researchLogger.enqueueEvent(logUnit, LOGSTATEMENT_LATINIME_PROMOTEPHANTOMSPACE);
     }
 
@@ -1394,11 +1332,7 @@
             final String charactersAfterSwap) {
         final ResearchLogger researchLogger = getInstance();
         final LogUnit logUnit;
-        if (researchLogger.mMainLogBuffer == null) {
-            logUnit = null;
-        } else {
-            logUnit = researchLogger.mMainLogBuffer.peekLastLogUnit();
-        }
+        logUnit = researchLogger.mMainLogBuffer.peekLastLogUnit();
         if (logUnit != null) {
             researchLogger.enqueueEvent(logUnit, LOGSTATEMENT_LATINIME_SWAPSWAPPERANDSPACE,
                     originalCharacters, charactersAfterSwap);
@@ -1471,14 +1405,10 @@
         final ResearchLogger researchLogger = getInstance();
         // TODO: Verify that mCurrentLogUnit has been restored and contains the reverted word.
         final LogUnit logUnit;
-        if (researchLogger.mMainLogBuffer == null) {
-            logUnit = null;
-        } else {
-            logUnit = researchLogger.mMainLogBuffer.peekLastLogUnit();
-        }
+        logUnit = researchLogger.mMainLogBuffer.peekLastLogUnit();
         if (originallyTypedWord.length() > 0 && hasLetters(originallyTypedWord)) {
             if (logUnit != null) {
-                logUnit.setWord(originallyTypedWord);
+                logUnit.setWords(originallyTypedWord);
             }
         }
         researchLogger.enqueueEvent(logUnit != null ? logUnit : researchLogger.mCurrentLogUnit,
@@ -1616,7 +1546,7 @@
      * Log a call to LatinIME.commitCurrentAutoCorrection().
      *
      * SystemResponse: The IME has committed an auto-correction.  An auto-correction changes the raw
-     * text input to another word that the user more likely desired to type.
+     * text input to another word (or words) that the user more likely desired to type.
      */
     private static final LogStatement LOGSTATEMENT_LATINIME_COMMITCURRENTAUTOCORRECTION =
             new LogStatement("LatinIMECommitCurrentAutoCorrection", true, true, "typedWord",
@@ -1826,6 +1756,9 @@
     public static void latinIME_onEndBatchInput(final CharSequence enteredText,
             final int enteredWordPos, final SuggestedWords suggestedWords) {
         final ResearchLogger researchLogger = getInstance();
+        if (!TextUtils.isEmpty(enteredText) && hasLetters(enteredText.toString())) {
+            researchLogger.mCurrentLogUnit.setWords(enteredText.toString());
+        }
         researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_ONENDBATCHINPUT, enteredText,
                 enteredWordPos);
         researchLogger.mCurrentLogUnit.initializeSuggestions(suggestedWords);
diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java
index 6a9717b..d2db349 100644
--- a/java/src/com/android/inputmethod/research/UploaderService.java
+++ b/java/src/com/android/inputmethod/research/UploaderService.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.SystemClock;
 
 import com.android.inputmethod.latin.define.ProductionFlag;
 
@@ -79,28 +80,14 @@
      */
     public static void cancelAndRescheduleUploadingService(final Context context,
             final boolean needsRescheduling) {
-        final PendingIntent pendingIntent = getPendingIntentForService(context);
+        final Intent intent = new Intent(context, UploaderService.class);
+        final PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0);
         final AlarmManager alarmManager = (AlarmManager) context.getSystemService(
                 Context.ALARM_SERVICE);
-        cancelAnyScheduledServiceAlarm(alarmManager, pendingIntent);
-        if (needsRescheduling) {
-            scheduleServiceAlarm(alarmManager, pendingIntent);
-        }
-    }
-
-    private static PendingIntent getPendingIntentForService(final Context context) {
-        final Intent intent = new Intent(context, UploaderService.class);
-        return PendingIntent.getService(context, 0, intent, 0);
-    }
-
-    private static void cancelAnyScheduledServiceAlarm(final AlarmManager alarmManager,
-            final PendingIntent pendingIntent) {
         alarmManager.cancel(pendingIntent);
-    }
-
-    private static void scheduleServiceAlarm(final AlarmManager alarmManager,
-            final PendingIntent pendingIntent) {
-        alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, UploaderService.RUN_INTERVAL,
-                pendingIntent);
+        if (needsRescheduling) {
+            alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()
+                    + UploaderService.RUN_INTERVAL, pendingIntent);
+        }
     }
 }
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index 11fa3da..1dd68ea 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -109,7 +109,8 @@
     }
     Dictionary *dictionary = 0;
     if (BinaryFormat::UNKNOWN_FORMAT
-            == BinaryFormat::detectFormat(static_cast<uint8_t *>(dictBuf))) {
+            == BinaryFormat::detectFormat(static_cast<uint8_t *>(dictBuf),
+                    static_cast<int>(dictSize))) {
         AKLOGE("DICT: dictionary format is unknown, bad magic number");
 #ifdef USE_MMAP_FOR_DICTIONARY
         releaseDictBuf(static_cast<const char *>(dictBuf) - adjust, adjDictSize, fd);
diff --git a/native/jni/src/bigram_dictionary.cpp b/native/jni/src/bigram_dictionary.cpp
index 9289038..9053e72 100644
--- a/native/jni/src/bigram_dictionary.cpp
+++ b/native/jni/src/bigram_dictionary.cpp
@@ -187,7 +187,7 @@
                 &pos);
         (*map)[bigramPos] = probability;
         setInFilter(filter, bigramPos);
-    } while (0 != (BinaryFormat::FLAG_ATTRIBUTE_HAS_NEXT & bigramFlags));
+    } while (BinaryFormat::FLAG_ATTRIBUTE_HAS_NEXT & bigramFlags);
 }
 
 bool BigramDictionary::checkFirstCharacter(int *word, int *inputCodePoints) const {
diff --git a/native/jni/src/binary_format.h b/native/jni/src/binary_format.h
index 2d2e195..9824153 100644
--- a/native/jni/src/binary_format.h
+++ b/native/jni/src/binary_format.h
@@ -23,6 +23,7 @@
 
 #include "bloom_filter.h"
 #include "char_utils.h"
+#include "hash_map_compat.h"
 
 namespace latinime {
 
@@ -63,12 +64,14 @@
     static const int UNKNOWN_FORMAT = -1;
     static const int SHORTCUT_LIST_SIZE_SIZE = 2;
 
-    static int detectFormat(const uint8_t *const dict);
-    static int getHeaderSize(const uint8_t *const dict);
-    static int getFlags(const uint8_t *const dict);
-    static void readHeaderValue(const uint8_t *const dict, const char *const key, int *outValue,
-            const int outValueSize);
-    static int readHeaderValueInt(const uint8_t *const dict, const char *const key);
+    static int detectFormat(const uint8_t *const dict, const int dictSize);
+    static int getHeaderSize(const uint8_t *const dict, const int dictSize);
+    static int getFlags(const uint8_t *const dict, const int dictSize);
+    static bool hasBlacklistedOrNotAWordFlag(const int flags);
+    static void readHeaderValue(const uint8_t *const dict, const int dictSize,
+            const char *const key, int *outValue, const int outValueSize);
+    static int readHeaderValueInt(const uint8_t *const dict, const int dictSize,
+            const char *const key);
     static int getGroupCountAndForwardPointer(const uint8_t *const dict, int *pos);
     static uint8_t getFlagsAndForwardPointer(const uint8_t *const dict, int *pos);
     static int getCodePointAndForwardPointer(const uint8_t *const dict, int *pos);
@@ -92,7 +95,13 @@
             const int unigramProbability, const int bigramProbability);
     static int getProbability(const int position, const std::map<int, int> *bigramMap,
             const uint8_t *bigramFilter, const int unigramProbability);
-    static float getMultiWordCostMultiplier(const uint8_t *const dict);
+    static int getBigramProbabilityFromHashMap(const int position,
+            const hash_map_compat<int, int> *bigramMap, const int unigramProbability);
+    static float getMultiWordCostMultiplier(const uint8_t *const dict, const int dictSize);
+    static void fillBigramProbabilityToHashMap(const uint8_t *const root, int position,
+            hash_map_compat<int, int> *bigramMap);
+    static int getBigramProbability(const uint8_t *const root, int position,
+            const int nextPosition, const int unigramProbability);
 
     // Flags for special processing
     // Those *must* match the flags in makedict (BinaryDictInputOutput#*_PROCESSING_FLAG) or
@@ -104,6 +113,8 @@
 
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(BinaryFormat);
+    static int getBigramListPositionForWordPosition(const uint8_t *const root, int position);
+
     static const int FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = 0x00;
     static const int FLAG_GROUP_ADDRESS_TYPE_ONEBYTE = 0x40;
     static const int FLAG_GROUP_ADDRESS_TYPE_TWOBYTES = 0x80;
@@ -112,6 +123,8 @@
     static const int FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES = 0x20;
     static const int FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES = 0x30;
 
+    // Any file smaller than this is not a dictionary.
+    static const int DICTIONARY_MINIMUM_SIZE = 4;
     // Originally, format version 1 had a 16-bit magic number, then the version number `01'
     // then options that must be 0. Hence the first 32-bits of the format are always as follow
     // and it's okay to consider them a magic number as a whole.
@@ -121,6 +134,8 @@
     // number, so we had to change it so that version 2 files would be rejected by older
     // implementations. On this occasion, we made the magic number 32 bits long.
     static const int FORMAT_VERSION_2_MAGIC_NUMBER = -1681835266; // 0x9BC13AFE
+    // Magic number (4 bytes), version (2 bytes), options (2 bytes), header size (4 bytes) = 12
+    static const int FORMAT_VERSION_2_MINIMUM_SIZE = 12;
 
     static const int CHARACTER_ARRAY_TERMINATOR_SIZE = 1;
     static const int MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20;
@@ -131,8 +146,11 @@
     static int skipBigrams(const uint8_t *const dict, const uint8_t flags, const int pos);
 };
 
-AK_FORCE_INLINE int BinaryFormat::detectFormat(const uint8_t *const dict) {
+AK_FORCE_INLINE int BinaryFormat::detectFormat(const uint8_t *const dict, const int dictSize) {
     // The magic number is stored big-endian.
+    // If the dictionary is less than 4 bytes, we can't even read the magic number, so we don't
+    // understand this format.
+    if (dictSize < DICTIONARY_MINIMUM_SIZE) return UNKNOWN_FORMAT;
     const int magicNumber = (dict[0] << 24) + (dict[1] << 16) + (dict[2] << 8) + dict[3];
     switch (magicNumber) {
     case FORMAT_VERSION_1_MAGIC_NUMBER:
@@ -142,6 +160,10 @@
         // Options (2 bytes) must be 0x00 0x00
         return 1;
     case FORMAT_VERSION_2_MAGIC_NUMBER:
+        // Version 2 dictionaries are at least 12 bytes long (see below details for the header).
+        // If this dictionary has the version 2 magic number but is less than 12 bytes long, then
+        // it's an unknown format and we need to avoid confidently reading the next bytes.
+        if (dictSize < FORMAT_VERSION_2_MINIMUM_SIZE) return UNKNOWN_FORMAT;
         // Format 2 header is as follows:
         // Magic number (4 bytes) 0x9B 0xC1 0x3A 0xFE
         // Version number (2 bytes) 0x00 0x02
@@ -153,8 +175,8 @@
     }
 }
 
-inline int BinaryFormat::getFlags(const uint8_t *const dict) {
-    switch (detectFormat(dict)) {
+inline int BinaryFormat::getFlags(const uint8_t *const dict, const int dictSize) {
+    switch (detectFormat(dict, dictSize)) {
     case 1:
         return NO_FLAGS; // TODO: NO_FLAGS is unused anywhere else?
     default:
@@ -162,8 +184,12 @@
     }
 }
 
-inline int BinaryFormat::getHeaderSize(const uint8_t *const dict) {
-    switch (detectFormat(dict)) {
+inline bool BinaryFormat::hasBlacklistedOrNotAWordFlag(const int flags) {
+    return (flags & (FLAG_IS_BLACKLISTED | FLAG_IS_NOT_A_WORD)) != 0;
+}
+
+inline int BinaryFormat::getHeaderSize(const uint8_t *const dict, const int dictSize) {
+    switch (detectFormat(dict, dictSize)) {
     case 1:
         return FORMAT_VERSION_1_HEADER_SIZE;
     case 2:
@@ -174,12 +200,12 @@
     }
 }
 
-inline void BinaryFormat::readHeaderValue(const uint8_t *const dict, const char *const key,
-        int *outValue, const int outValueSize) {
+inline void BinaryFormat::readHeaderValue(const uint8_t *const dict, const int dictSize,
+        const char *const key, int *outValue, const int outValueSize) {
     int outValueIndex = 0;
     // Only format 2 and above have header attributes as {key,value} string pairs. For prior
     // formats, we just return an empty string, as if the key wasn't found.
-    if (2 <= detectFormat(dict)) {
+    if (2 <= detectFormat(dict, dictSize)) {
         const int headerOptionsOffset = 4 /* magic number */
                 + 2 /* dictionary version */ + 2 /* flags */;
         const int headerSize =
@@ -222,11 +248,12 @@
     if (outValueIndex >= 0) outValue[outValueIndex] = 0;
 }
 
-inline int BinaryFormat::readHeaderValueInt(const uint8_t *const dict, const char *const key) {
+inline int BinaryFormat::readHeaderValueInt(const uint8_t *const dict, const int dictSize,
+        const char *const key) {
     const int bufferSize = LARGEST_INT_DIGIT_COUNT;
     int intBuffer[bufferSize];
     char charBuffer[bufferSize];
-    BinaryFormat::readHeaderValue(dict, key, intBuffer, bufferSize);
+    BinaryFormat::readHeaderValue(dict, dictSize, key, intBuffer, bufferSize);
     for (int i = 0; i < bufferSize; ++i) {
         charBuffer[i] = intBuffer[i];
     }
@@ -242,8 +269,10 @@
     return ((msb & 0x7F) << 8) | dict[(*pos)++];
 }
 
-inline float BinaryFormat::getMultiWordCostMultiplier(const uint8_t *const dict) {
-    const int headerValue = readHeaderValueInt(dict, "MULTIPLE_WORDS_DEMOTION_RATE");
+inline float BinaryFormat::getMultiWordCostMultiplier(const uint8_t *const dict,
+        const int dictSize) {
+    const int headerValue = readHeaderValueInt(dict, dictSize,
+            "MULTIPLE_WORDS_DEMOTION_RATE");
     if (headerValue == S_INT_MIN) {
         return 1.0f;
     }
@@ -682,5 +711,68 @@
     }
     return backoff(unigramProbability);
 }
+
+// This returns a probability in log space.
+inline int BinaryFormat::getBigramProbabilityFromHashMap(const int position,
+        const hash_map_compat<int, int> *bigramMap, const int unigramProbability) {
+    if (!bigramMap) return backoff(unigramProbability);
+    const hash_map_compat<int, int>::const_iterator bigramProbabilityIt = bigramMap->find(position);
+    if (bigramProbabilityIt != bigramMap->end()) {
+        const int bigramProbability = bigramProbabilityIt->second;
+        return computeProbabilityForBigram(unigramProbability, bigramProbability);
+    }
+    return backoff(unigramProbability);
+}
+
+AK_FORCE_INLINE void BinaryFormat::fillBigramProbabilityToHashMap(
+        const uint8_t *const root, int position, hash_map_compat<int, int> *bigramMap) {
+    position = getBigramListPositionForWordPosition(root, position);
+    if (0 == position) return;
+
+    uint8_t bigramFlags;
+    do {
+        bigramFlags = getFlagsAndForwardPointer(root, &position);
+        const int probability = MASK_ATTRIBUTE_PROBABILITY & bigramFlags;
+        const int bigramPos = getAttributeAddressAndForwardPointer(root, bigramFlags,
+                &position);
+        (*bigramMap)[bigramPos] = probability;
+    } while (FLAG_ATTRIBUTE_HAS_NEXT & bigramFlags);
+}
+
+AK_FORCE_INLINE int BinaryFormat::getBigramProbability(const uint8_t *const root, int position,
+        const int nextPosition, const int unigramProbability) {
+    position = getBigramListPositionForWordPosition(root, position);
+    if (0 == position) return backoff(unigramProbability);
+
+    uint8_t bigramFlags;
+    do {
+        bigramFlags = getFlagsAndForwardPointer(root, &position);
+        const int bigramPos = getAttributeAddressAndForwardPointer(
+                root, bigramFlags, &position);
+        if (bigramPos == nextPosition) {
+            const int bigramProbability = MASK_ATTRIBUTE_PROBABILITY & bigramFlags;
+            return computeProbabilityForBigram(unigramProbability, bigramProbability);
+        }
+    } while (FLAG_ATTRIBUTE_HAS_NEXT & bigramFlags);
+    return backoff(unigramProbability);
+}
+
+// Returns a pointer to the start of the bigram list.
+AK_FORCE_INLINE int BinaryFormat::getBigramListPositionForWordPosition(
+        const uint8_t *const root, int position) {
+    if (NOT_VALID_WORD == position) return 0;
+    const uint8_t flags = getFlagsAndForwardPointer(root, &position);
+    if (!(flags & FLAG_HAS_BIGRAMS)) return 0;
+    if (flags & FLAG_HAS_MULTIPLE_CHARS) {
+        position = skipOtherCharacters(root, position);
+    } else {
+        getCodePointAndForwardPointer(root, &position);
+    }
+    position = skipProbability(flags, position);
+    position = skipChildrenPosition(flags, position);
+    position = skipShortcuts(root, flags, position);
+    return position;
+}
+
 } // namespace latinime
 #endif // LATINIME_BINARY_FORMAT_H
diff --git a/native/jni/src/correction.cpp b/native/jni/src/correction.cpp
index 0c65939..61bf3f6 100644
--- a/native/jni/src/correction.cpp
+++ b/native/jni/src/correction.cpp
@@ -23,6 +23,8 @@
 #include "defines.h"
 #include "proximity_info_state.h"
 #include "suggest_utils.h"
+#include "suggest/policyimpl/utils/edit_distance.h"
+#include "suggest/policyimpl/utils/damerau_levenshtein_edit_distance_policy.h"
 
 namespace latinime {
 
@@ -906,50 +908,11 @@
     return totalFreq;
 }
 
-/* Damerau-Levenshtein distance */
-inline static int editDistanceInternal(int *editDistanceTable, const int *before,
-        const int beforeLength, const int *after, const int afterLength) {
-    // dp[li][lo] dp[a][b] = dp[ a * lo + b]
-    int *dp = editDistanceTable;
-    const int li = beforeLength + 1;
-    const int lo = afterLength + 1;
-    for (int i = 0; i < li; ++i) {
-        dp[lo * i] = i;
-    }
-    for (int i = 0; i < lo; ++i) {
-        dp[i] = i;
-    }
-
-    for (int i = 0; i < li - 1; ++i) {
-        for (int j = 0; j < lo - 1; ++j) {
-            const int ci = toBaseLowerCase(before[i]);
-            const int co = toBaseLowerCase(after[j]);
-            const int cost = (ci == co) ? 0 : 1;
-            dp[(i + 1) * lo + (j + 1)] = min(dp[i * lo + (j + 1)] + 1,
-                    min(dp[(i + 1) * lo + j] + 1, dp[i * lo + j] + cost));
-            if (i > 0 && j > 0 && ci == toBaseLowerCase(after[j - 1])
-                    && co == toBaseLowerCase(before[i - 1])) {
-                dp[(i + 1) * lo + (j + 1)] = min(
-                        dp[(i + 1) * lo + (j + 1)], dp[(i - 1) * lo + (j - 1)] + cost);
-            }
-        }
-    }
-
-    if (DEBUG_EDIT_DISTANCE) {
-        AKLOGI("IN = %d, OUT = %d", beforeLength, afterLength);
-        for (int i = 0; i < li; ++i) {
-            for (int j = 0; j < lo; ++j) {
-                AKLOGI("EDIT[%d][%d], %d", i, j, dp[i * lo + j]);
-            }
-        }
-    }
-    return dp[li * lo - 1];
-}
-
 /* static */ int Correction::RankingAlgorithm::editDistance(const int *before,
         const int beforeLength, const int *after, const int afterLength) {
-    int table[(beforeLength + 1) * (afterLength + 1)];
-    return editDistanceInternal(table, before, beforeLength, after, afterLength);
+    const DamerauLevenshteinEditDistancePolicy daemaruLevenshtein(
+            before, beforeLength, after, afterLength);
+    return static_cast<int>(EditDistance::getEditDistance(&daemaruLevenshtein));
 }
 
 
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index 6ef9f41..eb59744 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -379,6 +379,15 @@
 #error "BIGRAM_FILTER_MODULO is larger than BIGRAM_FILTER_BYTE_SIZE"
 #endif
 
+// Max number of bigram maps (previous word contexts) to be cached. Increasing this number could
+// improve bigram lookup speed for multi-word suggestions, but at the cost of more memory usage.
+// Also, there are diminishing returns since the most frequently used bigrams are typically near
+// the beginning of the input and are thus the first ones to be cached. Note that these bigrams
+// are reset for each new composing word.
+#define MAX_CACHED_PREV_WORDS_IN_BIGRAM_MAP 25
+// Most common previous word contexts currently have 100 bigrams
+#define DEFAULT_HASH_MAP_SIZE_FOR_EACH_BIGRAM_MAP 100
+
 template<typename T> AK_FORCE_INLINE const T &min(const T &a, const T &b) { return a < b ? a : b; }
 template<typename T> AK_FORCE_INLINE const T &max(const T &a, const T &b) { return a > b ? a : b; }
 
@@ -417,16 +426,45 @@
 } DoubleLetterLevel;
 
 typedef enum {
+    // Correction for MATCH_CHAR
     CT_MATCH,
+    // Correction for PROXIMITY_CHAR
     CT_PROXIMITY,
+    // Correction for ADDITIONAL_PROXIMITY_CHAR
     CT_ADDITIONAL_PROXIMITY,
+    // Correction for SUBSTITUTION_CHAR
     CT_SUBSTITUTION,
+    // Skip one omitted letter
     CT_OMISSION,
+    // Delete an unnecessarily inserted letter
     CT_INSERTION,
+    // Swap the order of next two touch points
     CT_TRANSPOSITION,
     CT_COMPLETION,
     CT_TERMINAL,
+    // Create new word with space omission
     CT_NEW_WORD_SPACE_OMITTION,
+    // Create new word with space substitution
     CT_NEW_WORD_SPACE_SUBSTITUTION,
 } CorrectionType;
+
+// ErrorType is mainly decided by CorrectionType but it is also depending on if
+// the correction has really been performed or not.
+typedef enum {
+    // Substitution, omission and transposition
+    ET_EDIT_CORRECTION,
+    // Proximity error
+    ET_PROXIMITY_CORRECTION,
+    // Completion
+    ET_COMPLETION,
+    // New word
+    // TODO: Remove.
+    // A new word error should be an edit correction error or a proximity correction error.
+    ET_NEW_WORD,
+    // Treat error as an intentional omission when the CorrectionType is omission and the node can
+    // be intentional omission.
+    ET_INTENTIONAL_OMISSION,
+    // Not treated as an error. Tracked for checking exact match
+    ET_NOT_AN_ERROR
+} ErrorType;
 #endif // LATINIME_DEFINES_H
diff --git a/native/jni/src/dictionary.cpp b/native/jni/src/dictionary.cpp
index c998c06..dadb2ba 100644
--- a/native/jni/src/dictionary.cpp
+++ b/native/jni/src/dictionary.cpp
@@ -34,9 +34,11 @@
 
 Dictionary::Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust)
         : mDict(static_cast<unsigned char *>(dict)),
-          mOffsetDict((static_cast<unsigned char *>(dict)) + BinaryFormat::getHeaderSize(mDict)),
+          mOffsetDict((static_cast<unsigned char *>(dict))
+                  + BinaryFormat::getHeaderSize(mDict, dictSize)),
           mDictSize(dictSize), mMmapFd(mmapFd), mDictBufAdjust(dictBufAdjust),
-          mUnigramDictionary(new UnigramDictionary(mOffsetDict, BinaryFormat::getFlags(mDict))),
+          mUnigramDictionary(new UnigramDictionary(mOffsetDict,
+                  BinaryFormat::getFlags(mDict, dictSize))),
           mBigramDictionary(new BigramDictionary(mOffsetDict)),
           mGestureSuggest(new Suggest(GestureSuggestPolicyFactory::getGestureSuggestPolicy())),
           mTypingSuggest(new Suggest(TypingSuggestPolicyFactory::getTypingSuggestPolicy())) {
diff --git a/native/jni/src/dictionary.h b/native/jni/src/dictionary.h
index 0653d3c..2ad5b6c 100644
--- a/native/jni/src/dictionary.h
+++ b/native/jni/src/dictionary.h
@@ -31,6 +31,7 @@
 class Dictionary {
  public:
     // Taken from SuggestedWords.java
+    static const int KIND_MASK_KIND = 0xFF; // Mask to get only the kind
     static const int KIND_TYPED = 0; // What user typed
     static const int KIND_CORRECTION = 1; // Simple correction/suggestion
     static const int KIND_COMPLETION = 2; // Completion (suggestion with appended chars)
@@ -41,6 +42,10 @@
     static const int KIND_SHORTCUT = 7; // A shortcut
     static const int KIND_PREDICTION = 8; // A prediction (== a suggestion with no input)
 
+    static const int KIND_MASK_FLAGS = 0xFFFFFF00; // Mask to get the flags
+    static const int KIND_FLAG_POSSIBLY_OFFENSIVE = 0x80000000;
+    static const int KIND_FLAG_EXACT_MATCH = 0x40000000;
+
     Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust);
 
     int getSuggestions(ProximityInfo *proximityInfo, void *traverseSession, int *xcoordinates,
diff --git a/native/jni/src/multi_bigram_map.h b/native/jni/src/multi_bigram_map.h
new file mode 100644
index 0000000..7e1b630
--- /dev/null
+++ b/native/jni/src/multi_bigram_map.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_MULTI_BIGRAM_MAP_H
+#define LATINIME_MULTI_BIGRAM_MAP_H
+
+#include <cstring>
+#include <stdint.h>
+
+#include "defines.h"
+#include "binary_format.h"
+#include "hash_map_compat.h"
+
+namespace latinime {
+
+// Class for caching bigram maps for multiple previous word contexts. This is useful since the
+// algorithm needs to look up the set of bigrams for every word pair that occurs in every
+// multi-word suggestion.
+class MultiBigramMap {
+ public:
+    MultiBigramMap() : mBigramMaps() {}
+    ~MultiBigramMap() {}
+
+    // Look up the bigram probability for the given word pair from the cached bigram maps.
+    // Also caches the bigrams if there is space remaining and they have not been cached already.
+    int getBigramProbability(const uint8_t *const dicRoot, const int wordPosition,
+            const int nextWordPosition, const int unigramProbability) {
+        hash_map_compat<int, BigramMap>::const_iterator mapPosition =
+                mBigramMaps.find(wordPosition);
+        if (mapPosition != mBigramMaps.end()) {
+            return mapPosition->second.getBigramProbability(nextWordPosition, unigramProbability);
+        }
+        if (mBigramMaps.size() < MAX_CACHED_PREV_WORDS_IN_BIGRAM_MAP) {
+            addBigramsForWordPosition(dicRoot, wordPosition);
+            return mBigramMaps[wordPosition].getBigramProbability(
+                    nextWordPosition, unigramProbability);
+        }
+        return BinaryFormat::getBigramProbability(
+                dicRoot, wordPosition, nextWordPosition, unigramProbability);
+    }
+
+    void clear() {
+        mBigramMaps.clear();
+    }
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(MultiBigramMap);
+
+    class BigramMap {
+     public:
+        BigramMap() : mBigramMap(DEFAULT_HASH_MAP_SIZE_FOR_EACH_BIGRAM_MAP) {}
+        ~BigramMap() {}
+
+        void init(const uint8_t *const dicRoot, int position) {
+            BinaryFormat::fillBigramProbabilityToHashMap(dicRoot, position, &mBigramMap);
+        }
+
+        inline int getBigramProbability(const int nextWordPosition, const int unigramProbability)
+                const {
+           return BinaryFormat::getBigramProbabilityFromHashMap(
+                   nextWordPosition, &mBigramMap, unigramProbability);
+        }
+
+     private:
+        // Note: Default copy constructor needed for use in hash_map.
+        hash_map_compat<int, int> mBigramMap;
+    };
+
+    void addBigramsForWordPosition(const uint8_t *const dicRoot, const int position) {
+        mBigramMaps[position].init(dicRoot, position);
+    }
+
+    hash_map_compat<int, BigramMap> mBigramMaps;
+};
+} // namespace latinime
+#endif // LATINIME_MULTI_BIGRAM_MAP_H
diff --git a/native/jni/src/suggest/core/dicnode/dic_node.h b/native/jni/src/suggest/core/dicnode/dic_node.h
index 32faae5..4225bb3 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node.h
+++ b/native/jni/src/suggest/core/dicnode/dic_node.h
@@ -210,8 +210,7 @@
     }
 
     bool isImpossibleBigramWord() const {
-        const int probability = mDicNodeProperties.getProbability();
-        if (probability == 0) {
+        if (mDicNodeProperties.hasBlacklistedOrNotAWordFlag()) {
             return true;
         }
         const int prevWordLen = mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength()
@@ -220,7 +219,7 @@
         return (prevWordLen == 1 && currentWordLen == 1);
     }
 
-    bool isCapitalized() const {
+    bool isFirstCharUppercase() const {
         const int c = getOutputWordBuf()[0];
         return isAsciiUpper(c);
     }
@@ -360,11 +359,6 @@
         return mDicNodeState.mDicNodeStateScoring.getCompoundDistance(languageWeight);
     }
 
-    // Note that "cost" means delta for "distance" that is weighted.
-    float getTotalPrevWordsLanguageCost() const {
-        return mDicNodeState.mDicNodeStateScoring.getTotalPrevWordsLanguageCost();
-    }
-
     // Used to commit input partially
     int getPrevWordNodePos() const {
         return mDicNodeState.mDicNodeStatePrevWord.getPrevWordNodePos();
@@ -469,6 +463,10 @@
         mDicNodeState.mDicNodeStateScoring.advanceDigraphIndex();
     }
 
+    bool isExactMatch() const {
+        return mDicNodeState.mDicNodeStateScoring.isExactMatch();
+    }
+
     uint8_t getFlags() const {
         return mDicNodeProperties.getFlags();
     }
@@ -548,13 +546,12 @@
     // Caveat: Must not be called outside Weighting
     // This restriction is guaranteed by "friend"
     AK_FORCE_INLINE void addCost(const float spatialCost, const float languageCost,
-            const bool doNormalization, const int inputSize, const bool isEditCorrection,
-            const bool isProximityCorrection) {
+            const bool doNormalization, const int inputSize, const ErrorType errorType) {
         if (DEBUG_GEO_FULL) {
             LOGI_SHOW_ADD_COST_PROP;
         }
         mDicNodeState.mDicNodeStateScoring.addCost(spatialCost, languageCost, doNormalization,
-                inputSize, getTotalInputIndex(), isEditCorrection, isProximityCorrection);
+                inputSize, getTotalInputIndex(), errorType);
     }
 
     // Caveat: Must not be called outside Weighting
diff --git a/native/jni/src/suggest/core/dicnode/dic_node_properties.h b/native/jni/src/suggest/core/dicnode/dic_node_properties.h
index 173ef35..63a6b13 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node_properties.h
+++ b/native/jni/src/suggest/core/dicnode/dic_node_properties.h
@@ -19,6 +19,7 @@
 
 #include <stdint.h>
 
+#include "binary_format.h"
 #include "defines.h"
 
 namespace latinime {
@@ -144,6 +145,10 @@
         return mChildrenCount > 0 || mDepth != mLeavingDepth;
     }
 
+    bool hasBlacklistedOrNotAWordFlag() const {
+        return BinaryFormat::hasBlacklistedOrNotAWordFlag(mFlags);
+    }
+
  private:
     // Caution!!!
     // Use a default copy constructor and an assign operator because shallow copies are ok
diff --git a/native/jni/src/suggest/core/dicnode/dic_node_state_input.h b/native/jni/src/suggest/core/dicnode/dic_node_state_input.h
index 7ad3e3e..bbd9435 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node_state_input.h
+++ b/native/jni/src/suggest/core/dicnode/dic_node_state_input.h
@@ -46,8 +46,8 @@
         for (int i = 0; i < MAX_POINTER_COUNT_G; i++) {
              mInputIndex[i] = src->mInputIndex[i];
              mPrevCodePoint[i] = src->mPrevCodePoint[i];
-        mTerminalDiffCost[i] = resetTerminalDiffCost ?
-                static_cast<float>(MAX_VALUE_FOR_WEIGHTING) : src->mTerminalDiffCost[i];
+             mTerminalDiffCost[i] = resetTerminalDiffCost ?
+                     static_cast<float>(MAX_VALUE_FOR_WEIGHTING) : src->mTerminalDiffCost[i];
          }
     }
 
diff --git a/native/jni/src/suggest/core/dicnode/dic_node_state_scoring.h b/native/jni/src/suggest/core/dicnode/dic_node_state_scoring.h
index 8902d31..dca9d60 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node_state_scoring.h
+++ b/native/jni/src/suggest/core/dicnode/dic_node_state_scoring.h
@@ -31,7 +31,7 @@
               mDigraphIndex(DigraphUtils::NOT_A_DIGRAPH_INDEX),
               mEditCorrectionCount(0), mProximityCorrectionCount(0),
               mNormalizedCompoundDistance(0.0f), mSpatialDistance(0.0f), mLanguageDistance(0.0f),
-              mTotalPrevWordsLanguageCost(0.0f), mRawLength(0.0f) {
+              mRawLength(0.0f), mExactMatch(true) {
     }
 
     virtual ~DicNodeStateScoring() {}
@@ -42,10 +42,10 @@
         mNormalizedCompoundDistance = 0.0f;
         mSpatialDistance = 0.0f;
         mLanguageDistance = 0.0f;
-        mTotalPrevWordsLanguageCost = 0.0f;
         mRawLength = 0.0f;
         mDoubleLetterLevel = NOT_A_DOUBLE_LETTER;
         mDigraphIndex = DigraphUtils::NOT_A_DIGRAPH_INDEX;
+        mExactMatch = true;
     }
 
     AK_FORCE_INLINE void init(const DicNodeStateScoring *const scoring) {
@@ -54,24 +54,35 @@
         mNormalizedCompoundDistance = scoring->mNormalizedCompoundDistance;
         mSpatialDistance = scoring->mSpatialDistance;
         mLanguageDistance = scoring->mLanguageDistance;
-        mTotalPrevWordsLanguageCost = scoring->mTotalPrevWordsLanguageCost;
         mRawLength = scoring->mRawLength;
         mDoubleLetterLevel = scoring->mDoubleLetterLevel;
         mDigraphIndex = scoring->mDigraphIndex;
+        mExactMatch = scoring->mExactMatch;
     }
 
     void addCost(const float spatialCost, const float languageCost, const bool doNormalization,
-            const int inputSize, const int totalInputIndex, const bool isEditCorrection,
-            const bool isProximityCorrection) {
+            const int inputSize, const int totalInputIndex, const ErrorType errorType) {
         addDistance(spatialCost, languageCost, doNormalization, inputSize, totalInputIndex);
-        if (isEditCorrection) {
-            ++mEditCorrectionCount;
-        }
-        if (isProximityCorrection) {
-            ++mProximityCorrectionCount;
-        }
-        if (languageCost > 0.0f) {
-            setTotalPrevWordsLanguageCost(mTotalPrevWordsLanguageCost + languageCost);
+        switch (errorType) {
+            case ET_EDIT_CORRECTION:
+                ++mEditCorrectionCount;
+                mExactMatch = false;
+                break;
+            case ET_PROXIMITY_CORRECTION:
+                ++mProximityCorrectionCount;
+                mExactMatch = false;
+                break;
+            case ET_COMPLETION:
+                mExactMatch = false;
+                break;
+            case ET_NEW_WORD:
+                mExactMatch = false;
+                break;
+            case ET_INTENTIONAL_OMISSION:
+                mExactMatch = false;
+                break;
+            case ET_NOT_AN_ERROR:
+                break;
         }
     }
 
@@ -148,8 +159,8 @@
         }
     }
 
-    float getTotalPrevWordsLanguageCost() const {
-        return mTotalPrevWordsLanguageCost;
+    bool isExactMatch() const {
+        return mExactMatch;
     }
 
  private:
@@ -165,8 +176,8 @@
     float mNormalizedCompoundDistance;
     float mSpatialDistance;
     float mLanguageDistance;
-    float mTotalPrevWordsLanguageCost;
     float mRawLength;
+    bool mExactMatch;
 
     AK_FORCE_INLINE void addDistance(float spatialDistance, float languageDistance,
             bool doNormalization, int inputSize, int totalInputIndex) {
@@ -179,11 +190,6 @@
                     / static_cast<float>(max(1, totalInputIndex));
         }
     }
-
-    //TODO: remove
-    AK_FORCE_INLINE void setTotalPrevWordsLanguageCost(float totalPrevWordsLanguageCost) {
-        mTotalPrevWordsLanguageCost = totalPrevWordsLanguageCost;
-    }
 };
 } // namespace latinime
 #endif // LATINIME_DIC_NODE_STATE_SCORING_H
diff --git a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp
index 031e706..5357c37 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp
+++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp
@@ -21,6 +21,7 @@
 #include "dic_node.h"
 #include "dic_node_utils.h"
 #include "dic_node_vector.h"
+#include "multi_bigram_map.h"
 #include "proximity_info.h"
 #include "proximity_info_state.h"
 
@@ -191,11 +192,11 @@
  * Computes the combined bigram / unigram cost for the given dicNode.
  */
 /* static */ float DicNodeUtils::getBigramNodeImprobability(const uint8_t *const dicRoot,
-        const DicNode *const node, hash_map_compat<int, int16_t> *bigramCacheMap) {
+        const DicNode *const node, MultiBigramMap *multiBigramMap) {
     if (node->isImpossibleBigramWord()) {
         return static_cast<float>(MAX_VALUE_FOR_WEIGHTING);
     }
-    const int probability = getBigramNodeProbability(dicRoot, node, bigramCacheMap);
+    const int probability = getBigramNodeProbability(dicRoot, node, multiBigramMap);
     // TODO: This equation to calculate the improbability looks unreasonable.  Investigate this.
     const float cost = static_cast<float>(MAX_PROBABILITY - probability)
             / static_cast<float>(MAX_PROBABILITY);
@@ -203,92 +204,25 @@
 }
 
 /* static */ int DicNodeUtils::getBigramNodeProbability(const uint8_t *const dicRoot,
-        const DicNode *const node, hash_map_compat<int, int16_t> *bigramCacheMap) {
+        const DicNode *const node, MultiBigramMap *multiBigramMap) {
     const int unigramProbability = node->getProbability();
-    const int encodedDiffOfBigramProbability =
-            getBigramNodeEncodedDiffProbability(dicRoot, node, bigramCacheMap);
-    if (NOT_A_PROBABILITY == encodedDiffOfBigramProbability) {
+    const int wordPos = node->getPos();
+    const int prevWordPos = node->getPrevWordPos();
+    if (NOT_VALID_WORD == wordPos || NOT_VALID_WORD == prevWordPos) {
+        // Note: Normally wordPos comes from the dictionary and should never equal NOT_VALID_WORD.
         return backoff(unigramProbability);
     }
-    return BinaryFormat::computeProbabilityForBigram(
-            unigramProbability, encodedDiffOfBigramProbability);
+    if (multiBigramMap) {
+        return multiBigramMap->getBigramProbability(
+                dicRoot, prevWordPos, wordPos, unigramProbability);
+    }
+    return BinaryFormat::getBigramProbability(dicRoot, prevWordPos, wordPos, unigramProbability);
 }
 
 ///////////////////////////////////////
 // Bigram / Unigram dictionary utils //
 ///////////////////////////////////////
 
-/* static */ int16_t DicNodeUtils::getBigramNodeEncodedDiffProbability(const uint8_t *const dicRoot,
-        const DicNode *const node, hash_map_compat<int, int16_t> *bigramCacheMap) {
-    const int wordPos = node->getPos();
-    const int prevWordPos = node->getPrevWordPos();
-    return getBigramProbability(dicRoot, prevWordPos, wordPos, bigramCacheMap);
-}
-
-// TODO: Move this to BigramDictionary
-/* static */ int16_t DicNodeUtils::getBigramProbability(const uint8_t *const dicRoot, int pos,
-        const int nextPos, hash_map_compat<int, int16_t> *bigramCacheMap) {
-    // TODO: this is painfully slow compared to the method used in the previous version of the
-    // algorithm. Switch to that method.
-    if (NOT_VALID_WORD == pos) return NOT_A_PROBABILITY;
-    if (NOT_VALID_WORD == nextPos) return NOT_A_PROBABILITY;
-
-    // Create a hash code for the given node pair (based on Josh Bloch's effective Java).
-    // TODO: Use a real hash map data structure that deals with collisions.
-    int hash = 17;
-    hash = hash * 31 + pos;
-    hash = hash * 31 + nextPos;
-
-    hash_map_compat<int, int16_t>::const_iterator mapPos = bigramCacheMap->find(hash);
-    if (mapPos != bigramCacheMap->end()) {
-        return mapPos->second;
-    }
-    if (NOT_VALID_WORD == pos) {
-        return NOT_A_PROBABILITY;
-    }
-    const uint8_t flags = BinaryFormat::getFlagsAndForwardPointer(dicRoot, &pos);
-    if (0 == (flags & BinaryFormat::FLAG_HAS_BIGRAMS)) {
-        return NOT_A_PROBABILITY;
-    }
-    if (0 == (flags & BinaryFormat::FLAG_HAS_MULTIPLE_CHARS)) {
-        BinaryFormat::getCodePointAndForwardPointer(dicRoot, &pos);
-    } else {
-        pos = BinaryFormat::skipOtherCharacters(dicRoot, pos);
-    }
-    pos = BinaryFormat::skipChildrenPosition(flags, pos);
-    pos = BinaryFormat::skipProbability(flags, pos);
-    uint8_t bigramFlags;
-    int count = 0;
-    do {
-        bigramFlags = BinaryFormat::getFlagsAndForwardPointer(dicRoot, &pos);
-        const int bigramPos = BinaryFormat::getAttributeAddressAndForwardPointer(dicRoot,
-                bigramFlags, &pos);
-        if (bigramPos == nextPos) {
-            const int16_t probability = BinaryFormat::MASK_ATTRIBUTE_PROBABILITY & bigramFlags;
-            if (static_cast<int>(bigramCacheMap->size()) < MAX_BIGRAM_MAP_SIZE) {
-                (*bigramCacheMap)[hash] = probability;
-            }
-            return probability;
-        }
-        count++;
-    } while ((0 != (BinaryFormat::FLAG_ATTRIBUTE_HAS_NEXT & bigramFlags))
-            && count < MAX_BIGRAMS_CONSIDERED_PER_CONTEXT);
-    if (static_cast<int>(bigramCacheMap->size()) < MAX_BIGRAM_MAP_SIZE) {
-        // TODO: does this -1 mean NOT_VALID_WORD?
-        (*bigramCacheMap)[hash] = -1;
-    }
-    return NOT_A_PROBABILITY;
-}
-
-/* static */ int DicNodeUtils::getWordPos(const uint8_t *const dicRoot, const int *word,
-        const int wordLength) {
-    if (!word) {
-        return NOT_VALID_WORD;
-    }
-    return BinaryFormat::getTerminalPosition(
-            dicRoot, word, wordLength, false /* forceLowerCaseSearch */);
-}
-
 /* static */ bool DicNodeUtils::isMatchedNodeCodePoint(const ProximityInfoState *pInfoState,
         const int pointIndex, const bool exactOnly, const int nodeCodePoint) {
     if (!pInfoState) {
diff --git a/native/jni/src/suggest/core/dicnode/dic_node_utils.h b/native/jni/src/suggest/core/dicnode/dic_node_utils.h
index 15f9730..5bc542d 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node_utils.h
+++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.h
@@ -21,7 +21,6 @@
 #include <vector>
 
 #include "defines.h"
-#include "hash_map_compat.h"
 
 namespace latinime {
 
@@ -29,6 +28,7 @@
 class DicNodeVector;
 class ProximityInfo;
 class ProximityInfoState;
+class MultiBigramMap;
 
 class DicNodeUtils {
  public:
@@ -41,9 +41,8 @@
     static void initByCopy(DicNode *srcNode, DicNode *destNode);
     static void getAllChildDicNodes(DicNode *dicNode, const uint8_t *const dicRoot,
             DicNodeVector *childDicNodes);
-    static int getWordPos(const uint8_t *const dicRoot, const int *word, const int prevWordLength);
     static float getBigramNodeImprobability(const uint8_t *const dicRoot,
-            const DicNode *const node, hash_map_compat<int, int16_t> *const bigramCacheMap);
+            const DicNode *const node, MultiBigramMap *const multiBigramMap);
     static bool isDicNodeFilteredOut(const int nodeCodePoint, const ProximityInfo *const pInfo,
             const std::vector<int> *const codePointsFilter);
     // TODO: Move to private
@@ -58,15 +57,11 @@
 
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(DicNodeUtils);
-    // Max cache size for the space omission error correction bigram lookup
-    static const int MAX_BIGRAM_MAP_SIZE = 20000;
     // Max number of bigrams to look up
     static const int MAX_BIGRAMS_CONSIDERED_PER_CONTEXT = 500;
 
     static int getBigramNodeProbability(const uint8_t *const dicRoot, const DicNode *const node,
-            hash_map_compat<int, int16_t> *bigramCacheMap);
-    static int16_t getBigramNodeEncodedDiffProbability(const uint8_t *const dicRoot,
-            const DicNode *const node, hash_map_compat<int, int16_t> *bigramCacheMap);
+            MultiBigramMap *multiBigramMap);
     static void createAndGetPassingChildNode(DicNode *dicNode, const ProximityInfoState *pInfoState,
             const int pointIndex, const bool exactOnly, DicNodeVector *childDicNodes);
     static void createAndGetAllLeavingChildNodes(DicNode *dicNode, const uint8_t *const dicRoot,
@@ -77,8 +72,6 @@
             const int terminalDepth, const ProximityInfoState *pInfoState, const int pointIndex,
             const bool exactOnly, const std::vector<int> *const codePointsFilter,
             const ProximityInfo *const pInfo, DicNodeVector *childDicNodes);
-    static int16_t getBigramProbability(const uint8_t *const dicRoot, int pos, const int nextPos,
-            hash_map_compat<int, int16_t> *bigramCacheMap);
 
     // TODO: Move to proximity info
     static bool isMatchedNodeCodePoint(const ProximityInfoState *pInfoState, const int pointIndex,
diff --git a/native/jni/src/suggest/core/policy/scoring.h b/native/jni/src/suggest/core/policy/scoring.h
index b8c10e2..102e856 100644
--- a/native/jni/src/suggest/core/policy/scoring.h
+++ b/native/jni/src/suggest/core/policy/scoring.h
@@ -29,16 +29,14 @@
  public:
     virtual int calculateFinalScore(const float compoundDistance, const int inputSize,
             const bool forceCommit) const = 0;
-    virtual bool getMostProbableString(
-            const DicTraverseSession *const traverseSession, const int terminalSize,
-            const float languageWeight, int *const outputCodePoints, int *const type,
-            int *const freq) const = 0;
+    virtual bool getMostProbableString(const DicTraverseSession *const traverseSession,
+            const int terminalSize, const float languageWeight, int *const outputCodePoints,
+            int *const type, int *const freq) const = 0;
     virtual void safetyNetForMostProbableString(const int terminalSize,
             const int maxScore, int *const outputCodePoints, int *const frequencies) const = 0;
     // TODO: Make more generic
-    virtual void searchWordWithDoubleLetter(DicNode *terminals,
-            const int terminalSize, int *doubleLetterTerminalIndex,
-            DoubleLetterLevel *doubleLetterLevel) const = 0;
+    virtual void searchWordWithDoubleLetter(DicNode *terminals, const int terminalSize,
+            int *doubleLetterTerminalIndex, DoubleLetterLevel *doubleLetterLevel) const = 0;
     virtual float getAdjustedLanguageWeight(DicTraverseSession *const traverseSession,
             DicNode *const terminals, const int size) const = 0;
     virtual float getDoubleLetterDemotionDistanceCost(const int terminalIndex,
diff --git a/native/jni/src/suggest/core/policy/suggest_policy.h b/native/jni/src/suggest/core/policy/suggest_policy.h
index 885e214..5b6402c 100644
--- a/native/jni/src/suggest/core/policy/suggest_policy.h
+++ b/native/jni/src/suggest/core/policy/suggest_policy.h
@@ -20,6 +20,7 @@
 #include "defines.h"
 
 namespace latinime {
+
 class Traversal;
 class Scoring;
 class Weighting;
diff --git a/native/jni/src/suggest/core/policy/traversal.h b/native/jni/src/suggest/core/policy/traversal.h
index 02c358a..c6f66f2 100644
--- a/native/jni/src/suggest/core/policy/traversal.h
+++ b/native/jni/src/suggest/core/policy/traversal.h
@@ -28,7 +28,8 @@
     virtual int getMaxPointerCount() const = 0;
     virtual bool allowsErrorCorrections(const DicNode *const dicNode) const = 0;
     virtual bool isOmission(const DicTraverseSession *const traverseSession,
-            const DicNode *const dicNode, const DicNode *const childDicNode) const = 0;
+            const DicNode *const dicNode, const DicNode *const childDicNode,
+            const bool allowsErrorCorrections) const = 0;
     virtual bool isSpaceSubstitutionTerminal(const DicTraverseSession *const traverseSession,
             const DicNode *const dicNode) const = 0;
     virtual bool isSpaceOmissionTerminal(const DicTraverseSession *const traverseSession,
@@ -38,9 +39,8 @@
             const DicNode *const dicNode) const = 0;
     virtual bool canDoLookAheadCorrection(const DicTraverseSession *const traverseSession,
             const DicNode *const dicNode) const = 0;
-    virtual ProximityType getProximityType(
-            const DicTraverseSession *const traverseSession, const DicNode *const dicNode,
-            const DicNode *const childDicNode) const = 0;
+    virtual ProximityType getProximityType(const DicTraverseSession *const traverseSession,
+            const DicNode *const dicNode, const DicNode *const childDicNode) const = 0;
     virtual bool sameAsTyped(const DicTraverseSession *const traverseSession,
             const DicNode *const dicNode) const = 0;
     virtual bool needsToTraverseAllUserInput() const = 0;
@@ -48,9 +48,8 @@
     virtual bool allowPartialCommit() const = 0;
     virtual int getDefaultExpandDicNodeSize() const = 0;
     virtual int getMaxCacheSize() const = 0;
-    virtual bool isPossibleOmissionChildNode(
-            const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode,
-            const DicNode *const dicNode) const = 0;
+    virtual bool isPossibleOmissionChildNode(const DicTraverseSession *const traverseSession,
+            const DicNode *const parentDicNode, const DicNode *const dicNode) const = 0;
     virtual bool isGoodToTraverseNextWord(const DicNode *const dicNode) const = 0;
 
  protected:
diff --git a/native/jni/src/suggest/core/policy/weighting.cpp b/native/jni/src/suggest/core/policy/weighting.cpp
index b9c0b81..d01531f 100644
--- a/native/jni/src/suggest/core/policy/weighting.cpp
+++ b/native/jni/src/suggest/core/policy/weighting.cpp
@@ -18,7 +18,6 @@
 
 #include "char_utils.h"
 #include "defines.h"
-#include "hash_map_compat.h"
 #include "suggest/core/dicnode/dic_node.h"
 #include "suggest/core/dicnode/dic_node_profiler.h"
 #include "suggest/core/dicnode/dic_node_utils.h"
@@ -26,6 +25,8 @@
 
 namespace latinime {
 
+class MultiBigramMap;
+
 static inline void profile(const CorrectionType correctionType, DicNode *const node) {
 #if DEBUG_DICT
     switch (correctionType) {
@@ -69,20 +70,18 @@
 }
 
 /* static */ void Weighting::addCostAndForwardInputIndex(const Weighting *const weighting,
-        const CorrectionType correctionType,
-        const DicTraverseSession *const traverseSession,
+        const CorrectionType correctionType, const DicTraverseSession *const traverseSession,
         const DicNode *const parentDicNode, DicNode *const dicNode,
-        hash_map_compat<int, int16_t> *const bigramCacheMap) {
+        MultiBigramMap *const multiBigramMap) {
     const int inputSize = traverseSession->getInputSize();
     DicNode_InputStateG inputStateG;
     inputStateG.mNeedsToUpdateInputStateG = false; // Don't use input info by default
     const float spatialCost = Weighting::getSpatialCost(weighting, correctionType,
             traverseSession, parentDicNode, dicNode, &inputStateG);
     const float languageCost = Weighting::getLanguageCost(weighting, correctionType,
-            traverseSession, parentDicNode, dicNode, bigramCacheMap);
-    const bool edit = Weighting::isEditCorrection(correctionType);
-    const bool proximity = Weighting::isProximityCorrection(weighting, correctionType,
-            traverseSession, dicNode);
+            traverseSession, parentDicNode, dicNode, multiBigramMap);
+    const ErrorType errorType = weighting->getErrorType(correctionType, traverseSession,
+            parentDicNode, dicNode);
     profile(correctionType, dicNode);
     if (inputStateG.mNeedsToUpdateInputStateG) {
         dicNode->updateInputIndexG(&inputStateG);
@@ -91,13 +90,13 @@
                 (correctionType == CT_TRANSPOSITION));
     }
     dicNode->addCost(spatialCost, languageCost, weighting->needsToNormalizeCompoundDistance(),
-            inputSize, edit, proximity);
+            inputSize, errorType);
 }
 
 /* static */ float Weighting::getSpatialCost(const Weighting *const weighting,
-        const CorrectionType correctionType,
-        const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode,
-        const DicNode *const dicNode, DicNode_InputStateG *const inputStateG) {
+        const CorrectionType correctionType, const DicTraverseSession *const traverseSession,
+        const DicNode *const parentDicNode, const DicNode *const dicNode,
+        DicNode_InputStateG *const inputStateG) {
     switch(correctionType) {
     case CT_OMISSION:
         return weighting->getOmissionCost(parentDicNode, dicNode);
@@ -129,14 +128,14 @@
 /* static */ float Weighting::getLanguageCost(const Weighting *const weighting,
         const CorrectionType correctionType, const DicTraverseSession *const traverseSession,
         const DicNode *const parentDicNode, const DicNode *const dicNode,
-        hash_map_compat<int, int16_t> *const bigramCacheMap) {
+        MultiBigramMap *const multiBigramMap) {
     switch(correctionType) {
     case CT_OMISSION:
         return 0.0f;
     case CT_SUBSTITUTION:
         return 0.0f;
     case CT_NEW_WORD_SPACE_OMITTION:
-        return weighting->getNewWordBigramCost(traverseSession, parentDicNode, bigramCacheMap);
+        return weighting->getNewWordBigramCost(traverseSession, parentDicNode, multiBigramMap);
     case CT_MATCH:
         return 0.0f;
     case CT_COMPLETION:
@@ -144,11 +143,11 @@
     case CT_TERMINAL: {
         const float languageImprobability =
                 DicNodeUtils::getBigramNodeImprobability(
-                        traverseSession->getOffsetDict(), dicNode, bigramCacheMap);
+                        traverseSession->getOffsetDict(), dicNode, multiBigramMap);
         return weighting->getTerminalLanguageCost(traverseSession, dicNode, languageImprobability);
     }
     case CT_NEW_WORD_SPACE_SUBSTITUTION:
-        return weighting->getNewWordBigramCost(traverseSession, parentDicNode, bigramCacheMap);
+        return weighting->getNewWordBigramCost(traverseSession, parentDicNode, multiBigramMap);
     case CT_INSERTION:
         return 0.0f;
     case CT_TRANSPOSITION:
@@ -158,64 +157,6 @@
     }
 }
 
-/* static */ bool Weighting::isEditCorrection(const CorrectionType correctionType) {
-    switch(correctionType) {
-        case CT_OMISSION:
-            return true;
-        case CT_ADDITIONAL_PROXIMITY:
-            // Should return true?
-            return false;
-        case CT_SUBSTITUTION:
-            // Should return true?
-            return false;
-        case CT_NEW_WORD_SPACE_OMITTION:
-            return false;
-        case CT_MATCH:
-            return false;
-        case CT_COMPLETION:
-            return false;
-        case CT_TERMINAL:
-            return false;
-        case CT_NEW_WORD_SPACE_SUBSTITUTION:
-            return false;
-        case CT_INSERTION:
-            return true;
-        case CT_TRANSPOSITION:
-            return true;
-        default:
-            return false;
-    }
-}
-
-/* static */ bool Weighting::isProximityCorrection(const Weighting *const weighting,
-        const CorrectionType correctionType,
-        const DicTraverseSession *const traverseSession, const DicNode *const dicNode) {
-    switch(correctionType) {
-        case CT_OMISSION:
-            return false;
-        case CT_ADDITIONAL_PROXIMITY:
-            return false;
-        case CT_SUBSTITUTION:
-            return false;
-        case CT_NEW_WORD_SPACE_OMITTION:
-            return false;
-        case CT_MATCH:
-            return weighting->isProximityDicNode(traverseSession, dicNode);
-        case CT_COMPLETION:
-            return false;
-        case CT_TERMINAL:
-            return false;
-        case CT_NEW_WORD_SPACE_SUBSTITUTION:
-            return false;
-        case CT_INSERTION:
-            return false;
-        case CT_TRANSPOSITION:
-            return false;
-        default:
-            return false;
-    }
-}
-
 /* static */ int Weighting::getForwardInputCount(const CorrectionType correctionType) {
     switch(correctionType) {
         case CT_OMISSION:
@@ -229,7 +170,7 @@
         case CT_MATCH:
             return 1;
         case CT_COMPLETION:
-            return 0;
+            return 1;
         case CT_TERMINAL:
             return 0;
         case CT_NEW_WORD_SPACE_SUBSTITUTION:
diff --git a/native/jni/src/suggest/core/policy/weighting.h b/native/jni/src/suggest/core/policy/weighting.h
index bce479c..0d2745b 100644
--- a/native/jni/src/suggest/core/policy/weighting.h
+++ b/native/jni/src/suggest/core/policy/weighting.h
@@ -18,13 +18,13 @@
 #define LATINIME_WEIGHTING_H
 
 #include "defines.h"
-#include "hash_map_compat.h"
 
 namespace latinime {
 
 class DicNode;
 class DicTraverseSession;
 struct DicNode_InputStateG;
+class MultiBigramMap;
 
 class Weighting {
  public:
@@ -32,7 +32,7 @@
             const CorrectionType correctionType,
             const DicTraverseSession *const traverseSession,
             const DicNode *const parentDicNode, DicNode *const dicNode,
-            hash_map_compat<int, int16_t> *const bigramCacheMap);
+            MultiBigramMap *const multiBigramMap);
 
  protected:
     virtual float getTerminalSpatialCost(const DicTraverseSession *const traverseSession,
@@ -61,7 +61,7 @@
 
     virtual float getNewWordBigramCost(
             const DicTraverseSession *const traverseSession, const DicNode *const dicNode,
-            hash_map_compat<int, int16_t> *const bigramCacheMap) const = 0;
+            MultiBigramMap *const multiBigramMap) const = 0;
 
     virtual float getCompletionCost(
             const DicTraverseSession *const traverseSession,
@@ -80,6 +80,10 @@
     virtual float getSpaceSubstitutionCost(const DicTraverseSession *const traverseSession,
             const DicNode *const dicNode) const = 0;
 
+    virtual ErrorType getErrorType(const CorrectionType correctionType,
+            const DicTraverseSession *const traverseSession,
+            const DicNode *const parentDicNode, const DicNode *const dicNode) const = 0;
+
     Weighting() {}
     virtual ~Weighting() {}
 
@@ -93,13 +97,7 @@
     static float getLanguageCost(const Weighting *const weighting,
             const CorrectionType correctionType, const DicTraverseSession *const traverseSession,
             const DicNode *const parentDicNode, const DicNode *const dicNode,
-            hash_map_compat<int, int16_t> *const bigramCacheMap);
-    // TODO: Move to TypingWeighting and GestureWeighting?
-    static bool isEditCorrection(const CorrectionType correctionType);
-    // TODO: Move to TypingWeighting and GestureWeighting?
-    static bool isProximityCorrection(const Weighting *const weighting,
-            const CorrectionType correctionType, const DicTraverseSession *const traverseSession,
-            const DicNode *const dicNode);
+            MultiBigramMap *const multiBigramMap);
     // TODO: Move to TypingWeighting and GestureWeighting?
     static int getForwardInputCount(const CorrectionType correctionType);
 };
diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.cpp b/native/jni/src/suggest/core/session/dic_traverse_session.cpp
index 3c44db2..6408f01 100644
--- a/native/jni/src/suggest/core/session/dic_traverse_session.cpp
+++ b/native/jni/src/suggest/core/session/dic_traverse_session.cpp
@@ -64,12 +64,21 @@
 void DicTraverseSession::init(const Dictionary *const dictionary, const int *prevWord,
         int prevWordLength) {
     mDictionary = dictionary;
-    mMultiWordCostMultiplier = BinaryFormat::getMultiWordCostMultiplier(mDictionary->getDict());
+    mMultiWordCostMultiplier = BinaryFormat::getMultiWordCostMultiplier(mDictionary->getDict(),
+            mDictionary->getDictSize());
     if (!prevWord) {
         mPrevWordPos = NOT_VALID_WORD;
         return;
     }
-    mPrevWordPos = DicNodeUtils::getWordPos(dictionary->getOffsetDict(), prevWord, prevWordLength);
+    // TODO: merge following similar calls to getTerminalPosition into one case-insensitive call.
+    mPrevWordPos = BinaryFormat::getTerminalPosition(dictionary->getOffsetDict(), prevWord,
+            prevWordLength, false /* forceLowerCaseSearch */);
+    if (mPrevWordPos == NOT_VALID_WORD) {
+        // Check bigrams for lower-cased previous word if original was not found. Useful for
+        // auto-capitalized words like "The [current_word]".
+        mPrevWordPos = BinaryFormat::getTerminalPosition(dictionary->getOffsetDict(), prevWord,
+                prevWordLength, true /* forceLowerCaseSearch */);
+    }
 }
 
 void DicTraverseSession::setupForGetSuggestions(const ProximityInfo *pInfo,
@@ -92,7 +101,7 @@
 
 void DicTraverseSession::resetCache(const int nextActiveCacheSize, const int maxWords) {
     mDicNodesCache.reset(nextActiveCacheSize, maxWords);
-    mBigramCacheMap.clear();
+    mMultiBigramMap.clear();
     mPartiallyCommited = false;
 }
 
diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.h b/native/jni/src/suggest/core/session/dic_traverse_session.h
index d9c2a51..d88be5b 100644
--- a/native/jni/src/suggest/core/session/dic_traverse_session.h
+++ b/native/jni/src/suggest/core/session/dic_traverse_session.h
@@ -21,8 +21,8 @@
 #include <vector>
 
 #include "defines.h"
-#include "hash_map_compat.h"
 #include "jni.h"
+#include "multi_bigram_map.h"
 #include "proximity_info_state.h"
 #include "suggest/core/dicnode/dic_nodes_cache.h"
 
@@ -35,7 +35,7 @@
  public:
     AK_FORCE_INLINE DicTraverseSession(JNIEnv *env, jstring localeStr)
             : mPrevWordPos(NOT_VALID_WORD), mProximityInfo(0),
-              mDictionary(0), mDicNodesCache(), mBigramCacheMap(),
+              mDictionary(0), mDicNodesCache(), mMultiBigramMap(),
               mInputSize(0), mPartiallyCommited(false), mMaxPointerCount(1),
               mMultiWordCostMultiplier(1.0f) {
         // NOTE: mProximityInfoStates is an array of instances.
@@ -67,7 +67,7 @@
     // TODO: Use proper parameter when changed
     int getDicRootPos() const { return 0; }
     DicNodesCache *getDicTraverseCache() { return &mDicNodesCache; }
-    hash_map_compat<int, int16_t> *getBigramCacheMap() { return &mBigramCacheMap; }
+    MultiBigramMap *getMultiBigramMap() { return &mMultiBigramMap; }
     const ProximityInfoState *getProximityInfoState(int id) const {
         return &mProximityInfoStates[id];
     }
@@ -170,7 +170,7 @@
 
     DicNodesCache mDicNodesCache;
     // Temporary cache for bigram frequencies
-    hash_map_compat<int, int16_t> mBigramCacheMap;
+    MultiBigramMap mMultiBigramMap;
     ProximityInfoState mProximityInfoStates[MAX_POINTER_COUNT_G];
 
     int mInputSize;
diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp
index 9de2cd2..a187948 100644
--- a/native/jni/src/suggest/core/suggest.cpp
+++ b/native/jni/src/suggest/core/suggest.cpp
@@ -161,12 +161,20 @@
                 + doubleLetterCost;
         const TerminalAttributes terminalAttributes(traverseSession->getOffsetDict(),
                 terminalDicNode->getFlags(), terminalDicNode->getAttributesPos());
-        const int originalTerminalProbability = terminalDicNode->getProbability();
+        const bool isPossiblyOffensiveWord = terminalDicNode->getProbability() <= 0;
+        const bool isExactMatch = terminalDicNode->isExactMatch();
+        const bool isFirstCharUppercase = terminalDicNode->isFirstCharUppercase();
+        // Heuristic: We exclude freq=0 first-char-uppercase words from exact match.
+        // (e.g. "AMD" and "and")
+        const bool isSafeExactMatch = isExactMatch
+                && !(isPossiblyOffensiveWord && isFirstCharUppercase);
+        const int outputTypeFlags =
+                (isPossiblyOffensiveWord ? Dictionary::KIND_FLAG_POSSIBLY_OFFENSIVE : 0)
+                | (isSafeExactMatch ? Dictionary::KIND_FLAG_EXACT_MATCH : 0);
 
-        // Do not suggest words with a 0 probability, or entries that are blacklisted or do not
-        // represent a word. However, we should still submit their shortcuts if any.
-        const bool isValidWord =
-                originalTerminalProbability > 0 && !terminalAttributes.isBlacklistedOrNotAWord();
+        // Entries that are blacklisted or do not represent a word should not be output.
+        const bool isValidWord = !terminalAttributes.isBlacklistedOrNotAWord();
+
         // Increase output score of top typing suggestion to ensure autocorrection.
         // TODO: Better integration with java side autocorrection logic.
         // Force autocorrection for obvious long multi-word suggestions.
@@ -188,10 +196,9 @@
             }
         }
 
-        // Do not suggest words with a 0 probability, or entries that are blacklisted or do not
-        // represent a word. However, we should still submit their shortcuts if any.
+        // Don't output invalid words. However, we still need to submit their shortcuts if any.
         if (isValidWord) {
-            outputTypes[outputWordIndex] = Dictionary::KIND_CORRECTION;
+            outputTypes[outputWordIndex] = Dictionary::KIND_CORRECTION | outputTypeFlags;
             frequencies[outputWordIndex] = finalScore;
             // Populate the outputChars array with the suggested word.
             const int startIndex = outputWordIndex * MAX_WORD_LENGTH;
@@ -294,8 +301,8 @@
                     correctionDicNode.advanceDigraphIndex();
                     processDicNodeAsDigraph(traverseSession, &correctionDicNode);
                 }
-                if (allowsErrorCorrections
-                        && TRAVERSAL->isOmission(traverseSession, &dicNode, childDicNode)) {
+                if (TRAVERSAL->isOmission(traverseSession, &dicNode, childDicNode,
+                        allowsErrorCorrections)) {
                     // TODO: (Gesture) Change weight between omission and substitution errors
                     // TODO: (Gesture) Terminal node should not be handled as omission
                     correctionDicNode.initByCopy(childDicNode);
@@ -357,7 +364,7 @@
     DicNode terminalDicNode;
     DicNodeUtils::initByCopy(dicNode, &terminalDicNode);
     Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_TERMINAL, traverseSession, 0,
-            &terminalDicNode, traverseSession->getBigramCacheMap());
+            &terminalDicNode, traverseSession->getMultiBigramMap());
     traverseSession->getDicTraverseCache()->copyPushTerminal(&terminalDicNode);
 }
 
@@ -389,8 +396,10 @@
 
 void Suggest::processDicNodeAsAdditionalProximityChar(DicTraverseSession *traverseSession,
         DicNode *dicNode, DicNode *childDicNode) const {
+    // Note: Most types of corrections don't need to look up the bigram information since they do
+    // not treat the node as a terminal. There is no need to pass the bigram map in these cases.
     Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_ADDITIONAL_PROXIMITY,
-            traverseSession, dicNode, childDicNode, 0 /* bigramCacheMap */);
+            traverseSession, dicNode, childDicNode, 0 /* multiBigramMap */);
     weightChildNode(traverseSession, childDicNode);
     processExpandedDicNode(traverseSession, childDicNode);
 }
@@ -398,7 +407,7 @@
 void Suggest::processDicNodeAsSubstitution(DicTraverseSession *traverseSession,
         DicNode *dicNode, DicNode *childDicNode) const {
     Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_SUBSTITUTION, traverseSession,
-            dicNode, childDicNode, 0 /* bigramCacheMap */);
+            dicNode, childDicNode, 0 /* multiBigramMap */);
     weightChildNode(traverseSession, childDicNode);
     processExpandedDicNode(traverseSession, childDicNode);
 }
@@ -422,20 +431,15 @@
  */
 void Suggest::processDicNodeAsOmission(
         DicTraverseSession *traverseSession, DicNode *dicNode) const {
-    // If the omission is surely intentional that it should incur zero cost.
-    const bool isZeroCostOmission = dicNode->isZeroCostOmission();
     DicNodeVector childDicNodes;
-
     DicNodeUtils::getAllChildDicNodes(dicNode, traverseSession->getOffsetDict(), &childDicNodes);
 
     const int size = childDicNodes.getSizeAndLock();
     for (int i = 0; i < size; i++) {
         DicNode *const childDicNode = childDicNodes[i];
-        if (!isZeroCostOmission) {
-            // Treat this word as omission
-            Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_OMISSION, traverseSession,
-                    dicNode, childDicNode, 0 /* bigramCacheMap */);
-        }
+        // Treat this word as omission
+        Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_OMISSION, traverseSession,
+                dicNode, childDicNode, 0 /* multiBigramMap */);
         weightChildNode(traverseSession, childDicNode);
 
         if (!TRAVERSAL->isPossibleOmissionChildNode(traverseSession, dicNode, childDicNode)) {
@@ -459,7 +463,7 @@
     for (int i = 0; i < size; i++) {
         DicNode *const childDicNode = childDicNodes[i];
         Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_INSERTION, traverseSession,
-                dicNode, childDicNode, 0 /* bigramCacheMap */);
+                dicNode, childDicNode, 0 /* multiBigramMap */);
         processExpandedDicNode(traverseSession, childDicNode);
     }
 }
@@ -484,7 +488,7 @@
             for (int j = 0; j < childSize2; j++) {
                 DicNode *const childDicNode2 = childDicNodes2[j];
                 Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_TRANSPOSITION,
-                        traverseSession, childDicNodes1[i], childDicNode2, 0 /* bigramCacheMap */);
+                        traverseSession, childDicNodes1[i], childDicNode2, 0 /* multiBigramMap */);
                 processExpandedDicNode(traverseSession, childDicNode2);
             }
         }
@@ -499,10 +503,10 @@
     const int inputSize = traverseSession->getInputSize();
     if (dicNode->isCompletion(inputSize)) {
         Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_COMPLETION, traverseSession,
-                0 /* parentDicNode */, dicNode, 0 /* bigramCacheMap */);
+                0 /* parentDicNode */, dicNode, 0 /* multiBigramMap */);
     } else { // completion
         Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_MATCH, traverseSession,
-                0 /* parentDicNode */, dicNode, 0 /* bigramCacheMap */);
+                0 /* parentDicNode */, dicNode, 0 /* multiBigramMap */);
     }
 }
 
@@ -523,7 +527,7 @@
     const CorrectionType correctionType = spaceSubstitution ?
             CT_NEW_WORD_SPACE_SUBSTITUTION : CT_NEW_WORD_SPACE_OMITTION;
     Weighting::addCostAndForwardInputIndex(WEIGHTING, correctionType, traverseSession, dicNode,
-            &newDicNode, traverseSession->getBigramCacheMap());
+            &newDicNode, traverseSession->getMultiBigramMap());
     traverseSession->getDicTraverseCache()->copyPushNextActive(&newDicNode);
 }
 } // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
index 0fa684f..f879892 100644
--- a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
+++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
@@ -28,25 +28,25 @@
 const float ScoringParams::DISTANCE_WEIGHT_LENGTH = 0.132f;
 const float ScoringParams::PROXIMITY_COST = 0.086f;
 const float ScoringParams::FIRST_PROXIMITY_COST = 0.104f;
-const float ScoringParams::OMISSION_COST = 0.388f;
-const float ScoringParams::OMISSION_COST_SAME_CHAR = 0.431f;
-const float ScoringParams::OMISSION_COST_FIRST_CHAR = 0.532f;
-const float ScoringParams::INSERTION_COST = 0.670f;
-const float ScoringParams::INSERTION_COST_SAME_CHAR = 0.526f;
-const float ScoringParams::INSERTION_COST_FIRST_CHAR = 0.563f;
-const float ScoringParams::TRANSPOSITION_COST = 0.494f;
-const float ScoringParams::SPACE_SUBSTITUTION_COST = 0.239f;
+const float ScoringParams::OMISSION_COST = 0.458f;
+const float ScoringParams::OMISSION_COST_SAME_CHAR = 0.491f;
+const float ScoringParams::OMISSION_COST_FIRST_CHAR = 0.582f;
+const float ScoringParams::INSERTION_COST = 0.730f;
+const float ScoringParams::INSERTION_COST_SAME_CHAR = 0.586f;
+const float ScoringParams::INSERTION_COST_FIRST_CHAR = 0.623f;
+const float ScoringParams::TRANSPOSITION_COST = 0.516f;
+const float ScoringParams::SPACE_SUBSTITUTION_COST = 0.319f;
 const float ScoringParams::ADDITIONAL_PROXIMITY_COST = 0.380f;
-const float ScoringParams::SUBSTITUTION_COST = 0.363f;
-const float ScoringParams::COST_NEW_WORD = 0.054f;
-const float ScoringParams::COST_NEW_WORD_CAPITALIZED = 0.174f;
+const float ScoringParams::SUBSTITUTION_COST = 0.403f;
+const float ScoringParams::COST_NEW_WORD = 0.042f;
+const float ScoringParams::COST_SECOND_OR_LATER_WORD_FIRST_CHAR_UPPERCASE = 0.25f;
 const float ScoringParams::DISTANCE_WEIGHT_LANGUAGE = 1.123f;
-const float ScoringParams::COST_FIRST_LOOKAHEAD = 0.462f;
-const float ScoringParams::COST_LOOKAHEAD = 0.092f;
-const float ScoringParams::HAS_PROXIMITY_TERMINAL_COST = 0.126f;
-const float ScoringParams::HAS_EDIT_CORRECTION_TERMINAL_COST = 0.056f;
-const float ScoringParams::HAS_MULTI_WORD_TERMINAL_COST = 0.136f;
+const float ScoringParams::COST_FIRST_LOOKAHEAD = 0.545f;
+const float ScoringParams::COST_LOOKAHEAD = 0.073f;
+const float ScoringParams::HAS_PROXIMITY_TERMINAL_COST = 0.105f;
+const float ScoringParams::HAS_EDIT_CORRECTION_TERMINAL_COST = 0.038f;
+const float ScoringParams::HAS_MULTI_WORD_TERMINAL_COST = 0.444f;
 const float ScoringParams::TYPING_BASE_OUTPUT_SCORE = 1.0f;
 const float ScoringParams::TYPING_MAX_OUTPUT_SCORE_PER_INPUT = 0.1f;
-const float ScoringParams::MAX_NORM_DISTANCE_FOR_EDIT = 0.1f;
+const float ScoringParams::NORMALIZED_SPATIAL_DISTANCE_THRESHOLD_FOR_EDIT = 0.06f;
 } // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.h b/native/jni/src/suggest/policyimpl/typing/scoring_params.h
index 8f104b3..53ac999 100644
--- a/native/jni/src/suggest/policyimpl/typing/scoring_params.h
+++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.h
@@ -48,7 +48,7 @@
     static const float ADDITIONAL_PROXIMITY_COST;
     static const float SUBSTITUTION_COST;
     static const float COST_NEW_WORD;
-    static const float COST_NEW_WORD_CAPITALIZED;
+    static const float COST_SECOND_OR_LATER_WORD_FIRST_CHAR_UPPERCASE;
     static const float DISTANCE_WEIGHT_LANGUAGE;
     static const float COST_FIRST_LOOKAHEAD;
     static const float COST_LOOKAHEAD;
@@ -57,7 +57,7 @@
     static const float HAS_MULTI_WORD_TERMINAL_COST;
     static const float TYPING_BASE_OUTPUT_SCORE;
     static const float TYPING_MAX_OUTPUT_SCORE_PER_INPUT;
-    static const float MAX_NORM_DISTANCE_FOR_EDIT;
+    static const float NORMALIZED_SPATIAL_DISTANCE_THRESHOLD_FOR_EDIT;
 
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(ScoringParams);
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h
index 9f83474..12110d5 100644
--- a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h
+++ b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h
@@ -39,14 +39,21 @@
 
     AK_FORCE_INLINE bool allowsErrorCorrections(const DicNode *const dicNode) const {
         return dicNode->getNormalizedSpatialDistance()
-                < ScoringParams::MAX_NORM_DISTANCE_FOR_EDIT;
+                < ScoringParams::NORMALIZED_SPATIAL_DISTANCE_THRESHOLD_FOR_EDIT;
     }
 
     AK_FORCE_INLINE bool isOmission(const DicTraverseSession *const traverseSession,
-            const DicNode *const dicNode, const DicNode *const childDicNode) const {
+            const DicNode *const dicNode, const DicNode *const childDicNode,
+            const bool allowsErrorCorrections) const {
         if (!CORRECT_OMISSION) {
             return false;
         }
+        // Note: Always consider intentional omissions (like apostrophes) since they are common.
+        const bool canConsiderOmission =
+                allowsErrorCorrections || childDicNode->canBeIntentionalOmission();
+        if (!canConsiderOmission) {
+            return false;
+        }
         const int inputSize = traverseSession->getInputSize();
         // TODO: Don't refer to isCompletion?
         if (dicNode->isCompletion(inputSize)) {
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp b/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp
index 1500341..e4c69d1 100644
--- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp
+++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp
@@ -20,5 +20,41 @@
 #include "suggest/policyimpl/typing/scoring_params.h"
 
 namespace latinime {
+
 const TypingWeighting TypingWeighting::sInstance;
+
+ErrorType TypingWeighting::getErrorType(const CorrectionType correctionType,
+        const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode,
+        const DicNode *const dicNode) const {
+    switch (correctionType) {
+        case CT_MATCH:
+            if (isProximityDicNode(traverseSession, dicNode)) {
+                return ET_PROXIMITY_CORRECTION;
+            } else {
+                return ET_NOT_AN_ERROR;
+            }
+        case CT_ADDITIONAL_PROXIMITY:
+            return ET_PROXIMITY_CORRECTION;
+        case CT_OMISSION:
+            if (parentDicNode->canBeIntentionalOmission()) {
+                return ET_INTENTIONAL_OMISSION;
+            } else {
+                return ET_EDIT_CORRECTION;
+            }
+            break;
+        case CT_SUBSTITUTION:
+        case CT_INSERTION:
+        case CT_TRANSPOSITION:
+            return ET_EDIT_CORRECTION;
+        case CT_NEW_WORD_SPACE_OMITTION:
+        case CT_NEW_WORD_SPACE_SUBSTITUTION:
+            return ET_NEW_WORD;
+        case CT_TERMINAL:
+            return ET_NOT_AN_ERROR;
+        case CT_COMPLETION:
+            return ET_COMPLETION;
+        default:
+            return ET_NOT_AN_ERROR;
+    }
+}
 }  // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
index 74e4e34..3938c0e 100644
--- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
+++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
@@ -28,14 +28,15 @@
 
 class DicNode;
 struct DicNode_InputStateG;
+class MultiBigramMap;
 
 class TypingWeighting : public Weighting {
  public:
     static const TypingWeighting *getInstance() { return &sInstance; }
 
  protected:
-    float getTerminalSpatialCost(
-            const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
+    float getTerminalSpatialCost(const DicTraverseSession *const traverseSession,
+            const DicNode *const dicNode) const {
         float cost = 0.0f;
         if (dicNode->hasMultipleWords()) {
             cost += ScoringParams::HAS_MULTI_WORD_TERMINAL_COST;
@@ -50,13 +51,14 @@
     }
 
     float getOmissionCost(const DicNode *const parentDicNode, const DicNode *const dicNode) const {
-        bool sameCodePoint = false;
-        bool isFirstLetterOmission = false;
-        float cost = 0.0f;
-        sameCodePoint = dicNode->isSameNodeCodePoint(parentDicNode);
+        const bool isZeroCostOmission = parentDicNode->isZeroCostOmission();
+        const bool sameCodePoint = dicNode->isSameNodeCodePoint(parentDicNode);
         // If the traversal omitted the first letter then the dicNode should now be on the second.
-        isFirstLetterOmission = dicNode->getDepth() == 2;
-        if (isFirstLetterOmission) {
+        const bool isFirstLetterOmission = dicNode->getDepth() == 2;
+        float cost = 0.0f;
+        if (isZeroCostOmission) {
+            cost = 0.0f;
+        } else if (isFirstLetterOmission) {
             cost = ScoringParams::OMISSION_COST_FIRST_CHAR;
         } else {
             cost = sameCodePoint ? ScoringParams::OMISSION_COST_SAME_CHAR
@@ -65,9 +67,8 @@
         return cost;
     }
 
-    float getMatchedCost(
-            const DicTraverseSession *const traverseSession, const DicNode *const dicNode,
-            DicNode_InputStateG *inputStateG) const {
+    float getMatchedCost(const DicTraverseSession *const traverseSession,
+            const DicNode *const dicNode, DicNode_InputStateG *inputStateG) const {
         const int pointIndex = dicNode->getInputIndex(0);
         // Note: min() required since length can be MAX_POINT_TO_KEY_LENGTH for characters not on
         // the keyboard (like accented letters)
@@ -79,13 +80,23 @@
 
         const bool isFirstChar = pointIndex == 0;
         const bool isProximity = isProximityDicNode(traverseSession, dicNode);
-        const float cost = isProximity ? (isFirstChar ? ScoringParams::FIRST_PROXIMITY_COST
+        float cost = isProximity ? (isFirstChar ? ScoringParams::FIRST_PROXIMITY_COST
                 : ScoringParams::PROXIMITY_COST) : 0.0f;
+        if (dicNode->getDepth() == 2) {
+            // At the second character of the current word, we check if the first char is uppercase
+            // and the word is a second or later word of a multiple word suggestion. We demote it
+            // if so.
+            const bool isSecondOrLaterWordFirstCharUppercase =
+                    dicNode->hasMultipleWords() && dicNode->isFirstCharUppercase();
+            if (isSecondOrLaterWordFirstCharUppercase) {
+                cost += ScoringParams::COST_SECOND_OR_LATER_WORD_FIRST_CHAR_UPPERCASE;
+            }
+        }
         return weightedDistance + cost;
     }
 
-    bool isProximityDicNode(
-            const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
+    bool isProximityDicNode(const DicTraverseSession *const traverseSession,
+            const DicNode *const dicNode) const {
         const int pointIndex = dicNode->getInputIndex(0);
         const int primaryCodePoint = toBaseLowerCase(
                 traverseSession->getProximityInfoState(0)->getPrimaryCodePointAt(pointIndex));
@@ -93,9 +104,8 @@
         return primaryCodePoint != dicNodeChar;
     }
 
-    float getTranspositionCost(
-            const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode,
-            const DicNode *const dicNode) const {
+    float getTranspositionCost(const DicTraverseSession *const traverseSession,
+            const DicNode *const parentDicNode, const DicNode *const dicNode) const {
         const int16_t parentPointIndex = parentDicNode->getInputIndex(0);
         const int prevCodePoint = parentDicNode->getNodeCodePoint();
         const float distance1 = traverseSession->getProximityInfoState(0)->getPointToKeyLength(
@@ -109,8 +119,7 @@
         return ScoringParams::TRANSPOSITION_COST + weightedLengthDistance;
     }
 
-    float getInsertionCost(
-            const DicTraverseSession *const traverseSession,
+    float getInsertionCost(const DicTraverseSession *const traverseSession,
             const DicNode *const parentDicNode, const DicNode *const dicNode) const {
         const int16_t parentPointIndex = parentDicNode->getInputIndex(0);
         const int prevCodePoint =
@@ -130,17 +139,14 @@
 
     float getNewWordCost(const DicTraverseSession *const traverseSession,
             const DicNode *const dicNode) const {
-        const bool isCapitalized = dicNode->isCapitalized();
-        const float cost = isCapitalized ?
-                ScoringParams::COST_NEW_WORD_CAPITALIZED : ScoringParams::COST_NEW_WORD;
-        return cost * traverseSession->getMultiWordCostMultiplier();
+        return ScoringParams::COST_NEW_WORD * traverseSession->getMultiWordCostMultiplier();
     }
 
-    float getNewWordBigramCost(
-            const DicTraverseSession *const traverseSession, const DicNode *const dicNode,
-            hash_map_compat<int, int16_t> *const bigramCacheMap) const {
+    float getNewWordBigramCost(const DicTraverseSession *const traverseSession,
+            const DicNode *const dicNode,
+            MultiBigramMap *const multiBigramMap) const {
         return DicNodeUtils::getBigramNodeImprobability(traverseSession->getOffsetDict(),
-                dicNode, bigramCacheMap);
+                dicNode, multiBigramMap) * ScoringParams::DISTANCE_WEIGHT_LANGUAGE;
     }
 
     float getCompletionCost(const DicTraverseSession *const traverseSession,
@@ -156,21 +162,9 @@
 
     float getTerminalLanguageCost(const DicTraverseSession *const traverseSession,
             const DicNode *const dicNode, const float dicNodeLanguageImprobability) const {
-        const bool hasEditCount = dicNode->getEditCorrectionCount() > 0;
-        const bool isSameLength = dicNode->getDepth() == traverseSession->getInputSize();
-        const bool hasMultipleWords = dicNode->hasMultipleWords();
-        const bool hasProximityErrors = dicNode->getProximityCorrectionCount() > 0;
-        // Gesture input is always assumed to have proximity errors
-        // because the input word shouldn't be treated as perfect
-        const bool isExactMatch = !hasEditCount && !hasMultipleWords
-                && !hasProximityErrors && isSameLength;
-
-        const float totalPrevWordsLanguageCost = dicNode->getTotalPrevWordsLanguageCost();
-        const float languageImprobability = isExactMatch ? 0.0f : dicNodeLanguageImprobability;
-        const float languageWeight = ScoringParams::DISTANCE_WEIGHT_LANGUAGE;
-        // TODO: Caveat: The following equation should be:
-        // totalPrevWordsLanguageCost + (languageImprobability * languageWeight);
-        return (totalPrevWordsLanguageCost + languageImprobability) * languageWeight;
+        const float languageImprobability = (dicNode->isExactMatch()) ?
+                0.0f : dicNodeLanguageImprobability;
+        return languageImprobability * ScoringParams::DISTANCE_WEIGHT_LANGUAGE;
     }
 
     AK_FORCE_INLINE bool needsToNormalizeCompoundDistance() const {
@@ -185,15 +179,16 @@
         return ScoringParams::SUBSTITUTION_COST;
     }
 
-    AK_FORCE_INLINE float getSpaceSubstitutionCost(
-            const DicTraverseSession *const traverseSession,
+    AK_FORCE_INLINE float getSpaceSubstitutionCost(const DicTraverseSession *const traverseSession,
             const DicNode *const dicNode) const {
-        const bool isCapitalized = dicNode->isCapitalized();
-        const float cost = ScoringParams::SPACE_SUBSTITUTION_COST + (isCapitalized ?
-                ScoringParams::COST_NEW_WORD_CAPITALIZED : ScoringParams::COST_NEW_WORD);
+        const float cost = ScoringParams::SPACE_SUBSTITUTION_COST + ScoringParams::COST_NEW_WORD;
         return cost * traverseSession->getMultiWordCostMultiplier();
     }
 
+    ErrorType getErrorType(const CorrectionType correctionType,
+            const DicTraverseSession *const traverseSession,
+            const DicNode *const parentDicNode, const DicNode *const dicNode) const;
+
  private:
     DISALLOW_COPY_AND_ASSIGN(TypingWeighting);
     static const TypingWeighting sInstance;
diff --git a/native/jni/src/suggest/policyimpl/utils/damerau_levenshtein_edit_distance_policy.h b/native/jni/src/suggest/policyimpl/utils/damerau_levenshtein_edit_distance_policy.h
new file mode 100644
index 0000000..ec14574
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/utils/damerau_levenshtein_edit_distance_policy.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_DAEMARU_LEVENSHTEIN_EDIT_DISTANCE_POLICY_H
+#define LATINIME_DAEMARU_LEVENSHTEIN_EDIT_DISTANCE_POLICY_H
+
+#include "char_utils.h"
+#include "suggest/policyimpl/utils/edit_distance_policy.h"
+
+namespace latinime {
+
+class DamerauLevenshteinEditDistancePolicy : public EditDistancePolicy {
+ public:
+    DamerauLevenshteinEditDistancePolicy(const int *const string0, const int length0,
+            const int *const string1, const int length1)
+            : mString0(string0), mString0Length(length0), mString1(string1),
+              mString1Length(length1) {}
+    ~DamerauLevenshteinEditDistancePolicy() {}
+
+    AK_FORCE_INLINE float getSubstitutionCost(const int index0, const int index1) const {
+        const int c0 = toBaseLowerCase(mString0[index0]);
+        const int c1 = toBaseLowerCase(mString1[index1]);
+        return (c0 == c1) ? 0.0f : 1.0f;
+    }
+
+    AK_FORCE_INLINE float getDeletionCost(const int index0, const int index1) const {
+        return 1.0f;
+    }
+
+    AK_FORCE_INLINE float getInsertionCost(const int index0, const int index1) const {
+        return 1.0f;
+    }
+
+    AK_FORCE_INLINE bool allowTransposition(const int index0, const int index1) const {
+        const int c0 = toBaseLowerCase(mString0[index0]);
+        const int c1 = toBaseLowerCase(mString1[index1]);
+        if (index0 > 0 && index1 > 0 && c0 == toBaseLowerCase(mString1[index1 - 1])
+                && c1 == toBaseLowerCase(mString0[index0 - 1])) {
+            return true;
+        }
+        return false;
+    }
+
+    AK_FORCE_INLINE float getTranspositionCost(const int index0, const int index1) const {
+        return getSubstitutionCost(index0, index1);
+    }
+
+    AK_FORCE_INLINE int getString0Length() const {
+        return mString0Length;
+    }
+
+    AK_FORCE_INLINE int getString1Length() const {
+        return mString1Length;
+    }
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN (DamerauLevenshteinEditDistancePolicy);
+
+    const int *const mString0;
+    const int mString0Length;
+    const int *const mString1;
+    const int mString1Length;
+};
+} // namespace latinime
+
+#endif  // LATINIME_DAEMARU_LEVENSHTEIN_EDIT_DISTANCE_POLICY_H
diff --git a/native/jni/src/suggest/policyimpl/utils/edit_distance.h b/native/jni/src/suggest/policyimpl/utils/edit_distance.h
new file mode 100644
index 0000000..cbbd668
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/utils/edit_distance.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_EDIT_DISTANCE_H
+#define LATINIME_EDIT_DISTANCE_H
+
+#include "defines.h"
+#include "suggest/policyimpl/utils/edit_distance_policy.h"
+
+namespace latinime {
+
+class EditDistance {
+ public:
+    // CAVEAT: There may be performance penalty if you need the edit distance as an integer value.
+    AK_FORCE_INLINE static float getEditDistance(const EditDistancePolicy *const policy) {
+        const int beforeLength = policy->getString0Length();
+        const int afterLength = policy->getString1Length();
+        float dp[(beforeLength + 1) * (afterLength + 1)];
+        for (int i = 0; i <= beforeLength; ++i) {
+            dp[(afterLength + 1) * i] = i * policy->getInsertionCost(i - 1, -1);
+        }
+        for (int i = 0; i <= afterLength; ++i) {
+            dp[i] = i * policy->getDeletionCost(-1, i - 1);
+        }
+
+        for (int i = 0; i < beforeLength; ++i) {
+            for (int j = 0; j < afterLength; ++j) {
+                dp[(afterLength + 1) * (i + 1) + (j + 1)] = min(
+                        dp[(afterLength + 1) * i + (j + 1)] + policy->getInsertionCost(i, j),
+                        min(dp[(afterLength + 1) * (i + 1) + j] + policy->getDeletionCost(i, j),
+                                dp[(afterLength + 1) * i + j]
+                                        + policy->getSubstitutionCost(i, j)));
+                if (policy->allowTransposition(i, j)) {
+                    dp[(afterLength + 1) * (i + 1) + (j + 1)] = min(
+                            dp[(afterLength + 1) * (i + 1) + (j + 1)],
+                            dp[(afterLength + 1) * (i - 1) + (j - 1)]
+                                    + policy->getTranspositionCost(i, j));
+                }
+            }
+        }
+        if (DEBUG_EDIT_DISTANCE) {
+            AKLOGI("IN = %d, OUT = %d", beforeLength, afterLength);
+            for (int i = 0; i < beforeLength + 1; ++i) {
+                for (int j = 0; j < afterLength + 1; ++j) {
+                    AKLOGI("EDIT[%d][%d], %f", i, j, dp[(afterLength + 1) * i + j]);
+                }
+            }
+        }
+        return dp[(beforeLength + 1) * (afterLength + 1) - 1];
+    }
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(EditDistance);
+};
+} // namespace latinime
+
+#endif  // LATINIME_EDIT_DISTANCE_H
diff --git a/native/jni/src/suggest/policyimpl/utils/edit_distance_policy.h b/native/jni/src/suggest/policyimpl/utils/edit_distance_policy.h
new file mode 100644
index 0000000..e3d1792
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/utils/edit_distance_policy.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_EDIT_DISTANCE_POLICY_H
+#define LATINIME_EDIT_DISTANCE_POLICY_H
+
+#include "defines.h"
+
+namespace latinime {
+
+class EditDistancePolicy {
+ public:
+    virtual float getSubstitutionCost(const int index0, const int index1) const = 0;
+    virtual float getDeletionCost(const int index0, const int index1) const = 0;
+    virtual float getInsertionCost(const int index0, const int index1) const = 0;
+    virtual bool allowTransposition(const int index0, const int index1) const = 0;
+    virtual float getTranspositionCost(const int index0, const int index1) const = 0;
+    virtual int getString0Length() const = 0;
+    virtual int getString1Length() const = 0;
+
+ protected:
+    EditDistancePolicy() {}
+    virtual ~EditDistancePolicy() {}
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(EditDistancePolicy);
+};
+} // namespace latinime
+
+#endif  // LATINIME_EDIT_DISTANCE_POLICY_H
diff --git a/native/jni/src/terminal_attributes.h b/native/jni/src/terminal_attributes.h
index 144ae14..92ef71c 100644
--- a/native/jni/src/terminal_attributes.h
+++ b/native/jni/src/terminal_attributes.h
@@ -72,7 +72,7 @@
     }
 
     bool isBlacklistedOrNotAWord() const {
-        return mFlags & (BinaryFormat::FLAG_IS_BLACKLISTED | BinaryFormat::FLAG_IS_NOT_A_WORD);
+        return BinaryFormat::hasBlacklistedOrNotAWordFlag(mFlags);
     }
 
  private:
diff --git a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java
index 6bb5ada..01814ae 100644
--- a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java
@@ -17,11 +17,11 @@
 package com.android.inputmethod.keyboard;
 
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.MediumTest;
 
 import com.android.inputmethod.keyboard.MoreKeysKeyboard.MoreKeysKeyboardParams;
 
-@SmallTest
+@MediumTest
 public class MoreKeysKeyboardBuilderFixedOrderTests extends AndroidTestCase {
     private static final int WIDTH = 10;
     private static final int HEIGHT = 10;
diff --git a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderTests.java b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderTests.java
index 99da481..ce5573d 100644
--- a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderTests.java
@@ -17,11 +17,11 @@
 package com.android.inputmethod.keyboard;
 
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.MediumTest;
 
 import com.android.inputmethod.keyboard.MoreKeysKeyboard.MoreKeysKeyboardParams;
 
-@SmallTest
+@MediumTest
 public class MoreKeysKeyboardBuilderTests extends AndroidTestCase {
     private static final int WIDTH = 10;
     private static final int HEIGHT = 10;
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java
index d05aabf..9014e7c 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java
@@ -18,7 +18,7 @@
 
 import android.app.Instrumentation;
 import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.MediumTest;
 
 import com.android.inputmethod.latin.CollectionUtils;
 import com.android.inputmethod.latin.StringUtils;
@@ -28,7 +28,7 @@
 import java.util.Arrays;
 import java.util.Locale;
 
-@SmallTest
+@MediumTest
 public class KeySpecParserCsvTests extends InstrumentationTestCase {
     private final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
 
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java
index df9ce5e..d5b9d1d 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java
@@ -352,30 +352,34 @@
         // Alphabet -> shift key + letter -> alphabet.
         // Press and slide from shift key, enter alphabet shifted.
         pressAndSlideFromKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
-        // Enter/release letter key, switch back to alphabet.
-        pressAndReleaseKey('Z', ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED);
+        // Enter/release letter keys, switch back to alphabet.
+        pressAndSlideFromKey('A', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
+        stopSlidingOnKey('Z', ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED);
 
         // Alphabet -> "?123" key + letter -> alphabet.
         // Press and slide from "123?" key, enter symbols.
         pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
-        // Enter/release into symbol letter key, switch back to alphabet.
-        pressAndReleaseKey('!', SYMBOLS_UNSHIFTED, ALPHABET_UNSHIFTED);
+        // Enter/release into symbol letter keys, switch back to alphabet.
+        pressAndSlideFromKey('@', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        stopSlidingOnKey('!', SYMBOLS_UNSHIFTED, ALPHABET_UNSHIFTED);
 
         // Alphabet shifted -> shift key + letter -> alphabet.
         // Press/release shift key, enter alphabet shifted.
         pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
         // Press and slide from shift key, remain alphabet shifted.
         pressAndSlideFromKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
-        // Enter/release letter key, switch back to alphabet (not alphabet shifted).
-        pressAndReleaseKey('Z', ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED);
+        // Enter/release letter keys, switch back to alphabet (not alphabet shifted).
+        pressAndSlideFromKey('A', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
+        stopSlidingOnKey('Z', ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED);
 
         // Alphabet shifted -> "?123" key + letter -> alphabet.
         // Press/release shift key, enter alphabet shifted.
         pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
         // Press and slide from "123?" key, enter symbols.
         pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
-        // Enter/release into symbol letter key, switch back to alphabet (not alphabet shifted).
-        pressAndReleaseKey('!', SYMBOLS_UNSHIFTED, ALPHABET_UNSHIFTED);
+        // Enter/release into symbol letter keys, switch back to alphabet (not alphabet shifted).
+        pressAndSlideFromKey('@', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        stopSlidingOnKey('!', SYMBOLS_UNSHIFTED, ALPHABET_UNSHIFTED);
 
         // Alphabet shift locked -> shift key + letter -> alphabet shift locked.
         // Long press shift key, enter alphabet shift locked.
@@ -383,14 +387,76 @@
                 ALPHABET_SHIFT_LOCKED);
         // Press and slide from "123?" key, enter symbols.
         pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
-        // Enter/release into symbol letter key, switch back to alphabet shift locked.
-        pressAndReleaseKey('!', SYMBOLS_UNSHIFTED, ALPHABET_SHIFT_LOCKED);
+        // Enter/release into symbol letter keys, switch back to alphabet shift locked.
+        pressAndSlideFromKey('!', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        stopSlidingOnKey('!', SYMBOLS_UNSHIFTED, ALPHABET_SHIFT_LOCKED);
 
         // Alphabet shift locked -> "?123" key + letter -> alphabet shift locked.
         // Press and slide from shift key, enter alphabet shifted.
         pressAndSlideFromKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCKED);
+        // Enter/release letter keys, switch back to shift locked.
+        pressAndSlideFromKey('A', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
+        stopSlidingOnKey('Z', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
+    }
+
+    // Cancel sliding input in alphabet.
+    public void testSlidingAlphabetCancel() {
+        // Alphabet -> shift key + letter -> cancel -> alphabet.
+        // Press and slide from shift key, enter alphabet shifted.
+        pressAndSlideFromKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
+        // Press and slide from shift key, enter alphabet shifted.
+        pressAndSlideFromKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
+        // Enter/release letter key, remains in alphabet shifted.
+        pressAndSlideFromKey('Z', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
+        // Cancel sliding, switch back to alphabet.
+        stopSlidingAndCancel(ALPHABET_UNSHIFTED);
+
+        // Alphabet -> "?123" key + letter -> cancel -> alphabet.
+        // Press and slide from "123?" key, enter symbols.
+        pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Enter/release into symbol letter key, remains in symbols.
+        pressAndSlideFromKey('!', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Cancel sliding, switch back to alphabet.
+        stopSlidingAndCancel(ALPHABET_UNSHIFTED);
+
+        // Alphabet shifted -> shift key + letter -> cancel -> alphabet.
+        // Press/release shift key, enter alphabet shifted.
+        pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
+        // Press and slide from shift key, remain alphabet shifted.
+        pressAndSlideFromKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
+        // Enter/release letter key, remains in alphabet shifted.
+        pressAndSlideFromKey('Z', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
+        // Cancel sliding, switch back to alphabet (not alphabet shifted).
+        stopSlidingAndCancel(ALPHABET_UNSHIFTED);
+
+        // Alphabet shifted -> "?123" key + letter -> cancel -> alphabet.
+        // Press/release shift key, enter alphabet shifted.
+        pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
+        // Press and slide from "123?" key, enter symbols.
+        pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Enter/release into symbol letter key, remains in symbols.
+        pressAndSlideFromKey('!', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Cancel sliding, switch back to alphabet (not alphabet shifted).
+        stopSlidingAndCancel(ALPHABET_UNSHIFTED);
+
+        // Alphabet shift locked -> shift key + letter -> cancel -> alphabet shift locked.
+        // Long press shift key, enter alphabet shift locked.
+        longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED,
+                ALPHABET_SHIFT_LOCKED);
+        // Press and slide from "123?" key, enter symbols.
+        pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Enter/release into symbol letter key, remains in symbols.
+        pressAndSlideFromKey('!', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Cancel sliding, switch back to alphabet shift locked.
+        stopSlidingAndCancel( ALPHABET_SHIFT_LOCKED);
+
+        // Alphabet shift locked -> "?123" key + letter -> cancel -> alphabet shift locked.
+        // Press and slide from shift key, enter alphabet shifted.
+        pressAndSlideFromKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCKED);
+        // Enter/release letter key, remains in alphabet shift locked.
+        pressAndSlideFromKey('Z', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
         // Enter/release letter key, switch back to shift locked.
-        pressAndReleaseKey('Z', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
+        stopSlidingAndCancel(ALPHABET_SHIFT_LOCKED);
     }
 
     // Sliding input in symbols.
@@ -398,16 +464,18 @@
         // Symbols -> "=\<" key + letter -> symbols.
         // Press/release "?123" key, enter into symbols.
         pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
-        // Press and slide from shift key, enter symols shifted.
+        // Press and slide from shift key, enter symbols shifted.
         pressAndSlideFromKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
-        // Enter/release symbol shifted letter key, switch back to symbols.
-        pressAndReleaseKey('~', SYMBOLS_SHIFTED, SYMBOLS_UNSHIFTED);
+        // Enter/release symbol shifted letter keys, switch back to symbols.
+        pressAndSlideFromKey('|', SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
+        stopSlidingOnKey('~', SYMBOLS_SHIFTED, SYMBOLS_UNSHIFTED);
 
         // Symbols -> "ABC" key + letter -> Symbols.
         // Press and slide from "ABC" key, enter alphabet.
         pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
-        // Enter/release letter key, switch back to symbols.
-        pressAndReleaseKey('a', ALPHABET_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Enter/release letter keys, switch back to symbols.
+        pressAndSlideFromKey('z', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
+        stopSlidingOnKey('a', ALPHABET_UNSHIFTED, SYMBOLS_UNSHIFTED);
         // Press/release "ABC" key, switch to alphabet.
         pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
 
@@ -421,8 +489,9 @@
         pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
         // Press and slide from "ABC" key.
         pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
-        // Enter/release letter key, switch back to symbols.
-        pressAndReleaseKey('a', ALPHABET_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Enter/release letter keys, switch back to symbols.
+        pressAndSlideFromKey('z', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
+        stopSlidingOnKey('a', ALPHABET_UNSHIFTED, SYMBOLS_UNSHIFTED);
         // Press/release "ABC" key, switch to alphabet (not alphabet shifted).
         pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
 
@@ -437,8 +506,9 @@
         pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
         // Press and slide from "ABC" key, enter alphabet shift locked.
         pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
-        // Enter/release letter key, switch back to symbols.
-        pressAndReleaseKey('A', ALPHABET_SHIFT_LOCKED, SYMBOLS_UNSHIFTED);
+        // Enter/release letter keys, switch back to symbols.
+        pressAndSlideFromKey('Z', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
+        stopSlidingOnKey('A', ALPHABET_SHIFT_LOCKED, SYMBOLS_UNSHIFTED);
         // Press/release "ABC" key, switch to alphabet shift locked.
         pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
 
@@ -453,8 +523,85 @@
         pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
         // Press and slide from "=\<" key, enter symbols shifted.
         pressAndSlideFromKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
-        // Enter/release symbols shift letter key, switch back to symbols.
-        pressAndReleaseKey('~', SYMBOLS_SHIFTED, SYMBOLS_UNSHIFTED);
+        // Enter/release symbols shift letter keys, switch back to symbols.
+        pressAndSlideFromKey('|', SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
+        stopSlidingOnKey('~', SYMBOLS_SHIFTED, SYMBOLS_UNSHIFTED);
+        // Press/release "ABC" key, switch to alphabet shift locked.
+        pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
+    }
+
+    // Cancel sliding input in symbols.
+    public void testSlidingSymbolsCancel() {
+        // Symbols -> "=\<" key + letter -> cancel -> symbols.
+        // Press/release "?123" key, enter into symbols.
+        pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Press and slide from shift key, enter symbols shifted.
+        pressAndSlideFromKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
+        // Enter/release symbol shifted letter key, remains in symbols shifted.
+        pressAndSlideFromKey('|', SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
+        // Cancel sliding, switch back to symbols.
+        stopSlidingAndCancel(SYMBOLS_UNSHIFTED);
+
+        // Symbols -> "ABC" key + letter -> Symbols.
+        // Press and slide from "ABC" key, enter alphabet.
+        pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
+        // Enter/release letter keys, remains in alphabet.
+        pressAndSlideFromKey('z', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
+        // Cancel sliding, switch back to symbols.
+        stopSlidingAndCancel(SYMBOLS_UNSHIFTED);
+        // Press/release "ABC" key, switch to alphabet.
+        pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
+
+        // Alphabet shifted -> symbols -> "ABC" key + letter -> symbols ->
+        // alphabet.
+        // Load keyboard
+        loadKeyboard(ALPHABET_UNSHIFTED);
+        // Press/release shift key, enter alphabet shifted.
+        pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
+        // Press/release "?123" key, enter into symbols.
+        pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Press and slide from "ABC" key.
+        pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
+        // Enter/release letter key, remains in alphabet.
+        pressAndSlideFromKey('z', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
+        // Cancel sliding, switch back to symbols.
+        stopSlidingAndCancel(SYMBOLS_UNSHIFTED);
+        // Press/release "ABC" key, switch to alphabet (not alphabet shifted).
+        pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
+
+        // Alphabet shift locked -> symbols -> "ABC" key + letter -> symbols ->
+        // alphabet shift locked.
+        // Load keyboard
+        loadKeyboard(ALPHABET_UNSHIFTED);
+        // Long press shift key, enter alphabet shift locked.
+        longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED,
+                ALPHABET_SHIFT_LOCKED);
+        // Press/release "?123" key, enter into symbols.
+        pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Press and slide from "ABC" key, enter alphabet shift locked.
+        pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
+        // Enter/release letter key, remains in alphabet shifted.
+        pressAndSlideFromKey('Z', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
+        // Cancel sliding, switch back to symbols.
+        stopSlidingAndCancel(SYMBOLS_UNSHIFTED);
+        // Press/release "ABC" key, switch to alphabet shift locked.
+        pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
+
+        // Alphabet shift locked -> symbols -> "=\<" key + letter -> symbols ->
+        // alphabet shift locked.
+        // Load keyboard
+        loadKeyboard(ALPHABET_UNSHIFTED);
+        // Long press shift key, enter alphabet shift locked.
+        longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED,
+                ALPHABET_SHIFT_LOCKED);
+        // Press/release "?123" key, enter into symbols.
+        pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Press and slide from "=\<" key, enter symbols shifted.
+        pressAndSlideFromKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
+        // Enter/release symbols shift letter key, remains in symbols shifted.
+        pressAndSlideFromKey('|', SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
+        // Cancel sliding, switch back to symbols.
+        stopSlidingAndCancel(SYMBOLS_UNSHIFTED);
         // Press/release "ABC" key, switch to alphabet shift locked.
         pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
     }
@@ -468,14 +615,16 @@
         pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
         // Press and slide from shift key, enter symbols.
         pressAndSlideFromKey(CODE_SHIFT, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
-        // Enter/release symbol letter key, switch back to symbols shifted.
-        pressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_SHIFTED);
+        // Enter/release symbol letter keys, switch back to symbols shifted.
+        pressAndSlideFromKey('2', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        stopSlidingOnKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_SHIFTED);
 
         // Symbols shifted -> "ABC" key + letter -> symbols shifted.
         // Press and slide from "ABC" key, enter alphabet.
         pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
-        // Enter/release letter key, switch back to symbols shifted.
-        pressAndReleaseKey('a', ALPHABET_UNSHIFTED, SYMBOLS_SHIFTED);
+        // Enter/release letter keys, switch back to symbols shifted.
+        pressAndSlideFromKey('z', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
+        stopSlidingOnKey('a', ALPHABET_UNSHIFTED, SYMBOLS_SHIFTED);
         // Press/release "ABC" key, switch to alphabet.
         pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
 
@@ -491,8 +640,9 @@
         pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
         // Press and slide from "ABC" key.
         pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
-        // Enter/release letter key, switch back to symbols shifted.
-        pressAndReleaseKey('a', ALPHABET_UNSHIFTED, SYMBOLS_SHIFTED);
+        // Enter/release letter keys, switch back to symbols shifted.
+        pressAndSlideFromKey('z', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
+        stopSlidingOnKey('a', ALPHABET_UNSHIFTED, SYMBOLS_SHIFTED);
         // Press/release "ABC" key, switch to alphabet (not alphabet shifted).
         pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
 
@@ -509,8 +659,9 @@
         pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
         // Press and slide from "ABC" key.
         pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
-        // Enter/release letter key, switch back to symbols shifted.
-        pressAndReleaseKey('A', ALPHABET_SHIFT_LOCKED, SYMBOLS_SHIFTED);
+        // Enter/release letter keys, switch back to symbols shifted.
+        pressAndSlideFromKey('Z', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
+        stopSlidingOnKey('A', ALPHABET_SHIFT_LOCKED, SYMBOLS_SHIFTED);
         // Press/release "ABC" key, switch to alphabet shift locked.
         pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
 
@@ -527,8 +678,93 @@
         pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
         // Press and slide from "?123" key.
         pressAndSlideFromKey(CODE_SHIFT, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
-        // Enter/release symbol letter key, switch back to symbols shifted.
-        pressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_SHIFTED);
+        // Enter/release symbol letter keys, switch back to symbols shifted.
+        pressAndSlideFromKey('2', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        stopSlidingOnKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_SHIFTED);
+        // Press/release "ABC" key, switch to alphabet shift locked.
+        pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
+    }
+
+    // Cancel sliding input in symbols shifted.
+    public void testSlidingSymbolsShiftedCancel() {
+        // Symbols shifted -> "?123" + letter -> symbols shifted.
+        // Press/release "?123" key, enter into symbols.
+        pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Press/release "=\<" key, enter into symbols shifted.
+        pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
+        // Press and slide from shift key, enter symbols.
+        pressAndSlideFromKey(CODE_SHIFT, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Enter/release symbol letter key, remains in symbols.
+        pressAndSlideFromKey('2', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Cancel sliding, switch back to symbols shifted.
+        stopSlidingAndCancel(SYMBOLS_SHIFTED);
+
+        // Symbols shifted -> "ABC" key + letter -> symbols shifted.
+        // Press and slide from "ABC" key, enter alphabet.
+        pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
+        // Enter/release letter key, remains in alphabet.
+        pressAndSlideFromKey('z', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
+        // Cancel sliding, switch back to symbols shifted.
+        stopSlidingAndCancel(SYMBOLS_SHIFTED);
+        // Press/release "ABC" key, switch to alphabet.
+        pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
+
+        // Alphabet shifted -> symbols shifted -> "ABC" + letter -> symbols shifted ->
+        // alphabet.
+        // Load keyboard
+        loadKeyboard(ALPHABET_UNSHIFTED);
+        // Press/release shift key, enter alphabet shifted.
+        pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
+        // Press/release "?123" key, enter into symbols.
+        pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Press/release "=\<" key, enter into symbols shifted.
+        pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
+        // Press and slide from "ABC" key.
+        pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
+        // Enter/release letter key, remains in alphabet.
+        pressAndSlideFromKey('z', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
+        // Cancel sliding, switch back to symbols shifted.
+        stopSlidingAndCancel(SYMBOLS_SHIFTED);
+        // Press/release "ABC" key, switch to alphabet (not alphabet shifted).
+        pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
+
+        // Alphabet shift locked -> symbols shifted -> "ABC" + letter -> symbols shifted ->
+        // alphabet shift locked.
+        // Load keyboard
+        loadKeyboard(ALPHABET_UNSHIFTED);
+        // Long press shift key, enter alphabet shift locked.
+        longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED,
+                ALPHABET_SHIFT_LOCKED);
+        // Press/release "?123" key, enter into symbols.
+        pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Press/release "=\<" key, enter into symbols shifted.
+        pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
+        // Press and slide from "ABC" key.
+        pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
+        // Enter/release letter key, remains in alphabet shift locked.
+        pressAndSlideFromKey('Z', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
+        // Cancel sliding, switch back to symbols shifted.
+        stopSlidingAndCancel(SYMBOLS_SHIFTED);
+        // Press/release "ABC" key, switch to alphabet shift locked.
+        pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
+
+        // Alphabet shift locked -> symbols shifted -> "?123" + letter -> symbols shifted ->
+        // alphabet shift locked.
+        // Load keyboard
+        loadKeyboard(ALPHABET_UNSHIFTED);
+        // Long press shift key, enter alphabet shift locked.
+        longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED,
+                ALPHABET_SHIFT_LOCKED);
+        // Press/release "?123" key, enter into symbols.
+        pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Press/release "=\<" key, enter into symbols shifted.
+        pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
+        // Press and slide from "?123" key.
+        pressAndSlideFromKey(CODE_SHIFT, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Enter/release symbol letter key, remains in symbols.
+        pressAndSlideFromKey('2', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Cancel sliding, switch back to symbols shifted.
+        stopSlidingAndCancel(SYMBOLS_SHIFTED);
         // Press/release "ABC" key, switch to alphabet shift locked.
         pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
     }
@@ -597,6 +833,17 @@
         // Rotate device, remain in alphabet.
         rotateDevice(ALPHABET_UNSHIFTED);
 
+        // Alphabet automatic shifted -> rotate -> automatic shifted.
+        // Set capitalize the first character of all words mode.
+        setAutoCapsMode(CAP_MODE_WORDS);
+        // Press/release auto caps trigger letter to enter alphabet automatic shifted.
+        pressAndReleaseKey(CODE_AUTO_CAPS_TRIGGER, ALPHABET_UNSHIFTED, ALPHABET_AUTOMATIC_SHIFTED);
+        // Rotate device, remain in alphabet.
+        rotateDevice(ALPHABET_AUTOMATIC_SHIFTED);
+        setAutoCapsMode(CAP_MODE_OFF);
+        // Press/release auto caps trigger letter to reset shift state.
+        pressAndReleaseKey(CODE_AUTO_CAPS_TRIGGER, ALPHABET_AUTOMATIC_SHIFTED, ALPHABET_UNSHIFTED);
+
         // Alphabet shifted -> rotate -> alphabet shifted.
         // Press/release shift key, enter alphabet shifted.
         pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java
index 5e94aeb..e06ca06 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java
@@ -71,7 +71,7 @@
     }
 
     public void releaseKey(final int code, final int afterRelease) {
-        mSwitcher.onCodeInput(code, SINGLE);
+        mSwitcher.onCodeInput(code);
         mSwitcher.onReleaseKey(code, NOT_SLIDING);
         assertLayout("afterRelease", afterRelease, mSwitcher.getLayoutId());
     }
@@ -87,7 +87,7 @@
     }
 
     public void chordingReleaseKey(final int code, final int afterRelease) {
-        mSwitcher.onCodeInput(code, MULTI);
+        mSwitcher.onCodeInput(code);
         mSwitcher.onReleaseKey(code, NOT_SLIDING);
         assertLayout("afterRelease", afterRelease, mSwitcher.getLayoutId());
     }
@@ -104,6 +104,19 @@
         assertLayout("afterSlide", afterSlide, mSwitcher.getLayoutId());
     }
 
+    public void stopSlidingOnKey(final int code, final int afterPress, final int afterSlide) {
+        pressKey(code, afterPress);
+        mSwitcher.onCodeInput(code);
+        mSwitcher.onReleaseKey(code, NOT_SLIDING);
+        mSwitcher.onFinishSlidingInput();
+        assertLayout("afterSlide", afterSlide, mSwitcher.getLayoutId());
+    }
+
+    public void stopSlidingAndCancel(final int afterCancelSliding) {
+        mSwitcher.onFinishSlidingInput();
+        assertLayout("afterCancelSliding", afterCancelSliding, mSwitcher.getLayoutId());
+    }
+
     public void longPressKey(final int code, final int afterPress, final int afterLongPress) {
         pressKey(code, afterPress);
         mSwitcher.onLongPressTimeout(code);
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
index 74506d2..2544b6c 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
@@ -185,7 +185,7 @@
         }
     }
 
-    public void onCodeInput(final int code, final boolean isSinglePointer) {
+    public void onCodeInput(final int code) {
         if (mAutoCapsMode == MockConstants.CAP_MODE_WORDS) {
             if (Constants.isLetterCode(code)) {
                 mAutoCapsState = (code == MockConstants.CODE_AUTO_CAPS_TRIGGER)
@@ -194,10 +194,10 @@
         } else {
             mAutoCapsState = mAutoCapsMode;
         }
-        mState.onCodeInput(code, isSinglePointer, mAutoCapsState);
+        mState.onCodeInput(code, mAutoCapsState);
     }
 
-    public void onCancelInput(final boolean isSinglePointer) {
-        mState.onCancelInput(isSinglePointer);
+    public void onFinishSlidingInput() {
+        mState.onFinishSlidingInput();
     }
-}
\ No newline at end of file
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java b/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java
index d6700cf..279559c 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java
@@ -51,7 +51,7 @@
         }
 
         @Override
-        public void cancelTracking() {}
+        public void cancelTrackingForAction() {}
 
         @Override
         public String toString() {
@@ -66,23 +66,23 @@
     private final PointerTrackerQueue mQueue = new PointerTrackerQueue();
 
     public void testEmpty() {
-        assertEquals("empty queue", 0, mQueue.size());
-        assertEquals("empty queue", "[]", mQueue.toString());
+        assertEquals(0, mQueue.size());
+        assertEquals("[]", mQueue.toString());
     }
 
     public void testAdd() {
         mQueue.add(mElement1);
-        assertEquals("add element1", 1, mQueue.size());
-        assertEquals("after adding element1", "[1]", mQueue.toString());
+        assertEquals(1, mQueue.size());
+        assertEquals("[1]", mQueue.toString());
         mQueue.add(mElement2);
-        assertEquals("add element2", 2, mQueue.size());
-        assertEquals("after adding element2", "[1 2]", mQueue.toString());
+        assertEquals(2, mQueue.size());
+        assertEquals("[1 2]", mQueue.toString());
         mQueue.add(mElement3);
-        assertEquals("add element3", 3, mQueue.size());
-        assertEquals("after adding element3", "[1 2 3]", mQueue.toString());
+        assertEquals(3, mQueue.size());
+        assertEquals("[1 2 3]", mQueue.toString());
         mQueue.add(mElement4);
-        assertEquals("add element4", 4, mQueue.size());
-        assertEquals("after adding element4", "[1 2 3 4]", mQueue.toString());
+        assertEquals(4, mQueue.size());
+        assertEquals("[1 2 3 4]", mQueue.toString());
     }
 
     public void testRemove() {
@@ -94,33 +94,29 @@
         mQueue.add(mElement4);
 
         mQueue.remove(mElement2);
-        assertEquals("remove element2", 3, mQueue.size());
-        assertEquals("after removing element2", "[1 3 4]", mQueue.toString());
+        assertEquals(3, mQueue.size());
+        assertEquals("[1 3 4]", mQueue.toString());
         mQueue.remove(mElement4);
-        assertEquals("remove element4", 2, mQueue.size());
-        assertEquals("after removing element4", "[1 3]", mQueue.toString());
+        assertEquals(2, mQueue.size());
+        assertEquals("[1 3]", mQueue.toString());
         mQueue.remove(mElement4);
-        assertEquals("remove element4 again", 2, mQueue.size());
-        assertEquals("after removing element4 again", "[1 3]", mQueue.toString());
+        assertEquals(2, mQueue.size());
+        assertEquals("[1 3]", mQueue.toString());
         mQueue.remove(mElement1);
-        assertEquals("remove element1", 1, mQueue.size());
-        assertEquals("after removing element4", "[3]", mQueue.toString());
+        assertEquals(1, mQueue.size());
+        assertEquals("[3]", mQueue.toString());
         mQueue.remove(mElement3);
-        assertEquals("remove element3", 0, mQueue.size());
-        assertEquals("after removing element3", "[]", mQueue.toString());
+        assertEquals(0, mQueue.size());
+        assertEquals("[]", mQueue.toString());
         mQueue.remove(mElement1);
-        assertEquals("remove element1 again", 0, mQueue.size());
-        assertEquals("after removing element1 again", "[]", mQueue.toString());
+        assertEquals(0, mQueue.size());
+        assertEquals("[]", mQueue.toString());
 
-        assertEquals("after remove elements", 0, Element.sPhantomUpCount);
-        assertEquals("after remove element1",
-                Element.NOT_HAPPENED, mElement1.mPhantomUpEventTime);
-        assertEquals("after remove element2",
-                Element.NOT_HAPPENED, mElement2.mPhantomUpEventTime);
-        assertEquals("after remove element3",
-                Element.NOT_HAPPENED, mElement3.mPhantomUpEventTime);
-        assertEquals("after remove element4",
-                Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
+        assertEquals(0, Element.sPhantomUpCount);
+        assertEquals(Element.NOT_HAPPENED, mElement1.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement2.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement3.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
     }
 
     public void testAddAndRemove() {
@@ -132,38 +128,34 @@
         mQueue.add(mElement4);
 
         mQueue.remove(mElement2);
-        assertEquals("remove element2", 3, mQueue.size());
-        assertEquals("after removing element2", "[1 3 4]", mQueue.toString());
+        assertEquals(3, mQueue.size());
+        assertEquals("[1 3 4]", mQueue.toString());
         mQueue.remove(mElement4);
-        assertEquals("remove element4", 2, mQueue.size());
-        assertEquals("after removing element4", "[1 3]", mQueue.toString());
+        assertEquals(2, mQueue.size());
+        assertEquals("[1 3]", mQueue.toString());
         mQueue.add(mElement2);
-        assertEquals("add element2", 3, mQueue.size());
-        assertEquals("after adding element2", "[1 3 2]", mQueue.toString());
+        assertEquals(3, mQueue.size());
+        assertEquals("[1 3 2]", mQueue.toString());
         mQueue.remove(mElement4);
-        assertEquals("remove element4 again", 3, mQueue.size());
-        assertEquals("after removing element4 again", "[1 3 2]", mQueue.toString());
+        assertEquals(3, mQueue.size());
+        assertEquals("[1 3 2]", mQueue.toString());
         mQueue.remove(mElement1);
-        assertEquals("remove element1", 2, mQueue.size());
-        assertEquals("after removing element4", "[3 2]", mQueue.toString());
+        assertEquals(2, mQueue.size());
+        assertEquals("[3 2]", mQueue.toString());
         mQueue.add(mElement1);
-        assertEquals("add element1", 3, mQueue.size());
-        assertEquals("after adding element1", "[3 2 1]", mQueue.toString());
+        assertEquals(3, mQueue.size());
+        assertEquals("[3 2 1]", mQueue.toString());
         mQueue.remove(mElement3);
-        assertEquals("remove element3", 2, mQueue.size());
-        assertEquals("after removing element3", "[2 1]", mQueue.toString());
+        assertEquals(2, mQueue.size());
+        assertEquals("[2 1]", mQueue.toString());
         mQueue.remove(mElement1);
-        assertEquals("remove element1 again", 1, mQueue.size());
-        assertEquals("after removing element1 again", "[2]", mQueue.toString());
+        assertEquals(1, mQueue.size());
+        assertEquals("[2]", mQueue.toString());
 
-        assertEquals("after remove element1",
-                Element.NOT_HAPPENED, mElement1.mPhantomUpEventTime);
-        assertEquals("after remove element2",
-                Element.NOT_HAPPENED, mElement2.mPhantomUpEventTime);
-        assertEquals("after remove element3",
-                Element.NOT_HAPPENED, mElement3.mPhantomUpEventTime);
-        assertEquals("after remove element4",
-                Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement1.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement2.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement3.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
     }
 
     public void testReleaseAllPointers() {
@@ -176,20 +168,33 @@
         final long eventTime = 123;
         Element.sPhantomUpCount = 0;
         mQueue.releaseAllPointers(eventTime);
-        assertEquals("after releaseAllPointers", 4, Element.sPhantomUpCount);
-        assertEquals("after releaseAllPointers", 0, mQueue.size());
-        assertEquals("after releaseAllPointers", "[]", mQueue.toString());
-        assertEquals("after releaseAllPointers element1",
-                eventTime + 1, mElement1.mPhantomUpEventTime);
-        assertEquals("after releaseAllPointers element2",
-                eventTime + 2, mElement2.mPhantomUpEventTime);
-        assertEquals("after releaseAllPointers element3",
-                eventTime + 3, mElement3.mPhantomUpEventTime);
-        assertEquals("after releaseAllPointers element4",
-                eventTime + 4, mElement4.mPhantomUpEventTime);
+        assertEquals(4, Element.sPhantomUpCount);
+        assertEquals(0, mQueue.size());
+        assertEquals("[]", mQueue.toString());
+        assertEquals(eventTime + 1, mElement1.mPhantomUpEventTime);
+        assertEquals(eventTime + 2, mElement2.mPhantomUpEventTime);
+        assertEquals(eventTime + 3, mElement3.mPhantomUpEventTime);
+        assertEquals(eventTime + 4, mElement4.mPhantomUpEventTime);
     }
 
-    public void testReleaseAllPointersOlderThan() {
+    public void testReleaseAllPointersOlderThanFirst() {
+        mElement2.mIsModifier = true;
+        mQueue.add(mElement1);
+        mQueue.add(mElement2);
+        mQueue.add(mElement3);
+
+        final long eventTime = 123;
+        Element.sPhantomUpCount = 0;
+        mQueue.releaseAllPointersOlderThan(mElement1, eventTime);
+        assertEquals(0, Element.sPhantomUpCount);
+        assertEquals(3, mQueue.size());
+        assertEquals("[1 2 3]", mQueue.toString());
+        assertEquals(Element.NOT_HAPPENED, mElement1.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement2.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement3.mPhantomUpEventTime);
+    }
+
+    public void testReleaseAllPointersOlderThanLast() {
         mElement2.mIsModifier = true;
         mQueue.add(mElement1);
         mQueue.add(mElement2);
@@ -199,20 +204,34 @@
         final long eventTime = 123;
         Element.sPhantomUpCount = 0;
         mQueue.releaseAllPointersOlderThan(mElement4, eventTime);
-        assertEquals("after releaseAllPointersOlderThan", 2, Element.sPhantomUpCount);
-        assertEquals("after releaseAllPointersOlderThan", 2, mQueue.size());
-        assertEquals("after releaseAllPointersOlderThan", "[2 4]", mQueue.toString());
-        assertEquals("after releaseAllPointersOlderThan element1",
-                eventTime + 1, mElement1.mPhantomUpEventTime);
-        assertEquals("after releaseAllPointersOlderThan element2",
-                Element.NOT_HAPPENED, mElement2.mPhantomUpEventTime);
-        assertEquals("after releaseAllPointersOlderThan element3",
-                eventTime + 2, mElement3.mPhantomUpEventTime);
-        assertEquals("after releaseAllPointersOlderThan element4",
-                Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
+        assertEquals(2, Element.sPhantomUpCount);
+        assertEquals(2, mQueue.size());
+        assertEquals("[2 4]", mQueue.toString());
+        assertEquals(eventTime + 1, mElement1.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement2.mPhantomUpEventTime);
+        assertEquals(eventTime + 2, mElement3.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
     }
 
-    public void testReleaseAllPointersOlderThanWithoutModifier() {
+    public void testReleaseAllPointersOlderThanWithoutModifierMiddle() {
+        mQueue.add(mElement1);
+        mQueue.add(mElement2);
+        mQueue.add(mElement3);
+        mQueue.add(mElement4);
+
+        final long eventTime = 123;
+        Element.sPhantomUpCount = 0;
+        mQueue.releaseAllPointersOlderThan(mElement3, eventTime);
+        assertEquals(2, Element.sPhantomUpCount);
+        assertEquals(2, mQueue.size());
+        assertEquals("[3 4]", mQueue.toString());
+        assertEquals(eventTime + 1, mElement1.mPhantomUpEventTime);
+        assertEquals(eventTime + 2, mElement2.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement3.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
+    }
+
+    public void testReleaseAllPointersOlderThanWithoutModifierLast() {
         mQueue.add(mElement1);
         mQueue.add(mElement2);
         mQueue.add(mElement3);
@@ -221,19 +240,13 @@
         final long eventTime = 123;
         Element.sPhantomUpCount = 0;
         mQueue.releaseAllPointersOlderThan(mElement4, eventTime);
-        assertEquals("after releaseAllPointersOlderThan without modifier",
-                3, Element.sPhantomUpCount);
-        assertEquals("after releaseAllPointersOlderThan without modifier", 1, mQueue.size());
-        assertEquals("after releaseAllPointersOlderThan without modifier",
-                "[4]", mQueue.toString());
-        assertEquals("after releaseAllPointersOlderThan without modifier element1",
-                eventTime + 1, mElement1.mPhantomUpEventTime);
-        assertEquals("after releaseAllPointersOlderThan without modifier element2",
-                eventTime + 2, mElement2.mPhantomUpEventTime);
-        assertEquals("after releaseAllPointersOlderThan without modifier element3",
-                eventTime + 3, mElement3.mPhantomUpEventTime);
-        assertEquals("after releaseAllPointersOlderThan without modifier element4",
-                Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
+        assertEquals(3, Element.sPhantomUpCount);
+        assertEquals(1, mQueue.size());
+        assertEquals("[4]", mQueue.toString());
+        assertEquals(eventTime + 1, mElement1.mPhantomUpEventTime);
+        assertEquals(eventTime + 2, mElement2.mPhantomUpEventTime);
+        assertEquals(eventTime + 3, mElement3.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
     }
 
     public void testReleaseAllPointersExcept() {
@@ -246,17 +259,13 @@
         final long eventTime = 123;
         Element.sPhantomUpCount = 0;
         mQueue.releaseAllPointersExcept(mElement3, eventTime);
-        assertEquals("after releaseAllPointersExcept", 3, Element.sPhantomUpCount);
-        assertEquals("after releaseAllPointersExcept", 1, mQueue.size());
-        assertEquals("after releaseAllPointersExcept", "[3]", mQueue.toString());
-        assertEquals("after releaseAllPointersExcept element1",
-                eventTime + 1, mElement1.mPhantomUpEventTime);
-        assertEquals("after releaseAllPointersExcept element2",
-                eventTime + 2, mElement2.mPhantomUpEventTime);
-        assertEquals("after releaseAllPointersExcept element3",
-                Element.NOT_HAPPENED, mElement3.mPhantomUpEventTime);
-        assertEquals("after releaseAllPointersExcept element4",
-                eventTime + 3, mElement4.mPhantomUpEventTime);
+        assertEquals(3, Element.sPhantomUpCount);
+        assertEquals(1, mQueue.size());
+        assertEquals("[3]", mQueue.toString());
+        assertEquals(eventTime + 1, mElement1.mPhantomUpEventTime);
+        assertEquals(eventTime + 2, mElement2.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement3.mPhantomUpEventTime);
+        assertEquals(eventTime + 3, mElement4.mPhantomUpEventTime);
     }
 
     public void testHasModifierKeyOlderThan() {
@@ -268,54 +277,46 @@
         mQueue.add(mElement3);
         mQueue.add(mElement4);
 
-        assertFalse("hasModifierKeyOlderThan element1", mQueue.hasModifierKeyOlderThan(mElement1));
-        assertFalse("hasModifierKeyOlderThan element2", mQueue.hasModifierKeyOlderThan(mElement2));
-        assertFalse("hasModifierKeyOlderThan element3", mQueue.hasModifierKeyOlderThan(mElement3));
-        assertFalse("hasModifierKeyOlderThan element4", mQueue.hasModifierKeyOlderThan(mElement4));
+        assertFalse(mQueue.hasModifierKeyOlderThan(mElement1));
+        assertFalse(mQueue.hasModifierKeyOlderThan(mElement2));
+        assertFalse(mQueue.hasModifierKeyOlderThan(mElement3));
+        assertFalse(mQueue.hasModifierKeyOlderThan(mElement4));
 
         mElement2.mIsModifier = true;
-        assertFalse("hasModifierKeyOlderThan element1", mQueue.hasModifierKeyOlderThan(mElement1));
-        assertFalse("hasModifierKeyOlderThan element2", mQueue.hasModifierKeyOlderThan(mElement2));
-        assertTrue("hasModifierKeyOlderThan element3", mQueue.hasModifierKeyOlderThan(mElement3));
-        assertTrue("hasModifierKeyOlderThan element4", mQueue.hasModifierKeyOlderThan(mElement4));
+        assertFalse(mQueue.hasModifierKeyOlderThan(mElement1));
+        assertFalse(mQueue.hasModifierKeyOlderThan(mElement2));
+        assertTrue(mQueue.hasModifierKeyOlderThan(mElement3));
+        assertTrue(mQueue.hasModifierKeyOlderThan(mElement4));
 
-        assertEquals("after hasModifierKeyOlderThan", 0, Element.sPhantomUpCount);
-        assertEquals("after hasModifierKeyOlderThan", 4, mQueue.size());
-        assertEquals("after hasModifierKeyOlderThan", "[1 2 3 4]", mQueue.toString());
-        assertEquals("after hasModifierKeyOlderThan element1",
-                Element.NOT_HAPPENED, mElement1.mPhantomUpEventTime);
-        assertEquals("after hasModifierKeyOlderThan element2",
-                Element.NOT_HAPPENED, mElement2.mPhantomUpEventTime);
-        assertEquals("after hasModifierKeyOlderThan element3",
-                Element.NOT_HAPPENED, mElement3.mPhantomUpEventTime);
-        assertEquals("after hasModifierKeyOlderThan element4",
-                Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
+        assertEquals(0, Element.sPhantomUpCount);
+        assertEquals(4, mQueue.size());
+        assertEquals("[1 2 3 4]", mQueue.toString());
+        assertEquals(Element.NOT_HAPPENED, mElement1.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement2.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement3.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
     }
 
     public void testIsAnyInSlidingKeyInput() {
         Element.sPhantomUpCount = 0;
-        assertFalse("isAnyInSlidingKeyInput empty", mQueue.isAnyInSlidingKeyInput());
+        assertFalse(mQueue.isAnyInSlidingKeyInput());
 
         mQueue.add(mElement1);
         mQueue.add(mElement2);
         mQueue.add(mElement3);
         mQueue.add(mElement4);
 
-        assertFalse("isAnyInSlidingKeyInput element1", mQueue.isAnyInSlidingKeyInput());
+        assertFalse(mQueue.isAnyInSlidingKeyInput());
 
         mElement3.mIsInSlidingKeyInput = true;
-        assertTrue("isAnyInSlidingKeyInput element1", mQueue.isAnyInSlidingKeyInput());
+        assertTrue(mQueue.isAnyInSlidingKeyInput());
 
-        assertEquals("after isAnyInSlidingKeyInput", 0, Element.sPhantomUpCount);
-        assertEquals("after isAnyInSlidingKeyInput", 4, mQueue.size());
-        assertEquals("after isAnyInSlidingKeyInput", "[1 2 3 4]", mQueue.toString());
-        assertEquals("after isAnyInSlidingKeyInput element1",
-                Element.NOT_HAPPENED, mElement1.mPhantomUpEventTime);
-        assertEquals("after isAnyInSlidingKeyInput element2",
-                Element.NOT_HAPPENED, mElement2.mPhantomUpEventTime);
-        assertEquals("after isAnyInSlidingKeyInput element3",
-                Element.NOT_HAPPENED, mElement3.mPhantomUpEventTime);
-        assertEquals("after isAnyInSlidingKeyInput element4",
-                Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
+        assertEquals(0, Element.sPhantomUpCount);
+        assertEquals(4, mQueue.size());
+        assertEquals("[1 2 3 4]", mQueue.toString());
+        assertEquals(Element.NOT_HAPPENED, mElement1.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement2.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement3.mPhantomUpEventTime);
+        assertEquals(Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
     }
 }
diff --git a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java
index 7275d3a..c4fd5a0 100644
--- a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java
+++ b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java
@@ -30,7 +30,7 @@
         type(STRING_TO_TYPE);
         sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
         runMessages();
-        final SpanGetter span = new SpanGetter(mTextView.getText(), SuggestionSpan.class);
+        final SpanGetter span = new SpanGetter(mEditText.getText(), SuggestionSpan.class);
         assertEquals("show blue underline, span start", EXPECTED_SPAN_START, span.mStart);
         assertEquals("show blue underline, span end", EXPECTED_SPAN_END, span.mEnd);
         assertEquals("show blue underline, span color", true, span.isAutoCorrectionIndicator());
@@ -47,7 +47,7 @@
         type(STRING_2_TO_TYPE);
         // We haven't have time to look into the dictionary yet, so the line should still be
         // blue to avoid any flicker.
-        final SpanGetter spanBefore = new SpanGetter(mTextView.getText(), SuggestionSpan.class);
+        final SpanGetter spanBefore = new SpanGetter(mEditText.getText(), SuggestionSpan.class);
         assertEquals("extend blue underline, span start", EXPECTED_SPAN_START, spanBefore.mStart);
         assertEquals("extend blue underline, span end", EXPECTED_SPAN_END, spanBefore.mEnd);
         assertEquals("extend blue underline, span color", true,
@@ -55,7 +55,7 @@
         sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
         runMessages();
         // Now we have been able to re-evaluate the word, there shouldn't be an auto-correction span
-        final SpanGetter spanAfter = new SpanGetter(mTextView.getText(), SuggestionSpan.class);
+        final SpanGetter spanAfter = new SpanGetter(mEditText.getText(), SuggestionSpan.class);
         assertNull("hide blue underline", spanAfter.mSpan);
     }
 
@@ -76,10 +76,10 @@
         type(Constants.CODE_DELETE);
         sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
         runMessages();
-        final SpanGetter suggestionSpan = new SpanGetter(mTextView.getText(), SuggestionSpan.class);
+        final SpanGetter suggestionSpan = new SpanGetter(mEditText.getText(), SuggestionSpan.class);
         assertEquals("show no blue underline after backspace, span start should be -1",
                 EXPECTED_SUGGESTION_SPAN_START, suggestionSpan.mStart);
-        final SpanGetter underlineSpan = new SpanGetter(mTextView.getText(), UnderlineSpan.class);
+        final SpanGetter underlineSpan = new SpanGetter(mEditText.getText(), UnderlineSpan.class);
         assertEquals("should be composing, so should have an underline span",
                 EXPECTED_UNDERLINE_SPAN_START, underlineSpan.mStart);
         assertEquals("should be composing, so should have an underline span",
@@ -103,7 +103,7 @@
                 NEW_CURSOR_POSITION, NEW_CURSOR_POSITION, -1, -1);
         sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
         runMessages();
-        final SpanGetter span = new SpanGetter(mTextView.getText(), SuggestionSpan.class);
+        final SpanGetter span = new SpanGetter(mEditText.getText(), SuggestionSpan.class);
         assertNull("blue underline removed when cursor is moved", span.mSpan);
     }
 
@@ -117,7 +117,7 @@
         // Here the blue underline has been set. testBlueUnderline() is testing for this already,
         // so let's not test it here again.
         // Now simulate the user moving the cursor.
-        SpanGetter span = new SpanGetter(mTextView.getText(), UnderlineSpan.class);
+        SpanGetter span = new SpanGetter(mEditText.getText(), UnderlineSpan.class);
         assertNull("should not be composing, so should not have an underline span", span.mSpan);
     }
 }
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index 72c8d9c..9140197 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -24,7 +24,7 @@
     public void testTypeWord() {
         final String WORD_TO_TYPE = "abcd";
         type(WORD_TO_TYPE);
-        assertEquals("type word", WORD_TO_TYPE, mTextView.getText().toString());
+        assertEquals("type word", WORD_TO_TYPE, mEditText.getText().toString());
     }
 
     public void testPickSuggestionThenBackspace() {
@@ -35,7 +35,7 @@
         mLatinIME.onUpdateSelection(0, 0, WORD_TO_TYPE.length(), WORD_TO_TYPE.length(), -1, -1);
         type(Constants.CODE_DELETE);
         assertEquals("press suggestion then backspace", EXPECTED_RESULT,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
     }
 
     public void testPickAutoCorrectionThenBackspace() {
@@ -48,10 +48,10 @@
         pickSuggestionManually(0, WORD_TO_PICK);
         mLatinIME.onUpdateSelection(0, 0, WORD_TO_TYPE.length(), WORD_TO_TYPE.length(), -1, -1);
         assertEquals("pick typed word over auto-correction then backspace", WORD_TO_PICK,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
         type(Constants.CODE_DELETE);
         assertEquals("pick typed word over auto-correction then backspace", EXPECTED_RESULT,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
     }
 
     public void testPickTypedWordOverAutoCorrectionThenBackspace() {
@@ -63,10 +63,10 @@
         pickSuggestionManually(1, WORD_TO_TYPE);
         mLatinIME.onUpdateSelection(0, 0, WORD_TO_TYPE.length(), WORD_TO_TYPE.length(), -1, -1);
         assertEquals("pick typed word over auto-correction then backspace", WORD_TO_TYPE,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
         type(Constants.CODE_DELETE);
         assertEquals("pick typed word over auto-correction then backspace", EXPECTED_RESULT,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
     }
 
     public void testPickDifferentSuggestionThenBackspace() {
@@ -79,10 +79,10 @@
         pickSuggestionManually(2, WORD_TO_PICK);
         mLatinIME.onUpdateSelection(0, 0, WORD_TO_TYPE.length(), WORD_TO_TYPE.length(), -1, -1);
         assertEquals("pick different suggestion then backspace", WORD_TO_PICK,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
         type(Constants.CODE_DELETE);
         assertEquals("pick different suggestion then backspace", EXPECTED_RESULT,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
     }
 
     public void testDeleteSelection() {
@@ -102,7 +102,7 @@
         mLatinIME.onUpdateSelection(typedLength, typedLength,
                 SELECTION_START, SELECTION_END, -1, -1);
         type(Constants.CODE_DELETE);
-        assertEquals("delete selection", EXPECTED_RESULT, mTextView.getText().toString());
+        assertEquals("delete selection", EXPECTED_RESULT, mEditText.getText().toString());
     }
 
     public void testDeleteSelectionTwice() {
@@ -123,21 +123,21 @@
                 SELECTION_START, SELECTION_END, -1, -1);
         type(Constants.CODE_DELETE);
         type(Constants.CODE_DELETE);
-        assertEquals("delete selection twice", EXPECTED_RESULT, mTextView.getText().toString());
+        assertEquals("delete selection twice", EXPECTED_RESULT, mEditText.getText().toString());
     }
 
     public void testAutoCorrect() {
         final String STRING_TO_TYPE = "tgis ";
         final String EXPECTED_RESULT = "this ";
         type(STRING_TO_TYPE);
-        assertEquals("simple auto-correct", EXPECTED_RESULT, mTextView.getText().toString());
+        assertEquals("simple auto-correct", EXPECTED_RESULT, mEditText.getText().toString());
     }
 
     public void testAutoCorrectWithPeriod() {
         final String STRING_TO_TYPE = "tgis.";
         final String EXPECTED_RESULT = "this.";
         type(STRING_TO_TYPE);
-        assertEquals("auto-correct with period", EXPECTED_RESULT, mTextView.getText().toString());
+        assertEquals("auto-correct with period", EXPECTED_RESULT, mEditText.getText().toString());
     }
 
     public void testAutoCorrectWithPeriodThenRevert() {
@@ -147,7 +147,7 @@
         mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1);
         type(Constants.CODE_DELETE);
         assertEquals("auto-correct with period then revert", EXPECTED_RESULT,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
     }
 
     public void testAutoCorrectWithSpaceThenRevert() {
@@ -157,7 +157,7 @@
         mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1);
         type(Constants.CODE_DELETE);
         assertEquals("auto-correct with space then revert", EXPECTED_RESULT,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
     }
 
     public void testAutoCorrectToSelfDoesNotRevert() {
@@ -167,14 +167,14 @@
         mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1);
         type(Constants.CODE_DELETE);
         assertEquals("auto-correct with space does not revert", EXPECTED_RESULT,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
     }
 
     public void testDoubleSpace() {
         final String STRING_TO_TYPE = "this  ";
         final String EXPECTED_RESULT = "this. ";
         type(STRING_TO_TYPE);
-        assertEquals("double space make a period", EXPECTED_RESULT, mTextView.getText().toString());
+        assertEquals("double space make a period", EXPECTED_RESULT, mEditText.getText().toString());
     }
 
     public void testCancelDoubleSpace() {
@@ -182,7 +182,7 @@
         final String EXPECTED_RESULT = "this  ";
         type(STRING_TO_TYPE);
         type(Constants.CODE_DELETE);
-        assertEquals("double space make a period", EXPECTED_RESULT, mTextView.getText().toString());
+        assertEquals("double space make a period", EXPECTED_RESULT, mEditText.getText().toString());
     }
 
     public void testBackspaceAtStartAfterAutocorrect() {
@@ -197,7 +197,7 @@
                 NEW_CURSOR_POSITION, NEW_CURSOR_POSITION, -1, -1);
         type(Constants.CODE_DELETE);
         assertEquals("auto correct then move cursor to start of line then backspace",
-                EXPECTED_RESULT, mTextView.getText().toString());
+                EXPECTED_RESULT, mEditText.getText().toString());
     }
 
     public void testAutoCorrectThenMoveCursorThenBackspace() {
@@ -212,7 +212,7 @@
                 NEW_CURSOR_POSITION, NEW_CURSOR_POSITION, -1, -1);
         type(Constants.CODE_DELETE);
         assertEquals("auto correct then move cursor then backspace",
-                EXPECTED_RESULT, mTextView.getText().toString());
+                EXPECTED_RESULT, mEditText.getText().toString());
     }
 
     public void testNoSpaceAfterManualPick() {
@@ -221,7 +221,7 @@
         type(WORD_TO_TYPE);
         pickSuggestionManually(0, WORD_TO_TYPE);
         assertEquals("no space after manual pick", EXPECTED_RESULT,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
     }
 
     public void testManualPickThenType() {
@@ -231,7 +231,7 @@
         type(WORD1_TO_TYPE);
         pickSuggestionManually(0, WORD1_TO_TYPE);
         type(WORD2_TO_TYPE);
-        assertEquals("manual pick then type", EXPECTED_RESULT, mTextView.getText().toString());
+        assertEquals("manual pick then type", EXPECTED_RESULT, mEditText.getText().toString());
     }
 
     public void testManualPickThenSeparator() {
@@ -241,7 +241,7 @@
         type(WORD1_TO_TYPE);
         pickSuggestionManually(0, WORD1_TO_TYPE);
         type(WORD2_TO_TYPE);
-        assertEquals("manual pick then separator", EXPECTED_RESULT, mTextView.getText().toString());
+        assertEquals("manual pick then separator", EXPECTED_RESULT, mEditText.getText().toString());
     }
 
     public void testManualPickThenStripperThenPick() {
@@ -254,7 +254,7 @@
         type(WORD_TO_TYPE);
         pickSuggestionManually(0, WORD_TO_TYPE);
         assertEquals("manual pick then \\n then manual pick", EXPECTED_RESULT,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
     }
 
     public void testManualPickThenSpaceThenType() {
@@ -265,7 +265,7 @@
         pickSuggestionManually(0, WORD1_TO_TYPE);
         type(WORD2_TO_TYPE);
         assertEquals("manual pick then space then type", EXPECTED_RESULT,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
     }
 
     public void testManualPickThenManualPick() {
@@ -279,7 +279,7 @@
         // to actually pass the right string.
         pickSuggestionManually(1, WORD2_TO_PICK);
         assertEquals("manual pick then manual pick", EXPECTED_RESULT,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
     }
 
     public void testDeleteWholeComposingWord() {
@@ -288,7 +288,7 @@
         for (int i = 0; i < WORD_TO_TYPE.length(); ++i) {
             type(Constants.CODE_DELETE);
         }
-        assertEquals("delete whole composing word", "", mTextView.getText().toString());
+        assertEquals("delete whole composing word", "", mEditText.getText().toString());
     }
     // TODO: Add some tests for non-BMP characters
 }
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java
index 333b602..2d736e3 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java
@@ -30,7 +30,7 @@
         changeLanguage("fr");
         type(STRING_TO_TYPE);
         assertEquals("simple auto-correct for French", EXPECTED_RESULT,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
     }
 
     public void testManualPickThenSeparatorForFrench() {
@@ -42,7 +42,7 @@
         pickSuggestionManually(0, WORD1_TO_TYPE);
         type(WORD2_TO_TYPE);
         assertEquals("manual pick then separator for French", EXPECTED_RESULT,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
     }
 
     public void testWordThenSpaceThenPunctuationFromStripTwiceForFrench() {
@@ -64,7 +64,7 @@
             pickSuggestionManually(0, PUNCTUATION_FROM_STRIP);
             pickSuggestionManually(0, PUNCTUATION_FROM_STRIP);
             assertEquals("type word then type space then punctuation from strip twice for French",
-                    EXPECTED_RESULT, mTextView.getText().toString());
+                    EXPECTED_RESULT, mEditText.getText().toString());
         } finally {
             setBooleanPreference(NEXT_WORD_PREDICTION_OPTION, previousNextWordPredictionOption,
                     defaultNextWordPredictionOption);
@@ -98,7 +98,7 @@
         changeLanguage("de");
         type(STRING_TO_TYPE);
         assertEquals("simple auto-correct for German", EXPECTED_RESULT,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
     }
 
     public void testAutoCorrectWithUmlautForGerman() {
@@ -107,6 +107,6 @@
         changeLanguage("de");
         type(STRING_TO_TYPE);
         assertEquals("auto-correct with umlaut for German", EXPECTED_RESULT,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
     }
 }
diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
index 9e107a4..aec4aac 100644
--- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java
+++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.os.Looper;
-import android.os.MessageQueue;
 import android.preference.PreferenceManager;
 import android.test.ServiceTestCase;
 import android.text.InputType;
@@ -31,8 +30,8 @@
 import android.view.ViewGroup;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
+import android.widget.EditText;
 import android.widget.FrameLayout;
-import android.widget.TextView;
 
 import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.Keyboard;
@@ -40,7 +39,7 @@
 
 import java.util.Locale;
 
-public class InputTestsBase extends ServiceTestCase<LatinIME> {
+public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> {
 
     private static final String PREF_DEBUG_MODE = "debug_mode";
 
@@ -49,7 +48,7 @@
 
     protected LatinIME mLatinIME;
     protected Keyboard mKeyboard;
-    protected MyTextView mTextView;
+    protected MyEditText mEditText;
     protected View mInputView;
     protected InputConnection mInputConnection;
 
@@ -88,26 +87,41 @@
         }
     }
 
-    // A helper class to increase control over the TextView
-    public static class MyTextView extends TextView {
+    // A helper class to increase control over the EditText
+    public static class MyEditText extends EditText {
         public Locale mCurrentLocale;
-        public MyTextView(final Context c) {
+        public MyEditText(final Context c) {
             super(c);
         }
+
+        @Override
         public void onAttachedToWindow() {
+            // Make onAttachedToWindow "public"
             super.onAttachedToWindow();
         }
+
+        // overriding hidden API in EditText
         public Locale getTextServicesLocale() {
-            // This method is necessary because TextView is asking this method for the language
+            // This method is necessary because EditText is asking this method for the language
             // to check the spell in. If we don't override this, the spell checker will run in
             // whatever language the keyboard is currently set on the test device, ignoring any
             // settings we do inside the tests.
             return mCurrentLocale;
         }
+
+        // overriding hidden API in EditText
+        public Locale getSpellCheckerLocale() {
+            // This method is necessary because EditText is asking this method for the language
+            // to check the spell in. If we don't override this, the spell checker will run in
+            // whatever language the keyboard is currently set on the test device, ignoring any
+            // settings we do inside the tests.
+            return mCurrentLocale;
+        }
+
     }
 
     public InputTestsBase() {
-        super(LatinIME.class);
+        super(LatinIMEForTests.class);
     }
 
     // TODO: Isn't there a way to make this generic somehow? We can take a <T> and return a <T>
@@ -130,18 +144,18 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mTextView = new MyTextView(getContext());
+        mEditText = new MyEditText(getContext());
         final int inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
                 | InputType.TYPE_TEXT_FLAG_MULTI_LINE;
-        mTextView.setInputType(inputType);
-        mTextView.setEnabled(true);
+        mEditText.setInputType(inputType);
+        mEditText.setEnabled(true);
         setupService();
         mLatinIME = getService();
         final boolean previousDebugSetting = setDebugMode(true);
         mLatinIME.onCreate();
         setDebugMode(previousDebugSetting);
         final EditorInfo ei = new EditorInfo();
-        final InputConnection ic = mTextView.onCreateInputConnection(ei);
+        final InputConnection ic = mEditText.onCreateInputConnection(ei);
         final LayoutInflater inflater =
                 (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         final ViewGroup vg = new FrameLayout(getContext());
@@ -225,8 +239,8 @@
     }
 
     protected void changeLanguage(final String locale) {
-        mTextView.mCurrentLocale = LocaleUtils.constructLocaleFromString(locale);
-        SubtypeSwitcher.getInstance().forceLocale(mTextView.mCurrentLocale);
+        mEditText.mCurrentLocale = LocaleUtils.constructLocaleFromString(locale);
+        SubtypeSwitcher.getInstance().forceLocale(mEditText.mCurrentLocale);
         mLatinIME.loadKeyboard();
         mKeyboard = mLatinIME.mKeyboardSwitcher.getKeyboard();
         waitForDictionaryToBeLoaded();
diff --git a/tests/src/com/android/inputmethod/latin/LatinIMEForTests.java b/tests/src/com/android/inputmethod/latin/LatinIMEForTests.java
new file mode 100644
index 0000000..e47c557
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/LatinIMEForTests.java
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+public class LatinIMEForTests extends LatinIME {
+    @Override
+    public boolean isInputViewShown() {
+        return true;
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/PunctuationTests.java b/tests/src/com/android/inputmethod/latin/PunctuationTests.java
index 1b2f0e6..84ff6b3 100644
--- a/tests/src/com/android/inputmethod/latin/PunctuationTests.java
+++ b/tests/src/com/android/inputmethod/latin/PunctuationTests.java
@@ -44,7 +44,7 @@
             pickSuggestionManually(0, PUNCTUATION_FROM_STRIP);
             pickSuggestionManually(0, PUNCTUATION_FROM_STRIP);
             assertEquals("type word then type space then punctuation from strip twice",
-                    EXPECTED_RESULT, mTextView.getText().toString());
+                    EXPECTED_RESULT, mEditText.getText().toString());
         } finally {
             setBooleanPreference(NEXT_WORD_PREDICTION_OPTION, previousNextWordPredictionOption,
                     defaultNextWordPredictionOption);
@@ -56,7 +56,7 @@
         final String EXPECTED_RESULT = "this !!";
         type(WORD_TO_TYPE);
         assertEquals("manual pick then space then punctuation from keyboard twice", EXPECTED_RESULT,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
     }
 
     public void testManualPickThenPunctuationFromStripTwiceThenType() {
@@ -70,7 +70,7 @@
         pickSuggestionManually(0, PUNCTUATION_FROM_STRIP);
         type(WORD2_TO_TYPE);
         assertEquals("pick word then pick punctuation twice then type", EXPECTED_RESULT,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
     }
 
     public void testManualPickThenManualPickWithPunctAtStart() {
@@ -81,7 +81,7 @@
         pickSuggestionManually(0, WORD1_TO_TYPE);
         pickSuggestionManually(1, WORD2_TO_PICK);
         assertEquals("manual pick then manual pick a word with punct at start", EXPECTED_RESULT,
-                mTextView.getText().toString());
+                mEditText.getText().toString());
     }
 
     public void testManuallyPickedWordThenColon() {
@@ -92,7 +92,7 @@
         pickSuggestionManually(0, WORD_TO_TYPE);
         type(PUNCTUATION);
         assertEquals("manually pick word then colon",
-                EXPECTED_RESULT, mTextView.getText().toString());
+                EXPECTED_RESULT, mEditText.getText().toString());
     }
 
     public void testManuallyPickedWordThenOpenParen() {
@@ -103,7 +103,7 @@
         pickSuggestionManually(0, WORD_TO_TYPE);
         type(PUNCTUATION);
         assertEquals("manually pick word then open paren",
-                EXPECTED_RESULT, mTextView.getText().toString());
+                EXPECTED_RESULT, mEditText.getText().toString());
     }
 
     public void testManuallyPickedWordThenCloseParen() {
@@ -114,7 +114,7 @@
         pickSuggestionManually(0, WORD_TO_TYPE);
         type(PUNCTUATION);
         assertEquals("manually pick word then close paren",
-                EXPECTED_RESULT, mTextView.getText().toString());
+                EXPECTED_RESULT, mEditText.getText().toString());
     }
 
     public void testManuallyPickedWordThenSmiley() {
@@ -125,7 +125,7 @@
         pickSuggestionManually(0, WORD_TO_TYPE);
         mLatinIME.onTextInput(SPECIAL_KEY);
         assertEquals("manually pick word then press the smiley key",
-                EXPECTED_RESULT, mTextView.getText().toString());
+                EXPECTED_RESULT, mEditText.getText().toString());
     }
 
     public void testManuallyPickedWordThenDotCom() {
@@ -136,7 +136,7 @@
         pickSuggestionManually(0, WORD_TO_TYPE);
         mLatinIME.onTextInput(SPECIAL_KEY);
         assertEquals("manually pick word then press the .com key",
-                EXPECTED_RESULT, mTextView.getText().toString());
+                EXPECTED_RESULT, mEditText.getText().toString());
     }
 
     public void testTypeWordTypeDotThenPressDotCom() {
@@ -146,7 +146,7 @@
         type(WORD_TO_TYPE);
         mLatinIME.onTextInput(SPECIAL_KEY);
         assertEquals("type word type dot then press the .com key",
-                EXPECTED_RESULT, mTextView.getText().toString());
+                EXPECTED_RESULT, mEditText.getText().toString());
     }
 
     public void testAutoCorrectionWithSingleQuoteInside() {
@@ -154,7 +154,7 @@
         final String EXPECTED_RESULT = "you'd ";
         type(WORD_TO_TYPE);
         assertEquals("auto-correction with single quote inside",
-                EXPECTED_RESULT, mTextView.getText().toString());
+                EXPECTED_RESULT, mEditText.getText().toString());
     }
 
     public void testAutoCorrectionWithSingleQuotesAround() {
@@ -162,6 +162,6 @@
         final String EXPECTED_RESULT = "'this' ";
         type(WORD_TO_TYPE);
         assertEquals("auto-correction with single quotes around",
-                EXPECTED_RESULT, mTextView.getText().toString());
+                EXPECTED_RESULT, mEditText.getText().toString());
     }
 }
diff --git a/tests/src/com/android/inputmethod/latin/ResourceUtilsTests.java b/tests/src/com/android/inputmethod/latin/ResourceUtilsTests.java
new file mode 100644
index 0000000..ed16846
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/ResourceUtilsTests.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.HashMap;
+
+@SmallTest
+public class ResourceUtilsTests extends AndroidTestCase {
+    public void testFindDefaultConstant() {
+        final String[] nullArray = null;
+        assertNull(ResourceUtils.findDefaultConstant(nullArray));
+
+        final String[] emptyArray = {};
+        assertNull(ResourceUtils.findDefaultConstant(emptyArray));
+
+        final String[] array = {
+            "HARDWARE=grouper,0.3",
+            "HARDWARE=mako,0.4",
+            ",defaultValue1",
+            "HARDWARE=manta,0.2",
+            ",defaultValue2",
+        };
+        assertEquals(ResourceUtils.findDefaultConstant(array), "defaultValue1");
+    }
+
+    public void testFindConstantForKeyValuePairsSimple() {
+        final HashMap<String,String> anyKeyValue = CollectionUtils.newHashMap();
+        anyKeyValue.put("anyKey", "anyValue");
+        final HashMap<String,String> nullKeyValue = null;
+        final HashMap<String,String> emptyKeyValue = CollectionUtils.newHashMap();
+
+        final String[] nullArray = null;
+        assertNull(ResourceUtils.findConstantForKeyValuePairs(anyKeyValue, nullArray));
+        assertNull(ResourceUtils.findConstantForKeyValuePairs(emptyKeyValue, nullArray));
+        assertNull(ResourceUtils.findConstantForKeyValuePairs(nullKeyValue, nullArray));
+
+        final String[] emptyArray = {};
+        assertNull(ResourceUtils.findConstantForKeyValuePairs(anyKeyValue, emptyArray));
+        assertNull(ResourceUtils.findConstantForKeyValuePairs(emptyKeyValue, emptyArray));
+        assertNull(ResourceUtils.findConstantForKeyValuePairs(nullKeyValue, emptyArray));
+
+        final String HARDWARE_KEY = "HARDWARE";
+        final String[] array = {
+            ",defaultValue",
+            "HARDWARE=grouper,0.3",
+            "HARDWARE=mako,0.4",
+            "HARDWARE=manta,0.2",
+            "HARDWARE=mako,0.5",
+        };
+
+        final HashMap<String,String> keyValues = CollectionUtils.newHashMap();
+        keyValues.put(HARDWARE_KEY, "grouper");
+        assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.3");
+        keyValues.put(HARDWARE_KEY, "mako");
+        assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.4");
+        keyValues.put(HARDWARE_KEY, "manta");
+        assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.2");
+
+        try {
+            keyValues.clear();
+            keyValues.put("hardware", "grouper");
+            final String constant = ResourceUtils.findConstantForKeyValuePairs(keyValues, array);
+            fail("condition without HARDWARE must fail: constant=" + constant);
+        } catch (final RuntimeException e) {
+            assertEquals(e.getMessage(), "Found unknown key: HARDWARE=grouper");
+        }
+        keyValues.clear();
+        keyValues.put(HARDWARE_KEY, "MAKO");
+        assertNull(ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
+        keyValues.put(HARDWARE_KEY, "mantaray");
+        assertNull(ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
+
+        try {
+            final String constant = ResourceUtils.findConstantForKeyValuePairs(
+                    emptyKeyValue, array);
+            fail("emptyCondition shouldn't match: constant=" + constant);
+        } catch (final RuntimeException e) {
+            assertEquals(e.getMessage(), "Found unknown key: HARDWARE=grouper");
+        }
+    }
+
+    public void testFindConstantForKeyValuePairsCombined() {
+        final String HARDWARE_KEY = "HARDWARE";
+        final String MODEL_KEY = "MODEL";
+        final String MANUFACTURER_KEY = "MANUFACTURER";
+        final String[] array = {
+            ",defaultValue",
+            "HARDWARE=grouper:MANUFACTURER=asus,0.3",
+            "HARDWARE=mako:MODEL=Nexus 4,0.4",
+            "HARDWARE=manta:MODEL=Nexus 10:MANUFACTURER=samsung,0.2"
+        };
+        final String[] failArray = {
+            ",defaultValue",
+            "HARDWARE=grouper:MANUFACTURER=ASUS,0.3",
+            "HARDWARE=mako:MODEL=Nexus_4,0.4",
+            "HARDWARE=mantaray:MODEL=Nexus 10:MANUFACTURER=samsung,0.2"
+        };
+
+        final HashMap<String,String> keyValues = CollectionUtils.newHashMap();
+        keyValues.put(HARDWARE_KEY, "grouper");
+        keyValues.put(MODEL_KEY, "Nexus 7");
+        keyValues.put(MANUFACTURER_KEY, "asus");
+        assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.3");
+        assertNull(ResourceUtils.findConstantForKeyValuePairs(keyValues, failArray));
+
+        keyValues.clear();
+        keyValues.put(HARDWARE_KEY, "mako");
+        keyValues.put(MODEL_KEY, "Nexus 4");
+        keyValues.put(MANUFACTURER_KEY, "LGE");
+        assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.4");
+        assertNull(ResourceUtils.findConstantForKeyValuePairs(keyValues, failArray));
+
+        keyValues.clear();
+        keyValues.put(HARDWARE_KEY, "manta");
+        keyValues.put(MODEL_KEY, "Nexus 10");
+        keyValues.put(MANUFACTURER_KEY, "samsung");
+        assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.2");
+        assertNull(ResourceUtils.findConstantForKeyValuePairs(keyValues, failArray));
+        keyValues.put(HARDWARE_KEY, "mantaray");
+        assertNull(ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
+        assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, failArray), "0.2");
+    }
+
+    public void testFindConstantForKeyValuePairsRegexp() {
+        final String HARDWARE_KEY = "HARDWARE";
+        final String MODEL_KEY = "MODEL";
+        final String MANUFACTURER_KEY = "MANUFACTURER";
+        final String[] array = {
+            ",defaultValue",
+            "HARDWARE=grouper|tilapia:MANUFACTURER=asus,0.3",
+            "HARDWARE=[mM][aA][kK][oO]:MODEL=Nexus 4,0.4",
+            "HARDWARE=manta.*:MODEL=Nexus 10:MANUFACTURER=samsung,0.2"
+        };
+
+        final HashMap<String,String> keyValues = CollectionUtils.newHashMap();
+        keyValues.put(HARDWARE_KEY, "grouper");
+        keyValues.put(MODEL_KEY, "Nexus 7");
+        keyValues.put(MANUFACTURER_KEY, "asus");
+        assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.3");
+        keyValues.put(HARDWARE_KEY, "tilapia");
+        assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.3");
+
+        keyValues.clear();
+        keyValues.put(HARDWARE_KEY, "mako");
+        keyValues.put(MODEL_KEY, "Nexus 4");
+        keyValues.put(MANUFACTURER_KEY, "LGE");
+        assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.4");
+        keyValues.put(HARDWARE_KEY, "MAKO");
+        assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.4");
+
+        keyValues.clear();
+        keyValues.put(HARDWARE_KEY, "manta");
+        keyValues.put(MODEL_KEY, "Nexus 10");
+        keyValues.put(MANUFACTURER_KEY, "samsung");
+        assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.2");
+        keyValues.put(HARDWARE_KEY, "mantaray");
+        assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.2");
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/StringUtilsTests.java b/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
index 1e3cc8a..29e790a 100644
--- a/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
@@ -178,7 +178,7 @@
         assertTrue(StringUtils.isIdenticalAfterDowncase(""));
     }
 
-    private void checkCapitalize(final String src, final String dst, final String separators,
+    private static void checkCapitalize(final String src, final String dst, final String separators,
             final Locale locale) {
         assertEquals(dst, StringUtils.capitalizeEachWord(src, separators, locale));
         assert(src.equals(dst)
diff --git a/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java b/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java
index 879cc46..995d7f0 100644
--- a/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java
+++ b/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java
@@ -24,14 +24,15 @@
 @LargeTest
 public class AndroidSpellCheckerServiceTest extends InputTestsBase {
     public void testSpellchecker() {
-        mTextView.onAttachedToWindow();
-        mTextView.setText("tgis");
-        type(" ");
+        changeLanguage("en_US");
+        mEditText.setText("tgis ");
+        mEditText.setSelection(mEditText.getText().length());
+        mEditText.onAttachedToWindow();
         sleep(1000);
         runMessages();
         sleep(1000);
 
-        final SpanGetter span = new SpanGetter(mTextView.getText(), SuggestionSpan.class);
+        final SpanGetter span = new SpanGetter(mEditText.getText(), SuggestionSpan.class);
         // If no span, the following will crash
         final String[] suggestions = span.getSuggestions();
         // For this test we consider "tgis" should yield at least 2 suggestions (at this moment
@@ -43,14 +44,15 @@
 
     public void testRussianSpellchecker() {
         changeLanguage("ru");
-        mTextView.onAttachedToWindow();
-        mTextView.setText("годп");
-        type(" ");
+        mEditText.onAttachedToWindow();
+        mEditText.setText("годп ");
+        mEditText.setSelection(mEditText.getText().length());
+        mEditText.onAttachedToWindow();
         sleep(1000);
         runMessages();
         sleep(1000);
 
-        final SpanGetter span = new SpanGetter(mTextView.getText(), SuggestionSpan.class);
+        final SpanGetter span = new SpanGetter(mEditText.getText(), SuggestionSpan.class);
         // If no span, the following will crash
         final String[] suggestions = span.getSuggestions();
         // For this test we consider "годп" should yield at least 2 suggestions (at this moment