am c24340f9: am 74d59cca: am dc1097ee: Merge "Update AOSP launcher icon" into lmp-dev

* commit 'c24340f956c16d8594c44d45685f8a814538245b':
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index a1ffe5a..1797dc9 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -33,7 +33,6 @@
 
     <application android:label="@string/english_ime_name"
             android:icon="@drawable/ic_launcher_keyboard"
-            android:killAfterRestore="false"
             android:supportsRtl="true"
             android:allowBackup="true">
 
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_background_lxx_dark.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_background_lxx_dark.9.png
index 306e455..93f300a 100644
--- a/java/res/drawable-hdpi/keyboard_key_feedback_background_lxx_dark.9.png
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_background_lxx_dark.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_background_lxx_light.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_background_lxx_light.9.png
index 867f551..e7c516a 100644
--- a/java/res/drawable-hdpi/keyboard_key_feedback_background_lxx_light.9.png
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_background_lxx_light.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_more_background_lxx_dark.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_more_background_lxx_dark.9.png
index b3e6ee7..b56fcf9 100644
--- a/java/res/drawable-hdpi/keyboard_key_feedback_more_background_lxx_dark.9.png
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_more_background_lxx_dark.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_more_background_lxx_light.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_more_background_lxx_light.9.png
index 827f80f..32005c3 100644
--- a/java/res/drawable-hdpi/keyboard_key_feedback_more_background_lxx_light.9.png
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_more_background_lxx_light.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_background_lxx_dark.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_background_lxx_dark.9.png
index 4f6731f..1a7dd34 100644
--- a/java/res/drawable-mdpi/keyboard_key_feedback_background_lxx_dark.9.png
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_background_lxx_dark.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_background_lxx_light.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_background_lxx_light.9.png
index 14da5f9..268863e 100644
--- a/java/res/drawable-mdpi/keyboard_key_feedback_background_lxx_light.9.png
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_background_lxx_light.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_more_background_lxx_dark.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_more_background_lxx_dark.9.png
index 9bca991..08d67ef 100644
--- a/java/res/drawable-mdpi/keyboard_key_feedback_more_background_lxx_dark.9.png
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_more_background_lxx_dark.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_more_background_lxx_light.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_more_background_lxx_light.9.png
index 14f4b5f..d0e46b1 100644
--- a/java/res/drawable-mdpi/keyboard_key_feedback_more_background_lxx_light.9.png
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_more_background_lxx_light.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_background_lxx_dark.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_background_lxx_dark.9.png
index 654ccd1..f934e33 100644
--- a/java/res/drawable-xhdpi/keyboard_key_feedback_background_lxx_dark.9.png
+++ b/java/res/drawable-xhdpi/keyboard_key_feedback_background_lxx_dark.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_background_lxx_light.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_background_lxx_light.9.png
index c566e3d..f70599e 100644
--- a/java/res/drawable-xhdpi/keyboard_key_feedback_background_lxx_light.9.png
+++ b/java/res/drawable-xhdpi/keyboard_key_feedback_background_lxx_light.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png
index f5f613c..fa5ddf2 100644
--- a/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png
+++ b/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_lxx_light.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_lxx_light.9.png
index 35aaa7d..0da8919 100644
--- a/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_lxx_light.9.png
+++ b/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_lxx_light.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_background_lxx_dark.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_background_lxx_dark.9.png
index f5215bc..1b92455 100644
--- a/java/res/drawable-xxhdpi/keyboard_key_feedback_background_lxx_dark.9.png
+++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_background_lxx_dark.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_background_lxx_light.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_background_lxx_light.9.png
index b565ff0..3effde3 100644
--- a/java/res/drawable-xxhdpi/keyboard_key_feedback_background_lxx_light.9.png
+++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_background_lxx_light.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png
index 6d931ed..55d6330 100644
--- a/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png
+++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_lxx_light.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_lxx_light.9.png
index 2c5ced9..4523be9 100644
--- a/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_lxx_light.9.png
+++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_lxx_light.9.png
Binary files differ
diff --git a/java/res/drawable-xxxhdpi/keyboard_key_feedback_background_lxx_dark.9.png b/java/res/drawable-xxxhdpi/keyboard_key_feedback_background_lxx_dark.9.png
index b68d3e8..0e08b6b 100644
--- a/java/res/drawable-xxxhdpi/keyboard_key_feedback_background_lxx_dark.9.png
+++ b/java/res/drawable-xxxhdpi/keyboard_key_feedback_background_lxx_dark.9.png
Binary files differ
diff --git a/java/res/drawable-xxxhdpi/keyboard_key_feedback_background_lxx_light.9.png b/java/res/drawable-xxxhdpi/keyboard_key_feedback_background_lxx_light.9.png
index 0da475b..fb10523 100644
--- a/java/res/drawable-xxxhdpi/keyboard_key_feedback_background_lxx_light.9.png
+++ b/java/res/drawable-xxxhdpi/keyboard_key_feedback_background_lxx_light.9.png
Binary files differ
diff --git a/java/res/drawable-xxxhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png b/java/res/drawable-xxxhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png
index fa86340..fd88668 100644
--- a/java/res/drawable-xxxhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png
+++ b/java/res/drawable-xxxhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png
Binary files differ
diff --git a/java/res/drawable-xxxhdpi/keyboard_key_feedback_more_background_lxx_light.9.png b/java/res/drawable-xxxhdpi/keyboard_key_feedback_more_background_lxx_light.9.png
index e3ff4d4..ab1bfad 100644
--- a/java/res/drawable-xxxhdpi/keyboard_key_feedback_more_background_lxx_light.9.png
+++ b/java/res/drawable-xxxhdpi/keyboard_key_feedback_more_background_lxx_light.9.png
Binary files differ
diff --git a/java/res/values-land/config.xml b/java/res/values-land/config.xml
index 34d6d4e..d36f6a0 100644
--- a/java/res/values-land/config.xml
+++ b/java/res/values-land/config.xml
@@ -40,16 +40,22 @@
     <!-- config_more_keys_keyboard_key_height x -0.5 -->
     <dimen name="config_more_keys_keyboard_vertical_correction_holo">-22.4dp</dimen>
     <dimen name="config_key_preview_offset_holo">1.6dp</dimen>
-
+    <dimen name="config_key_preview_height_holo">80dp</dimen>
+    <dimen name="config_key_preview_offset_lxx">43.6dp</dimen>
+    <dimen name="config_key_preview_height_lxx">122dp</dimen>
     <fraction name="config_key_preview_text_ratio">90%</fraction>
-    <fraction name="config_key_letter_ratio">65%</fraction>
+    <fraction name="config_key_letter_ratio_holo">65%</fraction>
+    <fraction name="config_key_letter_ratio_lxx">65%</fraction>
     <fraction name="config_key_large_letter_ratio_holo">74%</fraction>
     <fraction name="config_key_large_letter_ratio_lxx">90%</fraction>
-    <fraction name="config_key_label_ratio">40%</fraction>
-    <fraction name="config_key_hint_letter_ratio">30%</fraction>
+    <fraction name="config_key_label_ratio_holo">40%</fraction>
+    <fraction name="config_key_label_ratio_lxx">40%</fraction>
+    <fraction name="config_key_hint_letter_ratio_holo">30%</fraction>
+    <fraction name="config_key_hint_letter_ratio_lxx">30%</fraction>
     <fraction name="config_key_hint_label_ratio_holo">52%</fraction>
     <fraction name="config_key_hint_label_ratio_lxx">30%</fraction>
-    <fraction name="config_key_shifted_letter_hint_ratio">40%</fraction>
+    <fraction name="config_key_shifted_letter_hint_ratio_holo">40%</fraction>
+    <fraction name="config_key_shifted_letter_hint_ratio_lxx">40%</fraction>
     <fraction name="config_language_on_spacebar_text_ratio">40.000%</fraction>
 
     <!-- For 5-row keyboard -->
diff --git a/java/res/values-land/keyboard-heights.xml b/java/res/values-land/keyboard-heights.xml
index d57f96b..02d8b14 100644
--- a/java/res/values-land/keyboard-heights.xml
+++ b/java/res/values-land/keyboard-heights.xml
@@ -33,5 +33,7 @@
     <!-- Preferable keyboard height in absolute scale: 45.0mm -->
         <!-- Xoom -->
         <item>HARDWARE=stingray,265.4378</item>
+        <!-- Volantis -->
+        <item>HARDWARE=flounder,272.0</item>
     </string-array>
 </resources>
diff --git a/java/res/values-sw600dp-land/config.xml b/java/res/values-sw600dp-land/config.xml
index c238b2c..d33af2e 100644
--- a/java/res/values-sw600dp-land/config.xml
+++ b/java/res/values-sw600dp-land/config.xml
@@ -23,7 +23,7 @@
     <!-- Preferable keyboard height in absolute scale: 45.0mm -->
     <!-- This config_default_keyboard_height value should match with keyboard-heights.xml -->
     <dimen name="config_default_keyboard_height">283.5dp</dimen>
-    <fraction name="config_min_keyboard_height">45%p</fraction>
+    <fraction name="config_min_keyboard_height">40%p</fraction>
 
     <dimen name="config_more_keys_keyboard_key_height">81.9dp</dimen>
 
@@ -32,14 +32,18 @@
     <fraction name="config_key_vertical_gap_holo">4.5%p</fraction>
     <fraction name="config_key_horizontal_gap_holo">0.9%p</fraction>
 
-    <fraction name="config_key_letter_ratio">50%</fraction>
+    <fraction name="config_key_letter_ratio_holo">50%</fraction>
+    <fraction name="config_key_letter_ratio_lxx">50%</fraction>
     <fraction name="config_key_large_letter_ratio_holo">48%</fraction>
     <fraction name="config_key_large_letter_ratio_lxx">60%</fraction>
-    <fraction name="config_key_label_ratio">32%</fraction>
-    <fraction name="config_key_hint_letter_ratio">23%</fraction>
+    <fraction name="config_key_label_ratio_holo">32%</fraction>
+    <fraction name="config_key_label_ratio_lxx">32%</fraction>
+    <fraction name="config_key_hint_letter_ratio_holo">23%</fraction>
+    <fraction name="config_key_hint_letter_ratio_lxx">23%</fraction>
     <fraction name="config_key_hint_label_ratio_holo">34%</fraction>
     <fraction name="config_key_hint_label_ratio_lxx">20%</fraction>
-    <fraction name="config_key_shifted_letter_hint_ratio">29%</fraction>
+    <fraction name="config_key_shifted_letter_hint_ratio_holo">29%</fraction>
+    <fraction name="config_key_shifted_letter_hint_ratio_lxx">29%</fraction>
     <fraction name="config_language_on_spacebar_text_ratio">30.0%</fraction>
     <dimen name="config_key_shifted_letter_hint_padding">4dp</dimen>
 
diff --git a/java/res/values-sw600dp/config.xml b/java/res/values-sw600dp/config.xml
index 3c489bb..44e0d06 100644
--- a/java/res/values-sw600dp/config.xml
+++ b/java/res/values-sw600dp/config.xml
@@ -40,18 +40,20 @@
     <fraction name="config_key_horizontal_gap_holo">1.565%p</fraction>
     <!-- config_more_keys_keyboard_key_height x -0.5 -->
     <dimen name="config_more_keys_keyboard_vertical_correction_holo">-31.5dp</dimen>
-    <dimen name="config_key_preview_offset_holo">8.0dp</dimen>
 
-    <dimen name="config_key_preview_height">94.5dp</dimen>
     <fraction name="config_key_preview_text_ratio">50%</fraction>
-    <fraction name="config_key_letter_ratio">42%</fraction>
+    <fraction name="config_key_letter_ratio_holo">42%</fraction>
+    <fraction name="config_key_letter_ratio_lxx">50%</fraction>
     <fraction name="config_key_large_letter_ratio_holo">45%</fraction>
     <fraction name="config_key_large_letter_ratio_lxx">60%</fraction>
-    <fraction name="config_key_label_ratio">25%</fraction>
-    <fraction name="config_key_hint_letter_ratio">23%</fraction>
+    <fraction name="config_key_label_ratio_holo">25%</fraction>
+    <fraction name="config_key_label_ratio_lxx">32%</fraction>
+    <fraction name="config_key_hint_letter_ratio_holo">23%</fraction>
+    <fraction name="config_key_hint_letter_ratio_lxx">23%</fraction>
     <fraction name="config_key_hint_label_ratio_holo">28%</fraction>
     <fraction name="config_key_hint_label_ratio_lxx">20%</fraction>
-    <fraction name="config_key_shifted_letter_hint_ratio">22%</fraction>
+    <fraction name="config_key_shifted_letter_hint_ratio_holo">22%</fraction>
+    <fraction name="config_key_shifted_letter_hint_ratio_lxx">22%</fraction>
     <fraction name="config_language_on_spacebar_text_ratio">28.0%</fraction>
     <dimen name="config_key_hint_letter_padding">3dp</dimen>
     <dimen name="config_key_shifted_letter_hint_padding">3dp</dimen>
diff --git a/java/res/values-sw768dp-land/config.xml b/java/res/values-sw768dp-land/config.xml
index 587a3c1..fdb95c6 100644
--- a/java/res/values-sw768dp-land/config.xml
+++ b/java/res/values-sw768dp-land/config.xml
@@ -23,25 +23,27 @@
     <!-- Preferable keyboard height in absolute scale: 58.0mm -->
     <!-- This config_default_keyboard_height value should match with keyboard-heights.xml -->
     <dimen name="config_default_keyboard_height">365.4dp</dimen>
-    <fraction name="config_min_keyboard_height">45%p</fraction>
+    <fraction name="config_min_keyboard_height">35%p</fraction>
 
     <fraction name="config_keyboard_top_padding_holo">1.896%p</fraction>
     <fraction name="config_keyboard_bottom_padding_holo">0.0%p</fraction>
     <fraction name="config_key_vertical_gap_holo">3.690%p</fraction>
     <fraction name="config_key_horizontal_gap_holo">1.030%p</fraction>
-    <dimen name="config_key_preview_offset_holo">8.0dp</dimen>
 
     <dimen name="config_more_keys_keyboard_key_height">81.9dp</dimen>
 
-    <dimen name="config_key_preview_height">107.1dp</dimen>
-    <fraction name="config_key_letter_ratio">43%</fraction>
+    <fraction name="config_key_letter_ratio_holo">43%</fraction>
+    <fraction name="config_key_letter_ratio_lxx">50%</fraction>
     <fraction name="config_key_large_letter_ratio_holo">42%</fraction>
     <fraction name="config_key_large_letter_ratio_lxx">60%</fraction>
-    <fraction name="config_key_label_ratio">28%</fraction>
-    <fraction name="config_key_hint_letter_ratio">23%</fraction>
+    <fraction name="config_key_label_ratio_holo">28%</fraction>
+    <fraction name="config_key_label_ratio_lxx">32%</fraction>
+    <fraction name="config_key_hint_letter_ratio_holo">23%</fraction>
+    <fraction name="config_key_hint_letter_ratio_lxx">23%</fraction>
     <fraction name="config_key_hint_label_ratio_holo">28%</fraction>
     <fraction name="config_key_hint_label_ratio_lxx">20%</fraction>
-    <fraction name="config_key_shifted_letter_hint_ratio">24%</fraction>
+    <fraction name="config_key_shifted_letter_hint_ratio_holo">24%</fraction>
+    <fraction name="config_key_shifted_letter_hint_ratio_lxx">24%</fraction>
     <fraction name="config_language_on_spacebar_text_ratio">24.00%</fraction>
 
     <!-- For 5-row keyboard -->
diff --git a/java/res/values-sw768dp/config.xml b/java/res/values-sw768dp/config.xml
index f573c40..13be6be 100644
--- a/java/res/values-sw768dp/config.xml
+++ b/java/res/values-sw768dp/config.xml
@@ -32,7 +32,6 @@
     <fraction name="config_key_horizontal_gap_holo">1.066%p</fraction>
     <!-- config_more_keys_keyboard_key_height x -0.5 -->
     <dimen name="config_more_keys_keyboard_vertical_correction_holo">-31.5dp</dimen>
-    <dimen name="config_key_preview_offset_holo">8.0dp</dimen>
 
     <dimen name="config_more_keys_keyboard_key_height">63.0dp</dimen>
     <dimen name="config_more_keys_keyboard_key_horizontal_padding">12dp</dimen>
@@ -40,16 +39,19 @@
     <!-- config_more_keys_keyboard_key_height x 1.2 -->
     <dimen name="config_more_keys_keyboard_slide_allowance">98.3dp</dimen>
 
-    <dimen name="config_key_preview_height">94.5dp</dimen>
     <fraction name="config_key_preview_text_ratio">50%</fraction>
-    <fraction name="config_key_letter_ratio">40%</fraction>
+    <fraction name="config_key_letter_ratio_holo">40%</fraction>
+    <fraction name="config_key_letter_ratio_lxx">50%</fraction>
     <fraction name="config_key_large_letter_ratio_holo">42%</fraction>
     <fraction name="config_key_large_letter_ratio_lxx">60%</fraction>
-    <fraction name="config_key_label_ratio">28%</fraction>
-    <fraction name="config_key_hint_letter_ratio">23%</fraction>
+    <fraction name="config_key_label_ratio_holo">28%</fraction>
+    <fraction name="config_key_label_ratio_lxx">32%</fraction>
+    <fraction name="config_key_hint_letter_ratio_holo">23%</fraction>
+    <fraction name="config_key_hint_letter_ratio_lxx">23%</fraction>
     <fraction name="config_key_hint_label_ratio_holo">28%</fraction>
     <fraction name="config_key_hint_label_ratio_lxx">20%</fraction>
-    <fraction name="config_key_shifted_letter_hint_ratio">26%</fraction>
+    <fraction name="config_key_shifted_letter_hint_ratio_holo">26%</fraction>
+    <fraction name="config_key_shifted_letter_hint_ratio_lxx">26%</fraction>
     <fraction name="config_language_on_spacebar_text_ratio">29.03%</fraction>
     <dimen name="config_key_hint_letter_padding">3dp</dimen>
     <dimen name="config_key_shifted_letter_hint_padding">3dp</dimen>
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index af3b589..40760f6 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -43,17 +43,22 @@
     <!-- config_more_keys_keyboard_key_height x -0.5 -->
     <dimen name="config_more_keys_keyboard_vertical_correction_holo">-26.4dp</dimen>
     <dimen name="config_key_preview_offset_holo">8.0dp</dimen>
-
-    <dimen name="config_key_preview_height">80dp</dimen>
+    <dimen name="config_key_preview_height_holo">80dp</dimen>
+    <dimen name="config_key_preview_offset_lxx">50.0dp</dimen>
+    <dimen name="config_key_preview_height_lxx">122dp</dimen>
     <fraction name="config_key_preview_text_ratio">82%</fraction>
-    <fraction name="config_key_letter_ratio">55%</fraction>
+    <fraction name="config_key_letter_ratio_holo">55%</fraction>
+    <fraction name="config_key_letter_ratio_lxx">55%</fraction>
     <fraction name="config_key_large_letter_ratio_holo">65%</fraction>
     <fraction name="config_key_large_letter_ratio_lxx">90%</fraction>
-    <fraction name="config_key_label_ratio">34%</fraction>
-    <fraction name="config_key_hint_letter_ratio">25%</fraction>
+    <fraction name="config_key_label_ratio_holo">34%</fraction>
+    <fraction name="config_key_label_ratio_lxx">34%</fraction>
+    <fraction name="config_key_hint_letter_ratio_holo">25%</fraction>
+    <fraction name="config_key_hint_letter_ratio_lxx">25%</fraction>
     <fraction name="config_key_hint_label_ratio_holo">44%</fraction>
     <fraction name="config_key_hint_label_ratio_lxx">30%</fraction>
-    <fraction name="config_key_shifted_letter_hint_ratio">35%</fraction>
+    <fraction name="config_key_shifted_letter_hint_ratio_holo">35%</fraction>
+    <fraction name="config_key_shifted_letter_hint_ratio_lxx">35%</fraction>
     <fraction name="config_language_on_spacebar_text_ratio">33.735%</fraction>
     <dimen name="config_key_hint_letter_padding">1dp</dimen>
     <dimen name="config_key_shifted_letter_hint_padding">2dp</dimen>
diff --git a/java/res/values/donottranslate-text-decorator.xml b/java/res/values/donottranslate-text-decorator.xml
index 832610b..2693645 100644
--- a/java/res/values/donottranslate-text-decorator.xml
+++ b/java/res/values/donottranslate-text-decorator.xml
@@ -19,62 +19,11 @@
 -->
 
 <resources>
-    <!-- The delay time in milliseconds from to show the commit indicator -->
-    <integer name="text_decorator_delay_in_milliseconds_to_show_commit_indicator">
-        500
-    </integer>
-
     <!-- The extra margin in dp around the hit area of the commit/add-to-dictionary indicator -->
     <integer name="text_decorator_hit_area_margin_in_dp">
         4
     </integer>
 
-    <!-- If true, the commit/add-to-text indicator will be suppressed when the word isn't going to
-         trigger auto-correction. -->
-    <bool name="text_decorator_only_for_auto_correction">true</bool>
-
-    <!-- If true, the commit/add-to-text indicator will be suppressed when the word is already in
-         the dictionary. -->
-    <bool name="text_decorator_only_for_out_of_vocabulary">false</bool>
-
-    <!-- Background color to be used to highlight the target text when the commit indicator is
-         visible. -->
-    <color name="text_decorator_commit_indicator_text_highlight_color">
-        #B6E2DE
-    </color>
-
-    <!-- Background color of the commit indicator. -->
-    <color name="text_decorator_commit_indicator_background_color">
-        #48B6AC
-    </color>
-
-    <!-- Foreground color of the commit indicator. -->
-    <color name="text_decorator_commit_indicator_foreground_color">
-        #FFFFFF
-    </color>
-
-    <!-- Viewport size of "text_decorator_commit_indicator_path". -->
-    <integer name="text_decorator_commit_indicator_path_size">
-        480
-    </integer>
-
-    <!-- Coordinates of the closed path to be used to render the commit indicator.
-         The format is:  X[0], Y[0], X[1], Y[1], ..., X[N-1], Y[N-1] -->
-    <integer-array name="text_decorator_commit_indicator_path">
-        <item>180</item>
-        <item>323</item>
-        <item>97</item>
-        <item>240</item>
-        <item>68</item>
-        <item>268</item>
-        <item>180</item>
-        <item>380</item>
-        <item>420</item>
-        <item>140</item>
-        <item>392</item>
-        <item>112</item>
-    </integer-array>
-
     <!-- Background color to be used to highlight the target text when the add-to-dictionary
          indicator is visible. -->
     <color name="text_decorator_add_to_dictionary_indicator_text_highlight_color">
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index bccab93..2f8b380 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -36,18 +36,20 @@
     <!-- Option to control whether or not to show a popup with a larger font on each key press. -->
     <string name="popup_on_keypress">Popup on keypress</string>
 
-    <!-- Settings screen title for input preferences [CHAR LIMIT=33]-->
-    <string name="settings_screen_input">Input preferences</string>
-    <!-- Settings screen title for appearance preferences [CHAR LIMIT=33] -->
-    <string name="settings_screen_appearances">Appearance</string>
-    <!-- Settings screen title for multi lingual options [CHAR_LIMIT=33] -->
-    <string name="settings_screen_multi_lingual">Multi lingual options</string>
+    <!-- Settings screen title for preferences [CHAR LIMIT=33]-->
+    <string name="settings_screen_preferences">Preferences</string>
+    <!-- Settings screen title for appearance & layouts preferences [CHAR LIMIT=33] -->
+    <string name="settings_screen_appearance">Appearance &amp; layouts</string>
+    <!-- Settings screen title for multilingual options [CHAR_LIMIT=33] -->
+    <string name="settings_screen_multilingual">Multilingual options</string>
     <!-- Settings screen title for gesture typing preferences [CHAR_LIMIT=33] -->
-    <string name="settings_screen_gesture">Gesture typing preferences</string>
+    <string name="settings_screen_gesture">Gesture Typing</string>
     <!-- Settings screen title for text correction options [CHAR_LIMIT=33] -->
     <string name="settings_screen_correction">Text correction</string>
     <!-- Settings screen title for advanced settings [CHAR LIMIT=33] -->
     <string name="settings_screen_advanced">Advanced</string>
+    <!-- Settings screen title for keyboard theme settings [CHAR LIMIT=33] -->
+    <string name="settings_screen_theme">Theme</string>
 
     <!-- Option name for including other IMEs in the language switch list [CHAR LIMIT=30] -->
     <string name="include_other_imes_in_language_switch_list">Switch to other input methods</string>
@@ -152,14 +154,14 @@
     <string name="configure_input_method">Configure input methods</string>
 
     <!-- Title for input language selection screen -->
-    <string name="language_selection_title">Input languages</string>
+    <string name="language_selection_title">Languages</string>
 
     <!-- Title for the 'Help & feedback' settings fragment which shows a help page and has a button
          for submitting feedback. [CHAR LIMIT=35] -->
     <string name="help_and_feedback">Help &amp; feedback</string>
 
     <!-- Preference for input language selection -->
-    <string name="select_language">Input languages</string>
+    <string name="select_language">Languages</string>
 
     <!-- Add to dictionary hint -->
     <string name="hint_add_to_dictionary">Touch again to save</string>
diff --git a/java/res/values/themes-common.xml b/java/res/values/themes-common.xml
index becaddd..110f6b7 100644
--- a/java/res/values/themes-common.xml
+++ b/java/res/values/themes-common.xml
@@ -37,12 +37,6 @@
         <!-- This keyBackground is needed to run unit tests based on {@link InputTestBase}. -->
         <!-- TODO: Apply default {@link KeyboardTheme} to {@link InputTestBase} and remove this. -->
         <item name="keyBackground">@drawable/btn_keyboard_key_klp</item>
-        <item name="keyLetterSize">@fraction/config_key_letter_ratio</item>
-        <item name="keyLargeLetterRatio">@fraction/config_key_large_letter_ratio_holo</item>
-        <item name="keyLabelSize">@fraction/config_key_label_ratio</item>
-        <item name="keyHintLetterRatio">@fraction/config_key_hint_letter_ratio</item>
-        <item name="keyHintLabelRatio">@fraction/config_key_hint_label_ratio_holo</item>
-        <item name="keyShiftedLetterHintRatio">@fraction/config_key_shifted_letter_hint_ratio</item>
         <item name="keyTypeface">normal</item>
         <!-- A negative value to disable key text shadow layer. -->
         <item name="keyTextShadowRadius">-1.0</item>
@@ -81,7 +75,6 @@
         <item name="keyRepeatInterval">@integer/config_key_repeat_interval</item>
         <item name="longPressShiftLockTimeout">@integer/config_longpress_shift_lock_timeout</item>
         <item name="ignoreAltCodeKeyTimeout">@integer/config_ignore_alt_code_key_timeout</item>
-        <item name="keyPreviewHeight">@dimen/config_key_preview_height</item>
         <!-- TODO: consolidate key preview linger timeout with the key preview animation parameters. -->
         <item name="keyPreviewLingerTimeout">@integer/config_key_preview_linger_timeout</item>
         <item name="moreKeysKeyboardLayout">@layout/more_keys_keyboard</item>
diff --git a/java/res/values/themes-holo.xml b/java/res/values/themes-holo.xml
index cb3ad7a..9f1bd2f 100644
--- a/java/res/values/themes-holo.xml
+++ b/java/res/values/themes-holo.xml
@@ -25,6 +25,10 @@
         parent="KeyboardView"
     >
         <item name="keyTypeface">bold</item>
+        <item name="keyLetterSize">@fraction/config_key_letter_ratio_holo</item>
+        <item name="keyLabelSize">@fraction/config_key_label_ratio_holo</item>
+        <item name="keyHintLetterRatio">@fraction/config_key_hint_letter_ratio_holo</item>
+        <item name="keyShiftedLetterHintRatio">@fraction/config_key_shifted_letter_hint_ratio_holo</item>
         <item name="keyLargeLetterRatio">@fraction/config_key_large_letter_ratio_holo</item>
         <item name="keyLabelOffCenterRatio">-175%</item>
         <item name="keyHintLabelRatio">@fraction/config_key_hint_label_ratio_holo</item>
diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml
index 9e656dd..bfbac0a 100644
--- a/java/res/values/themes-ics.xml
+++ b/java/res/values/themes-ics.xml
@@ -60,6 +60,7 @@
         parent="KeyboardView.ICS"
     >
         <item name="keyPreviewBackground">@drawable/keyboard_key_feedback_ics</item>
+        <item name="keyPreviewHeight">@dimen/config_key_preview_height_holo</item>
         <item name="keyPreviewOffset">@dimen/config_key_preview_offset_holo</item>
         <item name="keyPreviewShowUpAnimator">@anim/key_preview_show_up_holo</item>
         <item name="keyPreviewDismissAnimator">@anim/key_preview_dismiss_holo</item>
diff --git a/java/res/values/themes-klp.xml b/java/res/values/themes-klp.xml
index c6319bc..36b1fc1 100644
--- a/java/res/values/themes-klp.xml
+++ b/java/res/values/themes-klp.xml
@@ -60,6 +60,7 @@
         parent="KeyboardView.KLP"
     >
         <item name="keyPreviewBackground">@drawable/keyboard_key_feedback_klp</item>
+        <item name="keyPreviewHeight">@dimen/config_key_preview_height_holo</item>
         <item name="keyPreviewOffset">@dimen/config_key_preview_offset_holo</item>
         <item name="keyPreviewShowUpAnimator">@anim/key_preview_show_up_holo</item>
         <item name="keyPreviewDismissAnimator">@anim/key_preview_dismiss_holo</item>
diff --git a/java/res/values/themes-lxx-dark.xml b/java/res/values/themes-lxx-dark.xml
index 5b26813..67f94f3 100644
--- a/java/res/values/themes-lxx-dark.xml
+++ b/java/res/values/themes-lxx-dark.xml
@@ -60,7 +60,8 @@
     >
         <item name="moreKeysKeyboardForActionLayout">@layout/more_keys_keyboard_for_action_lxx</item>
         <item name="keyPreviewBackground">@drawable/keyboard_key_feedback_lxx_dark</item>
-        <item name="keyPreviewOffset">@dimen/config_key_preview_offset_holo</item>
+        <item name="keyPreviewHeight">@dimen/config_key_preview_height_lxx</item>
+        <item name="keyPreviewOffset">@dimen/config_key_preview_offset_lxx</item>
         <item name="keyPreviewShowUpAnimator">@anim/key_preview_show_up_lxx</item>
         <item name="keyPreviewDismissAnimator">@anim/key_preview_dismiss_lxx</item>
         <item name="gestureFloatingPreviewTextColor">@color/auto_correct_color_lxx_dark</item>
diff --git a/java/res/values/themes-lxx-light.xml b/java/res/values/themes-lxx-light.xml
index f607807..be817f4 100644
--- a/java/res/values/themes-lxx-light.xml
+++ b/java/res/values/themes-lxx-light.xml
@@ -60,7 +60,8 @@
     >
         <item name="moreKeysKeyboardForActionLayout">@layout/more_keys_keyboard_for_action_lxx</item>
         <item name="keyPreviewBackground">@drawable/keyboard_key_feedback_lxx_light</item>
-        <item name="keyPreviewOffset">@dimen/config_key_preview_offset_holo</item>
+        <item name="keyPreviewHeight">@dimen/config_key_preview_height_lxx</item>
+        <item name="keyPreviewOffset">@dimen/config_key_preview_offset_lxx</item>
         <item name="keyPreviewShowUpAnimator">@anim/key_preview_show_up_lxx</item>
         <item name="keyPreviewDismissAnimator">@anim/key_preview_dismiss_lxx</item>
         <item name="gestureFloatingPreviewTextColor">@color/auto_correct_color_lxx_light</item>
diff --git a/java/res/values/themes-lxx.xml b/java/res/values/themes-lxx.xml
index 1c33cd6..c721888 100644
--- a/java/res/values/themes-lxx.xml
+++ b/java/res/values/themes-lxx.xml
@@ -25,6 +25,10 @@
         parent="KeyboardView"
     >
         <item name="keyTypeface">normal</item>
+        <item name="keyLetterSize">@fraction/config_key_letter_ratio_lxx</item>
+        <item name="keyLabelSize">@fraction/config_key_label_ratio_lxx</item>
+        <item name="keyHintLetterRatio">@fraction/config_key_hint_letter_ratio_lxx</item>
+        <item name="keyShiftedLetterHintRatio">@fraction/config_key_shifted_letter_hint_ratio_lxx</item>
         <item name="keyLargeLetterRatio">@fraction/config_key_large_letter_ratio_lxx</item>
         <item name="keyLabelOffCenterRatio">-80%</item>
         <item name="keyHintLabelRatio">@fraction/config_key_hint_label_ratio_lxx</item>
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index ba285de..c14cd64 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -19,17 +19,17 @@
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
     android:key="english_ime_settings">
     <PreferenceScreen
-        android:fragment="com.android.inputmethod.latin.settings.InputSettingsFragment"
-        android:title="@string/settings_screen_input"
-        android:key="screen_input" />
+        android:fragment="com.android.inputmethod.latin.settings.PreferencesSettingsFragment"
+        android:title="@string/settings_screen_preferences"
+        android:key="screen_preferences" />
     <PreferenceScreen
-        android:fragment="com.android.inputmethod.latin.settings.ThemeSettingsFragment"
-        android:title="@string/keyboard_theme"
-        android:key="screen_theme" />
+        android:fragment="com.android.inputmethod.latin.settings.AppearanceSettingsFragment"
+        android:title="@string/settings_screen_appearance"
+        android:key="screen_appearance" />
     <PreferenceScreen
         android:fragment="com.android.inputmethod.latin.settings.MultiLingualSettingsFragment"
-        android:title="@string/settings_screen_multi_lingual"
-        android:key="screen_multi_lingual" />
+        android:title="@string/settings_screen_multilingual"
+        android:key="screen_multilingual" />
     <PreferenceScreen
         android:fragment="com.android.inputmethod.latin.settings.GestureSettingsFragment"
         android:title="@string/settings_screen_gesture"
diff --git a/java/res/xml/prefs_screen_appearance.xml b/java/res/xml/prefs_screen_appearance.xml
new file mode 100644
index 0000000..7719c05
--- /dev/null
+++ b/java/res/xml/prefs_screen_appearance.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:key="screen_appearance"
+    android:title="@string/settings_screen_appearance">
+    <PreferenceScreen
+        android:fragment="com.android.inputmethod.latin.settings.ThemeSettingsFragment"
+        android:key="screen_theme"
+        android:title="@string/settings_screen_theme" />
+    <PreferenceScreen
+        android:fragment="com.android.inputmethod.latin.settings.CustomInputStyleSettingsFragment"
+        android:key="custom_input_styles"
+        android:title="@string/custom_input_styles_title" />
+</PreferenceScreen>
diff --git a/java/res/xml/prefs_screen_multi_lingual.xml b/java/res/xml/prefs_screen_multilingual.xml
similarity index 81%
rename from java/res/xml/prefs_screen_multi_lingual.xml
rename to java/res/xml/prefs_screen_multilingual.xml
index 937d439..07a4b70 100644
--- a/java/res/xml/prefs_screen_multi_lingual.xml
+++ b/java/res/xml/prefs_screen_multilingual.xml
@@ -16,8 +16,8 @@
 
 <PreferenceScreen
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:title="@string/settings_screen_multi_lingual"
-    android:key="screen_multi_lingual">
+    android:title="@string/settings_screen_multilingual"
+    android:key="screen_multilingual">
     <CheckBoxPreference
         android:key="pref_show_language_switch_key"
         android:title="@string/show_language_switch_key"
@@ -31,8 +31,4 @@
         android:summary="@string/include_other_imes_in_language_switch_list_summary"
         android:defaultValue="false"
         android:persistent="true" />
-    <PreferenceScreen
-        android:fragment="com.android.inputmethod.latin.settings.CustomInputStyleSettingsFragment"
-        android:key="custom_input_styles"
-        android:title="@string/custom_input_styles_title" />
 </PreferenceScreen>
diff --git a/java/res/xml/prefs_screen_input.xml b/java/res/xml/prefs_screen_preferences.xml
similarity index 97%
rename from java/res/xml/prefs_screen_input.xml
rename to java/res/xml/prefs_screen_preferences.xml
index 7704e3f..101edc8 100644
--- a/java/res/xml/prefs_screen_input.xml
+++ b/java/res/xml/prefs_screen_preferences.xml
@@ -17,7 +17,7 @@
 <PreferenceScreen
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    android:title="@string/settings_screen_input">
+    android:title="@string/settings_screen_preferences">
     <CheckBoxPreference
         android:key="auto_cap"
         android:title="@string/auto_cap"
diff --git a/java/res/xml/prefs_screen_theme.xml b/java/res/xml/prefs_screen_theme.xml
index b49f0be..677a6ea 100644
--- a/java/res/xml/prefs_screen_theme.xml
+++ b/java/res/xml/prefs_screen_theme.xml
@@ -17,7 +17,7 @@
 <PreferenceScreen
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    android:title="@string/keyboard_theme"
+    android:title="@string/settings_screen_theme"
     android:key="screen_theme">
     <!-- Keyboard theme list will be populated programmatically here. -->
 </PreferenceScreen>
diff --git a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java
index 8a28185..5af3179 100644
--- a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java
@@ -41,6 +41,8 @@
 
     // Note that CursorAnchorInfo has been introduced in API level XX (Build.VERSION_CODE.LXX).
     private static final CompatUtils.ClassWrapper sCursorAnchorInfoClass;
+    private static final CompatUtils.ToIntMethodWrapper sGetSelectionStartMethod;
+    private static final CompatUtils.ToIntMethodWrapper sGetSelectionEndMethod;
     private static final CompatUtils.ToObjectMethodWrapper<RectF> sGetCharacterBoundsMethod;
     private static final CompatUtils.ToIntMethodWrapper sGetCharacterBoundsFlagsMethod;
     private static final CompatUtils.ToObjectMethodWrapper<CharSequence> sGetComposingTextMethod;
@@ -52,10 +54,14 @@
     private static final CompatUtils.ToObjectMethodWrapper<Matrix> sGetMatrixMethod;
     private static final CompatUtils.ToIntMethodWrapper sGetInsertionMarkerFlagsMethod;
 
-    private static int COMPOSING_TEXT_START_DEFAULT = -1;
+    private static int INVALID_TEXT_INDEX = -1;
     static {
         sCursorAnchorInfoClass = CompatUtils.getClassWrapper(
                 "android.view.inputmethod.CursorAnchorInfo");
+        sGetSelectionStartMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
+                "getSelectionStart", INVALID_TEXT_INDEX);
+        sGetSelectionEndMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
+                "getSelectionEnd", INVALID_TEXT_INDEX);
         sGetCharacterBoundsMethod = sCursorAnchorInfoClass.getMethod(
                 "getCharacterBounds", (RectF)null, int.class);
         sGetCharacterBoundsFlagsMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
@@ -63,7 +69,7 @@
         sGetComposingTextMethod = sCursorAnchorInfoClass.getMethod(
                 "getComposingText", (CharSequence)null);
         sGetComposingTextStartMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
-                "getComposingTextStart", COMPOSING_TEXT_START_DEFAULT);
+                "getComposingTextStart", INVALID_TEXT_INDEX);
         sGetInsertionMarkerBaselineMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
                 "getInsertionMarkerBaseline", 0.0f);
         sGetInsertionMarkerBottomMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
@@ -78,8 +84,8 @@
     }
 
     @UsedForTesting
-    public static boolean isAvailable() {
-        return sCursorAnchorInfoClass.exists();
+    public boolean isAvailable() {
+        return sCursorAnchorInfoClass.exists() && mInstance != null;
     }
 
     private Object mInstance;
@@ -90,7 +96,7 @@
 
     @UsedForTesting
     public static CursorAnchorInfoCompatWrapper fromObject(final Object instance) {
-        if (!isAvailable()) {
+        if (!sCursorAnchorInfoClass.exists()) {
             return new CursorAnchorInfoCompatWrapper(null);
         }
         return new CursorAnchorInfoCompatWrapper(instance);
@@ -105,6 +111,14 @@
         return FakeHolder.sInstance;
     }
 
+    public int getSelectionStart() {
+        return sGetSelectionStartMethod.invoke(mInstance);
+    }
+
+    public int getSelectionEnd() {
+        return sGetSelectionEndMethod.invoke(mInstance);
+    }
+
     public CharSequence getComposingText() {
         return sGetComposingTextMethod.invoke(mInstance);
     }
diff --git a/java/src/com/android/inputmethod/dictionarypack/DownloadManagerWrapper.java b/java/src/com/android/inputmethod/dictionarypack/DownloadManagerWrapper.java
index 75cc7d4..3dbbc9b 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DownloadManagerWrapper.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DownloadManagerWrapper.java
@@ -54,15 +54,13 @@
             if (null != mDownloadManager) {
                 mDownloadManager.remove(ids);
             }
+        } catch (IllegalArgumentException e) {
+            // This is expected to happen on boot when the device is encrypted.
         } catch (SQLiteException e) {
             // We couldn't remove the file from DownloadManager. Apparently, the database can't
             // be opened. It may be a problem with file system corruption. In any case, there is
             // not much we can do apart from avoiding crashing.
             Log.e(TAG, "Can't remove files with ID " + ids + " from download manager", e);
-        } catch (IllegalArgumentException e) {
-            // Not sure how this can happen, but it could be another case where the provider
-            // is disabled. Or it could be a bug in older versions of the framework.
-            Log.e(TAG, "Can't find the content URL for DownloadManager?", e);
         }
     }
 
@@ -71,10 +69,10 @@
             if (null != mDownloadManager) {
                 return mDownloadManager.openDownloadedFile(fileId);
             }
+        } catch (IllegalArgumentException e) {
+            // This is expected to happen on boot when the device is encrypted.
         } catch (SQLiteException e) {
             Log.e(TAG, "Can't open downloaded file with ID " + fileId, e);
-        } catch (IllegalArgumentException e) {
-            Log.e(TAG, "Can't find the content URL for DownloadManager?", e);
         }
         // We come here if mDownloadManager is null or if an exception was thrown.
         throw new FileNotFoundException();
@@ -85,10 +83,10 @@
             if (null != mDownloadManager) {
                 return mDownloadManager.query(query);
             }
+        } catch (IllegalArgumentException e) {
+            // This is expected to happen on boot when the device is encrypted.
         } catch (SQLiteException e) {
             Log.e(TAG, "Can't query the download manager", e);
-        } catch (IllegalArgumentException e) {
-            Log.e(TAG, "Can't find the content URL for DownloadManager?", e);
         }
         // We come here if mDownloadManager is null or if an exception was thrown.
         return null;
@@ -99,10 +97,10 @@
             if (null != mDownloadManager) {
                 return mDownloadManager.enqueue(request);
             }
+        } catch (IllegalArgumentException e) {
+            // This is expected to happen on boot when the device is encrypted.
         } catch (SQLiteException e) {
             Log.e(TAG, "Can't enqueue a request with the download manager", e);
-        } catch (IllegalArgumentException e) {
-            Log.e(TAG, "Can't find the content URL for DownloadManager?", e);
         }
         return 0;
     }
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 86ea4c5..81ea90a 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -702,6 +702,10 @@
         return ((mLabelFlags | defaultFlags) & LABEL_FLAGS_KEEP_BACKGROUND_ASPECT_RATIO) != 0;
     }
 
+    public final boolean hasCustomActionLabel() {
+        return (mLabelFlags & LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL) != 0;
+    }
+
     private final boolean isShiftedLetterActivated() {
         return (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) != 0
                 && !TextUtils.isEmpty(mHintLabel);
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index e487516..60665f8 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.preference.PreferenceManager;
 import android.util.Log;
@@ -256,13 +255,12 @@
     }
 
     public void onToggleEmojiKeyboard() {
-        if (mKeyboardLayoutSet == null) {
-            return;
-        }
-        if (isShowingEmojiPalettes()) {
-            setAlphabetKeyboard();
-        } else {
+        if (mKeyboardLayoutSet == null || !isShowingEmojiPalettes()) {
+            mLatinIME.startShowingInputView();
             setEmojiKeyboard();
+        } else {
+            mLatinIME.stopShowingInputView();
+            setAlphabetKeyboard();
         }
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index bb3cbb0..98cd1da 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -343,7 +343,9 @@
         final int keyWidth = key.getDrawWidth();
         final int keyHeight = key.getHeight();
         final int bgWidth, bgHeight, bgX, bgY;
-        if (key.needsToKeepBackgroundAspectRatio(mDefaultKeyLabelFlags)) {
+        if (key.needsToKeepBackgroundAspectRatio(mDefaultKeyLabelFlags)
+                // HACK: To disable expanding normal/functional key background.
+                && !key.hasCustomActionLabel()) {
             final int intrinsicWidth = background.getIntrinsicWidth();
             final int intrinsicHeight = background.getIntrinsicHeight();
             final float minScale = Math.min(
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 6d0b2c2..2b16785 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -756,7 +756,8 @@
     public void onHideWindow() {
         onDismissMoreKeysPanel();
         final MainKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
-        if (accessibilityDelegate != null) {
+        if (accessibilityDelegate != null
+                && AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
             accessibilityDelegate.onHideWindow();
         }
     }
@@ -767,7 +768,8 @@
     @Override
     public boolean onHoverEvent(final MotionEvent event) {
         final MainKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
-        if (accessibilityDelegate != null) {
+        if (accessibilityDelegate != null
+                && AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
             return accessibilityDelegate.onHoverEvent(event);
         }
         return super.onHoverEvent(event);
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
index a9d1239..841283b 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
@@ -105,7 +105,7 @@
         super.setKeyboard(keyboard);
         mKeyDetector.setKeyboard(
                 keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection());
-        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
+        if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
             if (mAccessibilityDelegate == null) {
                 mAccessibilityDelegate = new MoreKeysKeyboardAccessibilityDelegate(
                         this, mKeyDetector);
@@ -142,7 +142,8 @@
         mOriginY = y + container.getPaddingTop();
         controller.onShowMoreKeysPanel(this);
         final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
-        if (accessibilityDelegate != null) {
+        if (accessibilityDelegate != null
+                && AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
             accessibilityDelegate.onShowMoreKeysKeyboard();
         }
     }
@@ -239,7 +240,8 @@
             return;
         }
         final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
-        if (accessibilityDelegate != null) {
+        if (accessibilityDelegate != null
+                && AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
             accessibilityDelegate.onDismissMoreKeysKeyboard();
         }
         mController.onDismissMoreKeysPanel();
@@ -285,7 +287,8 @@
     @Override
     public boolean onHoverEvent(final MotionEvent event) {
         final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
-        if (accessibilityDelegate != null) {
+        if (accessibilityDelegate != null
+                && AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
             return accessibilityDelegate.onHoverEvent(event);
         }
         return super.onHoverEvent(event);
diff --git a/java/src/com/android/inputmethod/keyboard/TextDecorator.java b/java/src/com/android/inputmethod/keyboard/TextDecorator.java
index f614b22..c22717f 100644
--- a/java/src/com/android/inputmethod/keyboard/TextDecorator.java
+++ b/java/src/com/android/inputmethod/keyboard/TextDecorator.java
@@ -17,23 +17,22 @@
 package com.android.inputmethod.keyboard;
 
 import android.graphics.Matrix;
-import android.graphics.PointF;
 import android.graphics.RectF;
 import android.inputmethodservice.InputMethodService;
 import android.os.Message;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
+import android.view.inputmethod.CursorAnchorInfo;
 
 import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
 
 import javax.annotation.Nonnull;
 
 /**
- * A controller class of commit/add-to-dictionary indicator (a.k.a. TextDecorator). This class
+ * A controller class of the add-to-dictionary indicator (a.k.a. TextDecorator). This class
  * is designed to be independent of UI subsystems such as {@link View}. All the UI related
  * operations are delegated to {@link TextDecoratorUi} via {@link TextDecoratorUiOperator}.
  */
@@ -41,18 +40,22 @@
     private static final String TAG = TextDecorator.class.getSimpleName();
     private static final boolean DEBUG = false;
 
-    private static final int MODE_NONE = 0;
-    private static final int MODE_COMMIT = 1;
-    private static final int MODE_ADD_TO_DICTIONARY = 2;
+    private static final int INVALID_CURSOR_INDEX = -1;
 
-    private int mMode = MODE_NONE;
+    private static final int MODE_MONITOR = 0;
+    private static final int MODE_WAITING_CURSOR_INDEX = 1;
+    private static final int MODE_SHOWING_INDICATOR = 2;
 
-    private final PointF mLocalOrigin = new PointF();
-    private final RectF mRelativeIndicatorBounds = new RectF();
-    private final RectF mRelativeComposingTextBounds = new RectF();
+    private int mMode = MODE_MONITOR;
+
+    private String mLastComposingText = null;
+    private boolean mHasRtlCharsInLastComposingText = false;
+    private RectF mComposingTextBoundsForLastComposingText = new RectF();
 
     private boolean mIsFullScreenMode = false;
-    private SuggestedWordInfo mWaitingWord = null;
+    private String mWaitingWord = null;
+    private int mWaitingCursorStart = INVALID_CURSOR_INDEX;
+    private int mWaitingCursorEnd = INVALID_CURSOR_INDEX;
     private CursorAnchorInfoCompatWrapper mCursorAnchorInfoWrapper = null;
 
     @Nonnull
@@ -63,16 +66,10 @@
 
     public interface Listener {
         /**
-         * Called when the user clicks the composing text to commit.
-         * @param wordInfo the suggested word which the user clicked on.
+         * Called when the user clicks the indicator to add the word into the dictionary.
+         * @param word the word which the user clicked on.
          */
-        void onClickComposingTextToCommit(final SuggestedWordInfo wordInfo);
-
-        /**
-         * Called when the user clicks the composing text to add the word into the dictionary.
-         * @param wordInfo the suggested word which the user clicked on.
-         */
-        void onClickComposingTextToAddToDictionary(final SuggestedWordInfo wordInfo);
+        void onClickComposingTextToAddToDictionary(final String word);
     }
 
     public TextDecorator(final Listener listener) {
@@ -103,46 +100,19 @@
     }
 
     /**
-     * Shows the "Commit" indicator and associates it with the given suggested word.
+     * Shows the "Add to dictionary" indicator and associates it with associating the given word.
      *
-     * <p>The effect of {@link #showCommitIndicator(SuggestedWordInfo)} and
-     * {@link #showAddToDictionaryIndicator(SuggestedWordInfo)} are exclusive to each other. Call
-     * {@link #reset()} to hide the indicator.</p>
-     *
-     * @param wordInfo the suggested word which should be associated with the indicator. This object
-     * will be passed back in {@link Listener#onClickComposingTextToCommit(SuggestedWordInfo)}
+     * @param word the word which should be associated with the indicator. This object will be
+     * passed back in {@link Listener#onClickComposingTextToAddToDictionary(String)}.
+     * @param selectionStart the cursor index (inclusive) when the indicator should be displayed.
+     * @param selectionEnd the cursor index (exclusive) when the indicator should be displayed.
      */
-    public void showCommitIndicator(final SuggestedWordInfo wordInfo) {
-        if (mMode == MODE_COMMIT && wordInfo != null &&
-                TextUtils.equals(mWaitingWord.mWord, wordInfo.mWord)) {
-            // Skip layout for better performance.
-            return;
-        }
-        mWaitingWord = wordInfo;
-        mMode = MODE_COMMIT;
-        layoutLater();
-    }
-
-    /**
-     * Shows the "Add to dictionary" indicator and associates it with associating the given
-     * suggested word.
-     *
-     * <p>The effect of {@link #showCommitIndicator(SuggestedWordInfo)} and
-     * {@link #showAddToDictionaryIndicator(SuggestedWordInfo)} are exclusive to each other. Call
-     * {@link #reset()} to hide the indicator.</p>
-     *
-     * @param wordInfo the suggested word which should be associated with the indicator. This object
-     * will be passed back in
-     * {@link Listener#onClickComposingTextToAddToDictionary(SuggestedWordInfo)}.
-     */
-    public void showAddToDictionaryIndicator(final SuggestedWordInfo wordInfo) {
-        if (mMode == MODE_ADD_TO_DICTIONARY && wordInfo != null &&
-                TextUtils.equals(mWaitingWord.mWord, wordInfo.mWord)) {
-            // Skip layout for better performance.
-            return;
-        }
-        mWaitingWord = wordInfo;
-        mMode = MODE_ADD_TO_DICTIONARY;
+    public void showAddToDictionaryIndicator(final String word, final int selectionStart,
+            final int selectionEnd) {
+        mWaitingWord = word;
+        mWaitingCursorStart = selectionStart;
+        mWaitingCursorEnd = selectionEnd;
+        mMode = MODE_WAITING_CURSOR_INDEX;
         layoutLater();
         return;
     }
@@ -165,18 +135,19 @@
      */
     public void reset() {
         mWaitingWord = null;
-        mMode = MODE_NONE;
-        mLocalOrigin.set(0.0f, 0.0f);
-        mRelativeIndicatorBounds.set(0.0f, 0.0f, 0.0f, 0.0f);
-        mRelativeComposingTextBounds.set(0.0f, 0.0f, 0.0f, 0.0f);
+        mMode = MODE_MONITOR;
+        mWaitingCursorStart = INVALID_CURSOR_INDEX;
+        mWaitingCursorEnd = INVALID_CURSOR_INDEX;
         cancelLayoutInternalExpectedly("Resetting internal state.");
     }
 
     /**
-     * Must be called when the {@link InputMethodService#onUpdateCursorAnchorInfo()} is called.
+     * Must be called when the {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)}
+     * is called.
      *
      * <p>CAVEAT: Currently the input method author is responsible for ignoring
-     * {@link InputMethodService#onUpdateCursorAnchorInfo()} called in full screen mode.</p>
+     * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} called in full screen
+     * mode.</p>
      * @param info the compatibility wrapper object for the received {@link CursorAnchorInfo}.
      */
     public void onUpdateCursorAnchorInfo(final CursorAnchorInfoCompatWrapper info) {
@@ -185,29 +156,6 @@
         layoutImmediately();
     }
 
-    /**
-     * Hides indicator if the new composing text doesn't match the expected one.
-     *
-     * <p>Calling this method is optional but recommended whenever the new composition is passed to
-     * the application. The motivation of this method is to reduce the UI latency. With this method,
-     * we can hide the indicator without waiting the arrival of the
-     * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} callback, assuming that
-     * the application accepts the new composing text without any modification. Even if this
-     * assumption is false, the indicator will be shown again when
-     * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} is actually received.
-     * </p>
-     *
-     * @param newComposingText the new composing text that is being passed to the application.
-     */
-    public void hideIndicatorIfNecessary(final CharSequence newComposingText) {
-        if (mMode != MODE_COMMIT && mMode != MODE_ADD_TO_DICTIONARY) {
-            return;
-        }
-        if (!TextUtils.equals(newComposingText, mWaitingWord.mWord)) {
-            mUiOperator.hideUi();
-        }
-    }
-
     private void cancelLayoutInternalUnexpectedly(final String message) {
         mUiOperator.hideUi();
         Log.d(TAG, message);
@@ -232,18 +180,9 @@
     }
 
     private void layoutMain() {
-        if (mMode != MODE_COMMIT && mMode != MODE_ADD_TO_DICTIONARY) {
-            if (mMode == MODE_NONE) {
-                cancelLayoutInternalExpectedly("Not ready for layouting.");
-            } else {
-                cancelLayoutInternalUnexpectedly("Unknown mMode=" + mMode);
-            }
-            return;
-        }
-
         final CursorAnchorInfoCompatWrapper info = mCursorAnchorInfoWrapper;
 
-        if (info == null) {
+        if (info == null || !info.isAvailable()) {
             cancelLayoutInternalExpectedly("CursorAnchorInfo isn't available.");
             return;
         }
@@ -254,104 +193,105 @@
         }
 
         final CharSequence composingText = info.getComposingText();
-        if (mMode == MODE_COMMIT) {
-            if (composingText == null) {
-                cancelLayoutInternalExpectedly("composingText is null.");
-                return;
-            }
+        if (!TextUtils.isEmpty(composingText)) {
             final int composingTextStart = info.getComposingTextStart();
             final int lastCharRectIndex = composingTextStart + composingText.length() - 1;
             final RectF lastCharRect = info.getCharacterBounds(lastCharRectIndex);
-            final int lastCharRectFlag = info.getCharacterBoundsFlags(lastCharRectIndex);
+            final int lastCharRectFlags = info.getCharacterBoundsFlags(lastCharRectIndex);
             final boolean hasInvisibleRegionInLastCharRect =
-                    (lastCharRectFlag & CursorAnchorInfoCompatWrapper.FLAG_HAS_INVISIBLE_REGION)
+                    (lastCharRectFlags & CursorAnchorInfoCompatWrapper.FLAG_HAS_INVISIBLE_REGION)
                             != 0;
             if (lastCharRect == null || matrix == null || hasInvisibleRegionInLastCharRect) {
                 mUiOperator.hideUi();
                 return;
             }
-            final RectF segmentStartCharRect = new RectF(lastCharRect);
-            for (int i = composingText.length() - 2; i >= 0; --i) {
-                final RectF charRect = info.getCharacterBounds(composingTextStart + i);
-                if (charRect == null) {
+
+            // Note that the following layout information is fragile, and must be invalidated
+            // even when surrounding text next to the composing text is changed because it can
+            // affect how the composing text is rendered.
+            // TODO: Investigate if we can change the input logic to make the target text
+            // composing state so that we can retrieve the character bounds reliably.
+            final String composingTextString = composingText.toString();
+            final float top = lastCharRect.top;
+            final float bottom = lastCharRect.bottom;
+            float left = lastCharRect.left;
+            float right = lastCharRect.right;
+            boolean useRtlLayout = false;
+            for (int i = composingText.length() - 1; i >= 0; --i) {
+                final int characterIndex = composingTextStart + i;
+                final RectF characterBounds = info.getCharacterBounds(characterIndex);
+                final int characterBoundsFlags = info.getCharacterBoundsFlags(characterIndex);
+                if (characterBounds == null) {
                     break;
                 }
-                if (charRect.top != segmentStartCharRect.top) {
+                if (characterBounds.top != top) {
                     break;
                 }
-                if (charRect.bottom != segmentStartCharRect.bottom) {
+                if (characterBounds.bottom != bottom) {
                     break;
                 }
-                segmentStartCharRect.set(charRect);
+                if ((characterBoundsFlags & CursorAnchorInfoCompatWrapper.FLAG_IS_RTL) != 0) {
+                    // This is for both RTL text and bi-directional text. RTL languages usually mix
+                    // RTL characters with LTR characters and in this case we should display the
+                    // indicator on the left, while in LTR languages that normally never happens.
+                    // TODO: Try to come up with a better algorithm.
+                    useRtlLayout = true;
+                }
+                left = Math.min(characterBounds.left, left);
+                right = Math.max(characterBounds.right, right);
             }
-
-            mLocalOrigin.set(lastCharRect.right, lastCharRect.top);
-            mRelativeIndicatorBounds.set(lastCharRect.right, lastCharRect.top,
-                    lastCharRect.right + lastCharRect.height(), lastCharRect.bottom);
-            mRelativeIndicatorBounds.offset(-mLocalOrigin.x, -mLocalOrigin.y);
-
-            mRelativeIndicatorBounds.set(lastCharRect.right, lastCharRect.top,
-                    lastCharRect.right + lastCharRect.height(), lastCharRect.bottom);
-            mRelativeIndicatorBounds.offset(-mLocalOrigin.x, -mLocalOrigin.y);
-
-            mRelativeComposingTextBounds.set(segmentStartCharRect.left, segmentStartCharRect.top,
-                    segmentStartCharRect.right, segmentStartCharRect.bottom);
-            mRelativeComposingTextBounds.offset(-mLocalOrigin.x, -mLocalOrigin.y);
-
-            if (mWaitingWord == null) {
-                cancelLayoutInternalExpectedly("mWaitingText is null.");
-                return;
-            }
-            if (TextUtils.isEmpty(mWaitingWord.mWord)) {
-                cancelLayoutInternalExpectedly("mWaitingText.mWord is empty.");
-                return;
-            }
-            if (!TextUtils.equals(composingText, mWaitingWord.mWord)) {
-                // This is indeed an expected situation because of the asynchronous nature of
-                // input method framework in Android. Note that composingText is notified from the
-                // application, while mWaitingWord.mWord is obtained directly from the InputLogic.
-                cancelLayoutInternalExpectedly(
-                        "Composing text doesn't match the one we are waiting for.");
-                return;
-            }
-        } else {
-            if (!mIsFullScreenMode && !TextUtils.isEmpty(composingText)) {
-                // This is an unexpected case.
-                // TODO: Document this.
-                mUiOperator.hideUi();
-                return;
-            }
-            // In MODE_ADD_TO_DICTIONARY, we cannot retrieve the character position at all because
-            // of the lack of composing text. We will use the insertion marker position instead.
-            if ((info.getInsertionMarkerFlags() &
-                    CursorAnchorInfoCompatWrapper.FLAG_HAS_INVISIBLE_REGION) != 0) {
-                mUiOperator.hideUi();
-                return;
-            }
-            final float insertionMarkerHolizontal = info.getInsertionMarkerHorizontal();
-            final float insertionMarkerTop = info.getInsertionMarkerTop();
-            mLocalOrigin.set(insertionMarkerHolizontal, insertionMarkerTop);
+            mLastComposingText = composingTextString;
+            mHasRtlCharsInLastComposingText = useRtlLayout;
+            mComposingTextBoundsForLastComposingText.set(left, top, right, bottom);
         }
 
-        final RectF indicatorBounds = new RectF(mRelativeIndicatorBounds);
-        final RectF composingTextBounds = new RectF(mRelativeComposingTextBounds);
-        indicatorBounds.offset(mLocalOrigin.x, mLocalOrigin.y);
-        composingTextBounds.offset(mLocalOrigin.x, mLocalOrigin.y);
-        mUiOperator.layoutUi(mMode == MODE_COMMIT, matrix, indicatorBounds, composingTextBounds);
+        final int selectionStart = info.getSelectionStart();
+        final int selectionEnd = info.getSelectionEnd();
+        switch (mMode) {
+            case MODE_MONITOR:
+                mUiOperator.hideUi();
+                return;
+            case MODE_WAITING_CURSOR_INDEX:
+                if (selectionStart != mWaitingCursorStart || selectionEnd != mWaitingCursorEnd) {
+                    mUiOperator.hideUi();
+                    return;
+                }
+                mMode = MODE_SHOWING_INDICATOR;
+                break;
+            case MODE_SHOWING_INDICATOR:
+                if (selectionStart != mWaitingCursorStart || selectionEnd != mWaitingCursorEnd) {
+                    mUiOperator.hideUi();
+                    mMode = MODE_MONITOR;
+                    mWaitingCursorStart = INVALID_CURSOR_INDEX;
+                    mWaitingCursorEnd = INVALID_CURSOR_INDEX;
+                    return;
+                }
+                break;
+            default:
+                cancelLayoutInternalUnexpectedly("Unexpected internal mode=" + mMode);
+                return;
+        }
+
+        if (!TextUtils.equals(mLastComposingText, mWaitingWord)) {
+            cancelLayoutInternalUnexpectedly("mLastComposingText doesn't match mWaitingWord");
+            return;
+        }
+
+        if ((info.getInsertionMarkerFlags() &
+                CursorAnchorInfoCompatWrapper.FLAG_HAS_INVISIBLE_REGION) != 0) {
+            mUiOperator.hideUi();
+            return;
+        }
+
+        mUiOperator.layoutUi(matrix, mComposingTextBoundsForLastComposingText,
+                mHasRtlCharsInLastComposingText);
     }
 
     private void onClickIndicator() {
-        if (mWaitingWord == null || TextUtils.isEmpty(mWaitingWord.mWord)) {
+        if (mMode != MODE_SHOWING_INDICATOR) {
             return;
         }
-        switch (mMode) {
-            case MODE_COMMIT:
-                mListener.onClickComposingTextToCommit(mWaitingWord);
-                break;
-            case MODE_ADD_TO_DICTIONARY:
-                mListener.onClickComposingTextToAddToDictionary(mWaitingWord);
-                break;
-        }
+        mListener.onClickComposingTextToAddToDictionary(mWaitingWord);
     }
 
     private final LayoutInvalidator mLayoutInvalidator = new LayoutInvalidator(this);
@@ -407,10 +347,7 @@
 
     private final static Listener EMPTY_LISTENER = new Listener() {
         @Override
-        public void onClickComposingTextToCommit(SuggestedWordInfo wordInfo) {
-        }
-        @Override
-        public void onClickComposingTextToAddToDictionary(SuggestedWordInfo wordInfo) {
+        public void onClickComposingTextToAddToDictionary(final String word) {
         }
     };
 
@@ -425,8 +362,7 @@
         public void setOnClickListener(Runnable listener) {
         }
         @Override
-        public void layoutUi(boolean isCommitMode, Matrix matrix, RectF indicatorBounds,
-                RectF composingTextBounds) {
+        public void layoutUi(Matrix matrix, RectF composingTextBounds, boolean useRtlLayout) {
         }
     };
 }
diff --git a/java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java b/java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java
index 6e215a9..d87dc1b 100644
--- a/java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java
+++ b/java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java
@@ -26,6 +26,7 @@
 import android.graphics.RectF;
 import android.graphics.drawable.ColorDrawable;
 import android.inputmethodservice.InputMethodService;
+import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.View;
@@ -46,11 +47,11 @@
     private static final int VISUAL_DEBUG_HIT_AREA_COLOR = 0x80ff8000;
 
     private final RelativeLayout mLocalRootView;
-    private final CommitIndicatorView mCommitIndicatorView;
     private final AddToDictionaryIndicatorView mAddToDictionaryIndicatorView;
     private final PopupWindow mTouchEventWindow;
     private final View mTouchEventWindowClickListenerView;
     private final float mHitAreaMarginInPixels;
+    private final RectF mDisplayRect;
 
     /**
      * This constructor is designed to be called from {@link InputMethodService#setInputView(View)}.
@@ -65,6 +66,9 @@
                 R.integer.text_decorator_hit_area_margin_in_dp);
         mHitAreaMarginInPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                 hitAreaMarginInDP, resources.getDisplayMetrics());
+        final DisplayMetrics displayMetrics = resources.getDisplayMetrics();
+        mDisplayRect = new RectF(0.0f, 0.0f, displayMetrics.widthPixels,
+                displayMetrics.heightPixels);
 
         mLocalRootView = new RelativeLayout(context);
         mLocalRootView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
@@ -73,9 +77,7 @@
         mLocalRootView.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
 
         final ViewGroup contentView = getContentView(inputView);
-        mCommitIndicatorView = new CommitIndicatorView(context);
         mAddToDictionaryIndicatorView = new AddToDictionaryIndicatorView(context);
-        mLocalRootView.addView(mCommitIndicatorView);
         mLocalRootView.addView(mAddToDictionaryIndicatorView);
         if (contentView != null) {
             contentView.addView(mLocalRootView);
@@ -110,43 +112,53 @@
 
     @Override
     public void hideUi() {
-        mCommitIndicatorView.setVisibility(View.GONE);
         mAddToDictionaryIndicatorView.setVisibility(View.GONE);
         mTouchEventWindow.dismiss();
     }
 
+    private static final RectF getIndicatorBoundsInScreenCoordinates(final Matrix matrix,
+            final RectF composingTextBounds, final boolean showAtLeftSide) {
+        final float indicatorSize = composingTextBounds.height();
+        final RectF indicatorBounds;
+        if (showAtLeftSide) {
+            indicatorBounds = new RectF(composingTextBounds.left - indicatorSize,
+                    composingTextBounds.top, composingTextBounds.left,
+                    composingTextBounds.top + indicatorSize);
+        } else {
+            indicatorBounds = new RectF(composingTextBounds.right, composingTextBounds.top,
+                    composingTextBounds.right + indicatorSize,
+                    composingTextBounds.top + indicatorSize);
+        }
+        matrix.mapRect(indicatorBounds);
+        return indicatorBounds;
+    }
+
     @Override
-    public void layoutUi(final boolean isCommitMode, final Matrix matrix,
-            final RectF indicatorBounds, final RectF composingTextBounds) {
-        final RectF indicatorBoundsInScreenCoordinates = new RectF();
-        matrix.mapRect(indicatorBoundsInScreenCoordinates, indicatorBounds);
-        mCommitIndicatorView.setBounds(indicatorBoundsInScreenCoordinates);
+    public void layoutUi(final Matrix matrix, final RectF composingTextBounds,
+            final boolean useRtlLayout) {
+        RectF indicatorBoundsInScreenCoordinates = getIndicatorBoundsInScreenCoordinates(matrix,
+                composingTextBounds, useRtlLayout /* showAtLeftSide */);
+        if (indicatorBoundsInScreenCoordinates.left < mDisplayRect.left ||
+                mDisplayRect.right < indicatorBoundsInScreenCoordinates.right) {
+            // The indicator is clipped by the screen. Show the indicator at the opposite side.
+            indicatorBoundsInScreenCoordinates = getIndicatorBoundsInScreenCoordinates(matrix,
+                    composingTextBounds, !useRtlLayout /* showAtLeftSide */);
+        }
+
         mAddToDictionaryIndicatorView.setBounds(indicatorBoundsInScreenCoordinates);
 
-        final RectF hitAreaBounds = new RectF(composingTextBounds);
-        hitAreaBounds.union(indicatorBounds);
         final RectF hitAreaBoundsInScreenCoordinates = new RectF();
-        matrix.mapRect(hitAreaBoundsInScreenCoordinates, hitAreaBounds);
+        matrix.mapRect(hitAreaBoundsInScreenCoordinates, composingTextBounds);
+        hitAreaBoundsInScreenCoordinates.union(indicatorBoundsInScreenCoordinates);
         hitAreaBoundsInScreenCoordinates.inset(-mHitAreaMarginInPixels, -mHitAreaMarginInPixels);
 
         final int[] originScreen = new int[2];
         mLocalRootView.getLocationOnScreen(originScreen);
         final int viewOriginX = originScreen[0];
         final int viewOriginY = originScreen[1];
-
-        final View toBeShown;
-        final View toBeHidden;
-        if (isCommitMode) {
-            toBeShown = mCommitIndicatorView;
-            toBeHidden = mAddToDictionaryIndicatorView;
-        } else {
-            toBeShown = mAddToDictionaryIndicatorView;
-            toBeHidden = mCommitIndicatorView;
-        }
-        toBeShown.setX(indicatorBoundsInScreenCoordinates.left - viewOriginX);
-        toBeShown.setY(indicatorBoundsInScreenCoordinates.top - viewOriginY);
-        toBeShown.setVisibility(View.VISIBLE);
-        toBeHidden.setVisibility(View.GONE);
+        mAddToDictionaryIndicatorView.setX(indicatorBoundsInScreenCoordinates.left - viewOriginX);
+        mAddToDictionaryIndicatorView.setY(indicatorBoundsInScreenCoordinates.top - viewOriginY);
+        mAddToDictionaryIndicatorView.setVisibility(View.VISIBLE);
 
         if (mTouchEventWindow.isShowing()) {
             mTouchEventWindow.update((int)hitAreaBoundsInScreenCoordinates.left - viewOriginX,
@@ -239,15 +251,6 @@
         return windowContentView;
     }
 
-    private static final class CommitIndicatorView extends TextDecoratorUi.IndicatorView {
-        public CommitIndicatorView(final Context context) {
-            super(context, R.array.text_decorator_commit_indicator_path,
-                    R.integer.text_decorator_commit_indicator_path_size,
-                    R.color.text_decorator_commit_indicator_background_color,
-                    R.color.text_decorator_commit_indicator_foreground_color);
-        }
-    }
-
     private static final class AddToDictionaryIndicatorView extends TextDecoratorUi.IndicatorView {
         public AddToDictionaryIndicatorView(final Context context) {
             super(context, R.array.text_decorator_add_to_dictionary_indicator_path,
diff --git a/java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java b/java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java
index f84e12d..9e30e41 100644
--- a/java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java
+++ b/java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java
@@ -17,7 +17,6 @@
 package com.android.inputmethod.keyboard;
 
 import android.graphics.Matrix;
-import android.graphics.PointF;
 import android.graphics.RectF;
 
 /**
@@ -44,12 +43,9 @@
 
     /**
      * Called when the layout should be updated.
-     * @param isCommitMode {@code true} if the commit indicator should be shown. Show the
-     * add-to-dictionary indicator otherwise.
      * @param matrix The matrix that transforms the local coordinates into the screen coordinates.
-     * @param indicatorBounds The bounding box of the indicator, in local coordinates.
      * @param composingTextBounds The bounding box of the composing text, in local coordinates.
+     * @param useRtlLayout {@code true} if the indicator should be optimized for RTL layout.
      */
-    void layoutUi(final boolean isCommitMode, final Matrix matrix, final RectF indicatorBounds,
-            final RectF composingTextBounds);
+    void layoutUi(final Matrix matrix, final RectF composingTextBounds, final boolean useRtlLayout);
 }
diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java
index 17dfc9c..925ec6b 100644
--- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java
@@ -104,7 +104,8 @@
     public boolean onHoverEvent(final MotionEvent event) {
         final KeyboardAccessibilityDelegate<EmojiPageKeyboardView> accessibilityDelegate =
                 mAccessibilityDelegate;
-        if (accessibilityDelegate != null) {
+        if (accessibilityDelegate != null
+                && AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
             return accessibilityDelegate.onHoverEvent(event);
         }
         return super.onHoverEvent(event);
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
index 36a0266..fd1f51d 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
@@ -491,8 +491,9 @@
             final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo,
             final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId) {
         final Dictionaries dictionaries = mDictionaries;
-        final SuggestionResults suggestionResults =
-                new SuggestionResults(dictionaries.mLocale, SuggestedWords.MAX_SUGGESTIONS);
+        final SuggestionResults suggestionResults = new SuggestionResults(
+                dictionaries.mLocale, SuggestedWords.MAX_SUGGESTIONS,
+                prevWordsInfo.mPrevWordsInfo[0].mIsBeginningOfSentence);
         final float[] languageWeight = new float[] { Dictionary.NOT_A_LANGUAGE_WEIGHT };
         for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) {
             final Dictionary dictionary = dictionaries.getDict(dictType);
diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java
index ebe4361..fecb0ef 100644
--- a/java/src/com/android/inputmethod/latin/InputAttributes.java
+++ b/java/src/com/android/inputmethod/latin/InputAttributes.java
@@ -42,6 +42,7 @@
     final public boolean mApplicationSpecifiedCompletionOn;
     final public boolean mShouldInsertSpacesAutomatically;
     final public boolean mShouldShowVoiceInputKey;
+    final public boolean mIsGeneralTextInput;
     final private int mInputType;
     final private EditorInfo mEditorInfo;
     final private String mPackageNameForPrivateImeOptions;
@@ -76,6 +77,7 @@
             mApplicationSpecifiedCompletionOn = false;
             mShouldInsertSpacesAutomatically = false;
             mShouldShowVoiceInputKey = false;
+            mIsGeneralTextInput = false;
             return;
         }
         // inputClass == InputType.TYPE_CLASS_TEXT
@@ -102,7 +104,7 @@
         mShouldInsertSpacesAutomatically = InputTypeUtils.isAutoSpaceFriendlyType(inputType);
 
         final boolean noMicrophone = mIsPasswordField
-                || InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS == variation
+                || InputTypeUtils.isEmailVariation(variation)
                 || InputType.TYPE_TEXT_VARIATION_URI == variation
                 || hasNoMicrophoneKeyOption();
         mShouldShowVoiceInputKey = !noMicrophone;
@@ -117,6 +119,15 @@
                 || (!flagAutoCorrect && !flagMultiLine);
 
         mApplicationSpecifiedCompletionOn = flagAutoComplete && isFullscreenMode;
+
+        // If we come here, inputClass is always TYPE_CLASS_TEXT
+        mIsGeneralTextInput = InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS != variation
+                && InputType.TYPE_TEXT_VARIATION_PASSWORD != variation
+                && InputType.TYPE_TEXT_VARIATION_PHONETIC != variation
+                && InputType.TYPE_TEXT_VARIATION_URI != variation
+                && InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD != variation
+                && InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS != variation
+                && InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD != variation;
     }
 
     public boolean isTypeNull() {
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 86fe642..d57db8e 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -52,7 +52,6 @@
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CursorAnchorInfo;
 import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodSubtype;
 import android.widget.TextView;
 
@@ -87,8 +86,8 @@
 import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor;
 import com.android.inputmethod.latin.utils.ApplicationUtils;
 import com.android.inputmethod.latin.utils.CapsModeUtils;
-import com.android.inputmethod.latin.utils.CursorAnchorInfoUtils;
 import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.utils.CursorAnchorInfoUtils;
 import com.android.inputmethod.latin.utils.DialogUtils;
 import com.android.inputmethod.latin.utils.DistracterFilterCheckingExactMatchesAndSuggestions;
 import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
@@ -162,6 +161,9 @@
     private final SubtypeSwitcher mSubtypeSwitcher;
     private final SubtypeState mSubtypeState = new SubtypeState();
     private final SpecialKeyDetector mSpecialKeyDetector;
+    // Working variable for {@link #startShowingInputView()} and
+    // {@link #onEvaluateInputViewShown()}.
+    private boolean mIsExecutingStartShowingInputView;
 
     // Object for reacting to adding/removing a dictionary pack.
     private final BroadcastReceiver mDictionaryPackInstallReceiver =
@@ -186,9 +188,8 @@
         private static final int MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED = 6;
         private static final int MSG_RESET_CACHES = 7;
         private static final int MSG_WAIT_FOR_DICTIONARY_LOAD = 8;
-        private static final int MSG_SHOW_COMMIT_INDICATOR = 9;
         // Update this when adding new messages
-        private static final int MSG_LAST = MSG_SHOW_COMMIT_INDICATOR;
+        private static final int MSG_LAST = MSG_WAIT_FOR_DICTIONARY_LOAD;
 
         private static final int ARG1_NOT_GESTURE_INPUT = 0;
         private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1;
@@ -199,7 +200,6 @@
 
         private int mDelayInMillisecondsToUpdateSuggestions;
         private int mDelayInMillisecondsToUpdateShiftState;
-        private int mDelayInMillisecondsToShowCommitIndicator;
 
         public UIHandler(final LatinIME ownerInstance) {
             super(ownerInstance);
@@ -215,8 +215,6 @@
                     R.integer.config_delay_in_milliseconds_to_update_suggestions);
             mDelayInMillisecondsToUpdateShiftState = res.getInteger(
                     R.integer.config_delay_in_milliseconds_to_update_shift_state);
-            mDelayInMillisecondsToShowCommitIndicator = res.getInteger(
-                    R.integer.text_decorator_delay_in_milliseconds_to_show_commit_indicator);
         }
 
         @Override
@@ -274,14 +272,6 @@
                             latinIme.getCurrentRecapitalizeState());
                 }
                 break;
-            case MSG_SHOW_COMMIT_INDICATOR:
-                // Protocol of MSG_SET_COMMIT_INDICATOR_ENABLED:
-                // - what: MSG_SHOW_COMMIT_INDICATOR
-                // - arg1: not used.
-                // - arg2: not used.
-                // - obj:  the Runnable object to be called back.
-                ((Runnable) msg.obj).run();
-                break;
             case MSG_WAIT_FOR_DICTIONARY_LOAD:
                 Log.i(TAG, "Timeout waiting for dictionary load");
                 break;
@@ -382,19 +372,6 @@
             obtainMessage(MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED, suggestedWords).sendToTarget();
         }
 
-        /**
-         * Posts a delayed task to show the commit indicator.
-         *
-         * <p>Only one task can exist in the queue. When this method is called, any prior task that
-         * has not yet fired will be canceled.</p>
-         * @param task the runnable object that will be fired when the delayed task is dispatched.
-         */
-        public void postShowCommitIndicatorTask(final Runnable task) {
-            removeMessages(MSG_SHOW_COMMIT_INDICATOR);
-            sendMessageDelayed(obtainMessage(MSG_SHOW_COMMIT_INDICATOR, task),
-                    mDelayInMillisecondsToShowCommitIndicator);
-        }
-
         // Working variables for the following methods.
         private boolean mIsOrientationChanging;
         private boolean mPendingSuccessiveImsCallback;
@@ -1056,7 +1033,8 @@
         // with cursor movement when we have a hardware keyboard since we are not in charge.
         final SettingsValues settingsValues = mSettings.getCurrent();
         if ((!settingsValues.mHasHardwareKeyboard || ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED)
-                && mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd)) {
+                && mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
+                        settingsValues)) {
             mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(),
                     getCurrentRecapitalizeState());
         }
@@ -1187,22 +1165,24 @@
         outInsets.visibleTopInsets = visibleTopY;
     }
 
-    @Override
-    public boolean onEvaluateInputViewShown() {
-        // Always show {@link InputView}.
-        return true;
+    public void startShowingInputView() {
+        mIsExecutingStartShowingInputView = true;
+        // This {@link #showWindow(boolean)} will eventually call back
+        // {@link #onEvaluateInputViewShown()}.
+        showWindow(true /* showInput */);
+        mIsExecutingStartShowingInputView = false;
+    }
+
+    public void stopShowingInputView() {
+        showWindow(false /* showInput */);
     }
 
     @Override
-    public boolean onShowInputRequested(final int flags, final boolean configChange) {
-        final SettingsValues settingsValues = mSettings.getCurrent();
-        if ((flags & InputMethod.SHOW_EXPLICIT) == 0 && settingsValues.mHasHardwareKeyboard) {
-            // Even when IME is implicitly shown and physical keyboard is connected, we should
-            // show {@link InputView}.
-            // See {@link InputMethodService#onShowInputRequested(int,boolean)}.
+    public boolean onEvaluateInputViewShown() {
+        if (mIsExecutingStartShowingInputView) {
             return true;
         }
-        return super.onShowInputRequested(flags, configChange);
+        return super.onEvaluateInputViewShown();
     }
 
     @Override
@@ -1221,9 +1201,8 @@
             // hack for now.  Let's get rid of this once the framework gets fixed.
             final EditorInfo ei = getCurrentInputEditorInfo();
             return !(ei != null && ((ei.imeOptions & EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0));
-        } else {
-            return false;
         }
+        return false;
     }
 
     @Override
@@ -1273,9 +1252,8 @@
         if (null == keyboard) {
             return CoordinateUtils.newCoordinateArray(codePoints.length,
                     Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
-        } else {
-            return keyboard.getCoordinates(codePoints);
         }
+        return keyboard.getCoordinates(codePoints);
     }
 
     // Callback for the {@link SuggestionStripView}, to call when the "add to dictionary" hint is
@@ -1511,19 +1489,23 @@
         final boolean isEmptyApplicationSpecifiedCompletions =
                 currentSettingsValues.isApplicationSpecifiedCompletionsOn()
                 && suggestedWords.isEmpty();
-        final boolean noSuggestionsToShow = (SuggestedWords.EMPTY == suggestedWords)
+        final boolean noSuggestionsFromDictionaries = (SuggestedWords.EMPTY == suggestedWords)
                 || suggestedWords.isPunctuationSuggestions()
                 || isEmptyApplicationSpecifiedCompletions;
-        if (shouldShowImportantNotice && noSuggestionsToShow) {
+        final boolean isBeginningOfSentencePrediction = (suggestedWords.mInputStyle
+                == SuggestedWords.INPUT_STYLE_BEGINNING_OF_SENTENCE_PREDICTION);
+        final boolean noSuggestionsToOverrideImportantNotice = noSuggestionsFromDictionaries
+                || isBeginningOfSentencePrediction;
+        if (shouldShowImportantNotice && noSuggestionsToOverrideImportantNotice) {
             if (mSuggestionStripView.maybeShowImportantNoticeTitle()) {
                 return;
             }
         }
 
         if (currentSettingsValues.isSuggestionsEnabledPerUserSettings()
-                // We should clear suggestions if there is no suggestion to show.
-                || noSuggestionsToShow
-                || currentSettingsValues.isApplicationSpecifiedCompletionsOn()) {
+                || currentSettingsValues.isApplicationSpecifiedCompletionsOn()
+                // We should clear the contextual strip if there is no suggestion from dictionaries.
+                || noSuggestionsFromDictionaries) {
             mSuggestionStripView.setSuggestions(suggestedWords,
                     SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype()));
         }
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index b5d42dd..744b032 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -252,7 +252,7 @@
      * See {@link InputConnection#commitText(CharSequence, int)}.
      */
     public void commitText(final CharSequence text, final int newCursorPosition) {
-        commitTextWithBackgroundColor(text, newCursorPosition, Color.TRANSPARENT);
+        commitTextWithBackgroundColor(text, newCursorPosition, Color.TRANSPARENT, text.length());
     }
 
     /**
@@ -265,9 +265,11 @@
      * the background color. Note that this method specifies {@link BackgroundColorSpan} with
      * {@link Spanned#SPAN_COMPOSING} flag, meaning that the background color persists until
      * {@link #finishComposingText()} is called.
+     * @param coloredTextLength the length of text, in Java chars, which should be rendered with
+     * the given background color.
      */
     public void commitTextWithBackgroundColor(final CharSequence text, final int newCursorPosition,
-            final int color) {
+            final int color, final int coloredTextLength) {
         if (DEBUG_BATCH_NESTING) checkBatchEdit();
         if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
         mCommittedTextBeforeComposingText.append(text);
@@ -285,7 +287,8 @@
                 mTempObjectForCommitText.clear();
                 mTempObjectForCommitText.append(text);
                 final BackgroundColorSpan backgroundColorSpan = new BackgroundColorSpan(color);
-                mTempObjectForCommitText.setSpan(backgroundColorSpan, 0, text.length(),
+                final int spanLength = Math.min(coloredTextLength, text.length());
+                mTempObjectForCommitText.setSpan(backgroundColorSpan, 0, spanLength,
                         Spanned.SPAN_COMPOSING | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                 mIC.commitText(mTempObjectForCommitText, newCursorPosition);
                 mLastCommittedTextHasBackgroundColor = true;
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 6779351..b03818c 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -186,8 +186,14 @@
             suggestionsList = suggestionsContainer;
         }
 
-        final int inputStyle = resultsArePredictions ? SuggestedWords.INPUT_STYLE_PREDICTION :
-                inputStyleIfNotPrediction;
+        final int inputStyle;
+        if (resultsArePredictions) {
+            inputStyle = suggestionResults.mIsBeginningOfSentence
+                    ? SuggestedWords.INPUT_STYLE_BEGINNING_OF_SENTENCE_PREDICTION
+                    : SuggestedWords.INPUT_STYLE_PREDICTION;
+        } else {
+            inputStyle = inputStyleIfNotPrediction;
+        }
         callback.onGetSuggestedWords(new SuggestedWords(suggestionsList,
                 suggestionResults.mRawSuggestions,
                 // TODO: this first argument is lying. If this is a whitelisted word which is an
@@ -240,6 +246,8 @@
 
         // In the batch input mode, the most relevant suggested word should act as a "typed word"
         // (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false).
+        // Note that because this method is never used to get predictions, there is no need to
+        // modify inputType such in getSuggestedWordsForNonBatchInput.
         callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer,
                 suggestionResults.mRawSuggestions,
                 true /* typedWordValid */,
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 6bc3da8..1d221b7 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -39,6 +39,7 @@
     public static final int INPUT_STYLE_APPLICATION_SPECIFIED = 4;
     public static final int INPUT_STYLE_RECORRECTION = 5;
     public static final int INPUT_STYLE_PREDICTION = 6;
+    public static final int INPUT_STYLE_BEGINNING_OF_SENTENCE_PREDICTION = 7;
 
     // The maximum number of suggestions available.
     public static final int MAX_SUGGESTIONS = 18;
@@ -80,10 +81,9 @@
             final int inputStyle,
             final int sequenceNumber) {
         this(suggestedWordInfoList, rawSuggestions,
-                (suggestedWordInfoList.isEmpty() || INPUT_STYLE_PREDICTION == inputStyle) ? null
+                (suggestedWordInfoList.isEmpty() || isPrediction(inputStyle)) ? null
                         : suggestedWordInfoList.get(INDEX_OF_TYPED_WORD).mWord,
-                typedWordValid, willAutoCorrect, isObsoleteSuggestions, inputStyle,
-                sequenceNumber);
+                typedWordValid, willAutoCorrect, isObsoleteSuggestions, inputStyle, sequenceNumber);
     }
 
     public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList,
@@ -171,6 +171,7 @@
         return "SuggestedWords:"
                 + " mTypedWordValid=" + mTypedWordValid
                 + " mWillAutoCorrect=" + mWillAutoCorrect
+                + " mInputStyle=" + mInputStyle
                 + " words=" + Arrays.toString(mSuggestedWordInfoList.toArray());
     }
 
@@ -377,8 +378,13 @@
         }
     }
 
+    private static boolean isPrediction(final int inputStyle) {
+        return INPUT_STYLE_PREDICTION == inputStyle
+                || INPUT_STYLE_BEGINNING_OF_SENTENCE_PREDICTION == inputStyle;
+    }
+
     public boolean isPrediction() {
-        return INPUT_STYLE_PREDICTION == mInputStyle;
+        return isPrediction(mInputStyle);
     }
 
     // SuggestedWords is an immutable object, as much as possible. We must not just remove
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 5ab3571..fdab7f2 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -28,6 +28,7 @@
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.CursorAnchorInfo;
 import android.view.inputmethod.EditorInfo;
 
 import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper;
@@ -90,12 +91,8 @@
 
     private final TextDecorator mTextDecorator = new TextDecorator(new TextDecorator.Listener() {
         @Override
-        public void onClickComposingTextToCommit(SuggestedWordInfo wordInfo) {
-            mLatinIME.pickSuggestionManually(wordInfo);
-        }
-        @Override
-        public void onClickComposingTextToAddToDictionary(SuggestedWordInfo wordInfo) {
-            mLatinIME.addWordToUserDictionary(wordInfo.mWord);
+        public void onClickComposingTextToAddToDictionary(final String word) {
+            mLatinIME.addWordToUserDictionary(word);
             mLatinIME.dismissAddToDictionaryHint();
         }
     });
@@ -170,6 +167,7 @@
                 mConnection.requestCursorUpdates(true /* enableMonitor */,
                         true /* requestImmediateCallback */);
             }
+            mTextDecorator.reset();
         }
     }
 
@@ -333,17 +331,8 @@
         }
 
         final boolean shouldShowAddToDictionaryHint = shouldShowAddToDictionaryHint(suggestionInfo);
-        final boolean shouldShowAddToDictionaryIndicator =
-                shouldShowAddToDictionaryHint && settingsValues.mShouldShowUiToAcceptTypedWord;
-        final int backgroundColor;
-        if (shouldShowAddToDictionaryIndicator) {
-            backgroundColor = settingsValues.mTextHighlightColorForAddToDictionaryIndicator;
-        } else {
-            backgroundColor = Color.TRANSPARENT;
-        }
-        commitChosenWordWithBackgroundColor(settingsValues, suggestion,
-                LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR,
-                backgroundColor);
+        commitChosenWord(settingsValues, suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK,
+                LastComposedWord.NOT_A_SEPARATOR);
         mConnection.endBatchEdit();
         // Don't allow cancellation of manual pick
         mLastComposedWord.deactivate();
@@ -358,9 +347,6 @@
             // That's going to be predictions (or punctuation suggestions), so INPUT_STYLE_NONE.
             handler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_NONE);
         }
-        if (shouldShowAddToDictionaryIndicator) {
-            mTextDecorator.showAddToDictionaryIndicator(suggestionInfo);
-        }
         return inputTransaction;
     }
 
@@ -372,10 +358,11 @@
      * @param oldSelEnd old selection end
      * @param newSelStart new selection start
      * @param newSelEnd new selection end
+     * @param settingsValues the current values of the settings.
      * @return whether the cursor has moved as a result of user interaction.
      */
     public boolean onUpdateSelection(final int oldSelStart, final int oldSelEnd,
-            final int newSelStart, final int newSelEnd) {
+            final int newSelStart, final int newSelEnd, final SettingsValues settingsValues) {
         if (mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart, oldSelEnd, newSelEnd)) {
             return false;
         }
@@ -400,8 +387,9 @@
         // should be true, but that is if the framework had taken that wrong cursor position
         // into account, which means we have to reset the entire composing state whenever there
         // is or was a selection regardless of whether it changed or not.
-        if (hasOrHadSelection || (selectionChangedOrSafeToReset
-                && !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) {
+        if (hasOrHadSelection || !settingsValues.needsToLookupSuggestions()
+                || (selectionChangedOrSafeToReset
+                        && !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) {
             // If we are composing a word and moving the cursor, we would want to set a
             // suggestion span for recorrection to work correctly. Unfortunately, that
             // would involve the keyboard committing some new text, which would move the
@@ -430,6 +418,9 @@
         mRecapitalizeStatus.enable();
         // We moved the cursor and need to invalidate the indicator right now.
         mTextDecorator.reset();
+        // Remaining background color that was used for the add-to-dictionary indicator should be
+        // removed.
+        mConnection.removeBackgroundColorFromHighlightedTextIfNecessary();
         // We moved the cursor. If we are touching a word, we need to resume suggestion.
         mLatinIME.mHandler.postResumeSuggestions(false /* shouldIncludeResumedWordInSuggestions */,
                 true /* shouldDelay */);
@@ -508,7 +499,9 @@
         handler.cancelUpdateSuggestionStrip();
         ++mAutoCommitSequenceNumber;
         mConnection.beginBatchEdit();
-        if (mWordComposer.isComposingWord()) {
+        if (!mWordComposer.isComposingWord()) {
+            mConnection.removeBackgroundColorFromHighlightedTextIfNecessary();
+        } else {
             if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
                 // If we are in the middle of a recorrection, we need to commit the recorrection
                 // first so that we can insert the batch input at the current cursor position.
@@ -620,42 +613,6 @@
         }
         mSuggestedWords = suggestedWords;
         final boolean newAutoCorrectionIndicator = suggestedWords.mWillAutoCorrect;
-        if (shouldShowCommitIndicator(suggestedWords, settingsValues)) {
-            // typedWordInfo is never null here.
-            final int textBackgroundColor = settingsValues.mTextHighlightColorForCommitIndicator;
-            final SuggestedWordInfo typedWordInfo = suggestedWords.getTypedWordInfoOrNull();
-            handler.postShowCommitIndicatorTask(new Runnable() {
-                @Override
-                public void run() {
-                    // TODO: This needs to be refactored to ensure that mWordComposer is accessed
-                    // only from the UI thread.
-                    if (!mWordComposer.isComposingWord()) {
-                        mTextDecorator.reset();
-                        return;
-                    }
-                    final SuggestedWordInfo currentTypedWordInfo =
-                            mSuggestedWords.getTypedWordInfoOrNull();
-                    if (currentTypedWordInfo == null) {
-                        mTextDecorator.reset();
-                        return;
-                    }
-                    if (!currentTypedWordInfo.equals(typedWordInfo)) {
-                        // Suggested word has been changed. This task is obsolete.
-                        mTextDecorator.reset();
-                        return;
-                    }
-                    // TODO: As with the above TODO comment, this operation must be performed only
-                    // on the UI thread too. Needs to be refactored.
-                    setComposingTextInternalWithBackgroundColor(typedWordInfo.mWord,
-                            1 /* newCursorPosition */, textBackgroundColor);
-                    mTextDecorator.showCommitIndicator(typedWordInfo);
-                }
-            });
-        } else {
-            // Note: It is OK to not cancel previous postShowCommitIndicatorTask() here. Having a
-            // cancellation mechanism could improve performance a bit though.
-            mTextDecorator.reset();
-        }
 
         // Put a blue underline to a word in TextView which will be auto-corrected.
         if (mIsAutoCorrectionIndicatorOn != newAutoCorrectionIndicator
@@ -833,13 +790,14 @@
             final InputTransaction inputTransaction,
             // TODO: remove this argument
             final LatinIME.UIHandler handler) {
-        // In case the "add to dictionary" hint was still displayed.
-        // TODO: Do we really need to check if we have composing text here?
-        if (!mWordComposer.isComposingWord() &&
-                mSuggestionStripViewAccessor.isShowingAddToDictionaryHint()) {
-            mSuggestionStripViewAccessor.dismissAddToDictionaryHint();
+        if (!mWordComposer.isComposingWord()) {
             mConnection.removeBackgroundColorFromHighlightedTextIfNecessary();
-            mTextDecorator.reset();
+            // In case the "add to dictionary" hint was still displayed.
+            // TODO: Do we really need to check if we have composing text here?
+            if (mSuggestionStripViewAccessor.isShowingAddToDictionaryHint()) {
+                mSuggestionStripViewAccessor.dismissAddToDictionaryHint();
+                mTextDecorator.reset();
+            }
         }
 
         final int codePoint = event.mCodePoint;
@@ -1096,7 +1054,7 @@
             inputTransaction.setRequiresUpdateSuggestions();
         } else {
             if (mLastComposedWord.canRevertCommit()) {
-                revertCommit(inputTransaction);
+                revertCommit(inputTransaction, inputTransaction.mSettingsValues);
                 return;
             }
             if (mEnteredText != null && mConnection.sameAsTextBeforeCursor(mEnteredText)) {
@@ -1582,14 +1540,19 @@
      * This is triggered upon pressing backspace just after a commit with auto-correction.
      *
      * @param inputTransaction The transaction in progress.
+     * @param settingsValues the current values of the settings.
      */
-    private void revertCommit(final InputTransaction inputTransaction) {
+    private void revertCommit(final InputTransaction inputTransaction,
+            final SettingsValues settingsValues) {
         final CharSequence originallyTypedWord = mLastComposedWord.mTypedWord;
+        final String originallyTypedWordString =
+                originallyTypedWord != null ? originallyTypedWord.toString() : "";
         final CharSequence committedWord = mLastComposedWord.mCommittedWord;
         final String committedWordString = committedWord.toString();
         final int cancelLength = committedWord.length();
+        final String separatorString = mLastComposedWord.mSeparatorString;
         // We want java chars, not codepoints for the following.
-        final int separatorLength = mLastComposedWord.mSeparatorString.length();
+        final int separatorLength = separatorString.length();
         // TODO: should we check our saved separator against the actual contents of the text view?
         final int deleteLength = cancelLength + separatorLength;
         if (DebugFlags.DEBUG_ENABLED) {
@@ -1608,7 +1571,7 @@
         if (!TextUtils.isEmpty(committedWord)) {
             mDictionaryFacilitator.removeWordFromPersonalizedDicts(committedWordString);
         }
-        final String stringToCommit = originallyTypedWord + mLastComposedWord.mSeparatorString;
+        final String stringToCommit = originallyTypedWord + separatorString;
         final SpannableString textToCommit = new SpannableString(stringToCommit);
         if (committedWord instanceof SpannableString) {
             final SpannableString committedWordWithSuggestionSpans = (SpannableString)committedWord;
@@ -1645,23 +1608,53 @@
                     suggestions.toArray(new String[suggestions.size()]), 0 /* flags */),
                     0 /* start */, lastCharIndex /* end */, 0 /* flags */);
         }
+
+        final boolean shouldShowAddToDictionaryForTypedWord =
+                shouldShowAddToDictionaryForTypedWord(mLastComposedWord, settingsValues);
+
         if (inputTransaction.mSettingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces) {
             // For languages with spaces, we revert to the typed string, but the cursor is still
             // after the separator so we don't resume suggestions. If the user wants to correct
             // the word, they have to press backspace again.
-            mConnection.commitText(textToCommit, 1);
+            if (shouldShowAddToDictionaryForTypedWord) {
+                mConnection.commitTextWithBackgroundColor(textToCommit, 1,
+                        settingsValues.mTextHighlightColorForAddToDictionaryIndicator,
+                        originallyTypedWordString.length());
+            } else {
+                mConnection.commitText(textToCommit, 1);
+            }
         } else {
             // For languages without spaces, we revert the typed string but the cursor is flush
             // with the typed word, so we need to resume suggestions right away.
             final int[] codePoints = StringUtils.toCodePointArray(stringToCommit);
             mWordComposer.setComposingWord(codePoints,
                     mLatinIME.getCoordinatesForCurrentKeyboard(codePoints));
-            setComposingTextInternal(textToCommit, 1);
+            if (shouldShowAddToDictionaryForTypedWord) {
+                setComposingTextInternalWithBackgroundColor(textToCommit, 1,
+                        settingsValues.mTextHighlightColorForAddToDictionaryIndicator,
+                        originallyTypedWordString.length());
+            } else {
+                setComposingTextInternal(textToCommit, 1);
+            }
         }
         // Don't restart suggestion yet. We'll restart if the user deletes the separator.
         mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
-        // We have a separator between the word and the cursor: we should show predictions.
-        inputTransaction.setRequiresUpdateSuggestions();
+
+        if (shouldShowAddToDictionaryForTypedWord) {
+            // Due to the API limitation as of L, we cannot reliably retrieve the reverted text
+            // when the separator causes line breaking. Until this API limitation is addressed in
+            // the framework, show the indicator only when the separator doesn't contain
+            // line-breaking characters.
+            if (!StringUtils.hasLineBreakCharacter(separatorString)) {
+                mTextDecorator.showAddToDictionaryIndicator(originallyTypedWordString,
+                        mConnection.getExpectedSelectionStart(),
+                        mConnection.getExpectedSelectionEnd());
+            }
+            mSuggestionStripViewAccessor.showAddToDictionaryHint(originallyTypedWordString);
+        } else {
+            // We have a separator between the word and the cursor: we should show predictions.
+            inputTransaction.setRequiresUpdateSuggestions();
+        }
     }
 
     /**
@@ -2085,9 +2078,7 @@
     }
 
     /**
-     * Commits the chosen word to the text field and saves it for later retrieval. This is a
-     * synonym of {@code commitChosenWordWithBackgroundColor(settingsValues, chosenWord,
-     * commitType, separatorString, Color.TRANSPARENT}.
+     * Commits the chosen word to the text field and saves it for later retrieval.
      *
      * @param settingsValues the current values of the settings.
      * @param chosenWord the word we want to commit.
@@ -2096,23 +2087,6 @@
      */
     private void commitChosenWord(final SettingsValues settingsValues, final String chosenWord,
             final int commitType, final String separatorString) {
-        commitChosenWordWithBackgroundColor(settingsValues, chosenWord, commitType, separatorString,
-                Color.TRANSPARENT);
-    }
-
-    /**
-     * Commits the chosen word to the text field and saves it for later retrieval.
-     *
-     * @param settingsValues the current values of the settings.
-     * @param chosenWord the word we want to commit.
-     * @param commitType the type of the commit, as one of LastComposedWord.COMMIT_TYPE_*
-     * @param separatorString the separator that's causing the commit, or NOT_A_SEPARATOR if none.
-     * @param backgroundColor the background color to be specified with the committed text. Pass
-     * {@link Color#TRANSPARENT} to not specify the background color.
-     */
-    private void commitChosenWordWithBackgroundColor(final SettingsValues settingsValues,
-            final String chosenWord, final int commitType, final String separatorString,
-            final int backgroundColor) {
         final SuggestedWords suggestedWords = mSuggestedWords;
         final CharSequence chosenWordWithSuggestions =
                 SuggestionSpanUtils.getTextWithSuggestionSpan(mLatinIME, chosenWord,
@@ -2122,7 +2096,7 @@
         // information from the 1st previous word.
         final PrevWordsInfo prevWordsInfo = mConnection.getPrevWordsInfoFromNthPreviousWord(
                 settingsValues.mSpacingAndPunctuations, mWordComposer.isComposingWord() ? 2 : 1);
-        mConnection.commitTextWithBackgroundColor(chosenWordWithSuggestions, 1, backgroundColor);
+        mConnection.commitText(chosenWordWithSuggestions, 1);
         // Add the word to the user history dictionary
         performAdditionToUserHistoryDictionary(settingsValues, chosenWord, prevWordsInfo);
         // TODO: figure out here if this is an auto-correct or if the best word is actually
@@ -2206,7 +2180,7 @@
     private void setComposingTextInternal(final CharSequence newComposingText,
             final int newCursorPosition) {
         setComposingTextInternalWithBackgroundColor(newComposingText, newCursorPosition,
-                Color.TRANSPARENT);
+                Color.TRANSPARENT, newComposingText.length());
     }
 
     /**
@@ -2222,9 +2196,11 @@
      * @param newCursorPosition the new cursor position
      * @param backgroundColor the background color to be set to the composing text. Set
      * {@link Color#TRANSPARENT} to disable the background color.
+     * @param coloredTextLength the length of text, in Java chars, which should be rendered with
+     * the given background color.
      */
     private void setComposingTextInternalWithBackgroundColor(final CharSequence newComposingText,
-            final int newCursorPosition, final int backgroundColor) {
+            final int newCursorPosition, final int backgroundColor, final int coloredTextLength) {
         final CharSequence composingTextToBeSet;
         if (backgroundColor == Color.TRANSPARENT) {
             composingTextToBeSet = newComposingText;
@@ -2232,7 +2208,8 @@
             final SpannableString spannable = new SpannableString(newComposingText);
             final BackgroundColorSpan backgroundColorSpan =
                     new BackgroundColorSpan(backgroundColor);
-            spannable.setSpan(backgroundColorSpan, 0, spannable.length(),
+            final int spanLength = Math.min(coloredTextLength, spannable.length());
+            spannable.setSpan(backgroundColorSpan, 0, spanLength,
                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
             composingTextToBeSet = spannable;
         }
@@ -2254,7 +2231,8 @@
     }
 
     /**
-     * Must be called from {@link InputMethodService#onUpdateCursorAnchorInfo} is called.
+     * Must be called from {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} is
+     * called.
      * @param info The wrapper object with which we can access cursor/anchor info.
      */
     public void onUpdateCursorAnchorInfo(final CursorAnchorInfoCompatWrapper info) {
@@ -2278,12 +2256,12 @@
     }
 
     /**
-     * Returns whether the commit indicator should be shown or not.
-     * @param suggestedWords the suggested word that is being displayed.
+     * Returns whether the add to dictionary indicator should be shown or not.
+     * @param lastComposedWord the last composed word information.
      * @param settingsValues the current settings value.
      * @return {@code true} if the commit indicator should be shown.
      */
-    private boolean shouldShowCommitIndicator(final SuggestedWords suggestedWords,
+    private boolean shouldShowAddToDictionaryForTypedWord(final LastComposedWord lastComposedWord,
             final SettingsValues settingsValues) {
         if (!mConnection.isCursorAnchorInfoMonitorEnabled()) {
             // We cannot help in this case because we are heavily relying on this new API.
@@ -2292,24 +2270,16 @@
         if (!settingsValues.mShouldShowUiToAcceptTypedWord) {
             return false;
         }
-        final SuggestedWordInfo typedWordInfo = suggestedWords.getTypedWordInfoOrNull();
-        if (typedWordInfo == null) {
+        if (TextUtils.isEmpty(lastComposedWord.mTypedWord)) {
             return false;
         }
-        if (suggestedWords.mInputStyle != SuggestedWords.INPUT_STYLE_TYPING){
+        if (TextUtils.equals(lastComposedWord.mTypedWord, lastComposedWord.mCommittedWord)) {
             return false;
         }
-        if (settingsValues.mShowCommitIndicatorOnlyForAutoCorrection
-                && !suggestedWords.mWillAutoCorrect) {
+        if (!mDictionaryFacilitator.isUserDictionaryEnabled()) {
             return false;
         }
-        // TODO: Calling shouldShowAddToDictionaryHint(typedWordInfo) multiple times should be fine
-        // in terms of performance, but we can do better. One idea is to make SuggestedWords include
-        // a boolean that tells whether the word is a dictionary word or not.
-        if (settingsValues.mShowCommitIndicatorOnlyForOutOfVocabulary
-                && !shouldShowAddToDictionaryHint(typedWordInfo)) {
-            return false;
-        }
-        return true;
+        return !mDictionaryFacilitator.isValidWord(lastComposedWord.mTypedWord,
+                true /* ignoreCase */);
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java
new file mode 100644
index 0000000..f5e4d33
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.settings;
+
+import android.os.Bundle;
+
+import com.android.inputmethod.latin.R;
+
+
+/**
+ * "Appearance" settings sub screen.
+ */
+public final class AppearanceSettingsFragment extends SubScreenFragment {
+    @Override
+    public void onCreate(final Bundle icicle) {
+        super.onCreate(icicle);
+        addPreferencesFromResource(R.xml.prefs_screen_appearance);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        CustomInputStyleSettingsFragment.updateCustomInputStylesSummary(
+                findPreference(Settings.PREF_CUSTOM_INPUT_STYLES));
+        ThemeSettingsFragment.updateKeyboardThemeSummary(findPreference(Settings.SCREEN_THEME));
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java
index d53a616..9bc3986 100644
--- a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java
@@ -31,6 +31,7 @@
 import android.preference.PreferenceFragment;
 import android.preference.PreferenceGroup;
 import android.support.v4.view.ViewCompat;
+import android.text.TextUtils;
 import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -396,6 +397,25 @@
         // Empty constructor for fragment generation.
     }
 
+    static void updateCustomInputStylesSummary(final Preference pref) {
+        // 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()}.
+        SubtypeLocaleUtils.init(pref.getContext());
+
+        final Resources res = pref.getContext().getResources();
+        final SharedPreferences prefs = pref.getSharedPreferences();
+        final String prefSubtype = Settings.readPrefAdditionalSubtypes(prefs, res);
+        final InputMethodSubtype[] subtypes =
+                AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtype);
+        final ArrayList<String> subtypeNames = new ArrayList<>();
+        for (final InputMethodSubtype subtype : subtypes) {
+            subtypeNames.add(SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype));
+        }
+        // TODO: A delimiter of custom input styles should be localized.
+        pref.setSummary(TextUtils.join(", ", subtypeNames));
+    }
+
     @Override
     public void onCreate(final Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
diff --git a/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java
index fcdd393..b073c50 100644
--- a/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java
@@ -16,66 +16,27 @@
 
 package com.android.inputmethod.latin.settings;
 
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
 import android.os.Bundle;
-import android.preference.PreferenceScreen;
-import android.text.TextUtils;
-import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
-import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
 
 import java.util.ArrayList;
 
 /**
- * "Multi lingual options" settings sub screen.
+ * "Multilingual options" settings sub screen.
  *
  * This settings sub screen handles the following input preferences.
  * - Language switch key
  * - Switch to other input methods
- * - Custom input styles
  */
 public final class MultiLingualSettingsFragment extends SubScreenFragment {
     @Override
     public void onCreate(final Bundle icicle) {
         super.onCreate(icicle);
-        addPreferencesFromResource(R.xml.prefs_screen_multi_lingual);
-
-        final Context context = getActivity();
-
-        // 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()}.
-        SubtypeLocaleUtils.init(context);
-
+        addPreferencesFromResource(R.xml.prefs_screen_multilingual);
         if (!Settings.ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS) {
             removePreference(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY);
             removePreference(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST);
         }
     }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        updateCustomInputStylesSummary();
-    }
-
-    private void updateCustomInputStylesSummary() {
-        final SharedPreferences prefs = getSharedPreferences();
-        final Resources res = getResources();
-        final PreferenceScreen customInputStyles =
-                (PreferenceScreen)findPreference(Settings.PREF_CUSTOM_INPUT_STYLES);
-        final String prefSubtype = Settings.readPrefAdditionalSubtypes(prefs, res);
-        final InputMethodSubtype[] subtypes =
-                AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtype);
-        final ArrayList<String> subtypeNames = new ArrayList<>();
-        for (final InputMethodSubtype subtype : subtypes) {
-            subtypeNames.add(SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype));
-        }
-        // TODO: A delimiter of custom input styles should be localized.
-        customInputStyles.setSummary(TextUtils.join(", ", subtypeNames));
-    }
 }
diff --git a/java/src/com/android/inputmethod/latin/settings/InputSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java
similarity index 95%
rename from java/src/com/android/inputmethod/latin/settings/InputSettingsFragment.java
rename to java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java
index f459d68..49db2bd 100644
--- a/java/src/com/android/inputmethod/latin/settings/InputSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java
@@ -27,7 +27,7 @@
 import com.android.inputmethod.latin.SubtypeSwitcher;
 
 /**
- * "Input preferences" settings sub screen.
+ * "Preferences" settings sub screen.
  *
  * This settings sub screen handles the following input preferences.
  * - Auto-capitalization
@@ -37,11 +37,11 @@
  * - Popup on keypress
  * - Voice input key
  */
-public final class InputSettingsFragment extends SubScreenFragment {
+public final class PreferencesSettingsFragment extends SubScreenFragment {
     @Override
     public void onCreate(final Bundle icicle) {
         super.onCreate(icicle);
-        addPreferencesFromResource(R.xml.prefs_screen_input);
+        addPreferencesFromResource(R.xml.prefs_screen_preferences);
 
         final Resources res = getResources();
         final Context context = getActivity();
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index 3c7a991..0de2d88 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -42,9 +42,10 @@
 public final class Settings implements SharedPreferences.OnSharedPreferenceChangeListener {
     private static final String TAG = Settings.class.getSimpleName();
     // Settings screens
-    public static final String SCREEN_INPUT = "screen_input";
+    public static final String SCREEN_PREFERENCES = "screen_preferences";
+    public static final String SCREEN_APPEARANCE = "screen_appearance";
     public static final String SCREEN_THEME = "screen_theme";
-    public static final String SCREEN_MULTI_LINGUAL = "screen_multi_lingual";
+    public static final String SCREEN_MULTILINGUAL = "screen_multilingual";
     public static final String SCREEN_GESTURE = "screen_gesture";
     public static final String SCREEN_CORRECTION = "screen_correction";
     public static final String SCREEN_ADVANCED = "screen_advanced";
@@ -69,6 +70,9 @@
             "pref_key_use_double_space_period";
     public static final String PREF_BLOCK_POTENTIALLY_OFFENSIVE =
             "pref_key_block_potentially_offensive";
+    // No multilingual options in Android L and above for now.
+    public static final boolean SHOW_MULTILINGUAL_SETTINGS =
+            BuildCompatUtils.EFFECTIVE_SDK_INT <= Build.VERSION_CODES.KITKAT;
     public static final boolean ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS =
             BuildCompatUtils.EFFECTIVE_SDK_INT <= Build.VERSION_CODES.KITKAT;
     public static final boolean HAS_UI_TO_ACCEPT_TYPED_WORD =
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
index ff74958..4fc1738 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
@@ -18,6 +18,7 @@
 
 import android.content.Intent;
 import android.os.Bundle;
+import android.preference.Preference;
 import android.preference.PreferenceScreen;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -46,12 +47,10 @@
         final PreferenceScreen preferenceScreen = getPreferenceScreen();
         preferenceScreen.setTitle(
                 ApplicationUtils.getActivityTitleResId(getActivity(), SettingsActivity.class));
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        ThemeSettingsFragment.updateKeyboardThemeSummary(findPreference(Settings.SCREEN_THEME));
+        if (!Settings.SHOW_MULTILINGUAL_SETTINGS) {
+            final Preference multilingualOptions = findPreference(Settings.SCREEN_MULTILINGUAL);
+            preferenceScreen.removePreference(multilingualOptions);
+        }
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index 308f3b4..d8c548d 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -97,10 +97,7 @@
             new int[AdditionalFeaturesSettingUtils.ADDITIONAL_FEATURES_SETTINGS_SIZE];
 
     // TextDecorator
-    public final int mTextHighlightColorForCommitIndicator;
     public final int mTextHighlightColorForAddToDictionaryIndicator;
-    public final boolean mShowCommitIndicatorOnlyForAutoCorrection;
-    public final boolean mShowCommitIndicatorOnlyForOutOfVocabulary;
 
     // Debug settings
     public final boolean mIsInternal;
@@ -148,7 +145,8 @@
                 ? Settings.readShowsLanguageSwitchKey(prefs) : true /* forcibly */;
         mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true);
         mUsePersonalizedDicts = prefs.getBoolean(Settings.PREF_KEY_USE_PERSONALIZED_DICTS, true);
-        mUseDoubleSpacePeriod = prefs.getBoolean(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true);
+        mUseDoubleSpacePeriod = prefs.getBoolean(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true)
+                && inputAttributes.mIsGeneralTextInput;
         mBlockPotentiallyOffensive = Settings.readBlockPotentiallyOffensive(prefs, res);
         mAutoCorrectEnabled = Settings.readAutoCorrectEnabled(autoCorrectionThresholdRawValue, res);
         mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res);
@@ -174,12 +172,6 @@
         mSuggestionsEnabledPerUserSettings = readSuggestionsEnabled(prefs);
         AdditionalFeaturesSettingUtils.readAdditionalFeaturesPreferencesIntoArray(
                 prefs, mAdditionalFeaturesSettingValues);
-        mShowCommitIndicatorOnlyForAutoCorrection = res.getBoolean(
-                R.bool.text_decorator_only_for_auto_correction);
-        mShowCommitIndicatorOnlyForOutOfVocabulary = res.getBoolean(
-                R.bool.text_decorator_only_for_out_of_vocabulary);
-        mTextHighlightColorForCommitIndicator = res.getColor(
-                R.color.text_decorator_commit_indicator_text_highlight_color);
         mTextHighlightColorForAddToDictionaryIndicator = res.getColor(
                 R.color.text_decorator_add_to_dictionary_indicator_text_highlight_color);
         mIsInternal = Settings.isInternal(prefs);
@@ -425,12 +417,6 @@
         sb.append("" + (null == awu ? "null" : awu.toString()));
         sb.append("\n   mAdditionalFeaturesSettingValues = ");
         sb.append("" + Arrays.toString(mAdditionalFeaturesSettingValues));
-        sb.append("\n   mShowCommitIndicatorOnlyForAutoCorrection = ");
-        sb.append("" + mShowCommitIndicatorOnlyForAutoCorrection);
-        sb.append("\n   mShowCommitIndicatorOnlyForOutOfVocabulary = ");
-        sb.append("" + mShowCommitIndicatorOnlyForOutOfVocabulary);
-        sb.append("\n   mTextHighlightColorForCommitIndicator = ");
-        sb.append("" + mTextHighlightColorForCommitIndicator);
         sb.append("\n   mTextHighlightColorForAddToDictionaryIndicator = ");
         sb.append("" + mTextHighlightColorForAddToDictionaryIndicator);
         sb.append("\n   mIsInternal = ");
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index 33745a8..0fd5e13 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -408,8 +408,8 @@
             // Decided to be in the sliding suggestion mode only when the touch point has been moved
             // upward. Further {@link MotionEvent}s will be delivered to
             // {@link #onTouchEvent(MotionEvent)}.
-            mNeedsToTransformTouchEventToHoverEvent = AccessibilityUtils.getInstance()
-                    .isTouchExplorationEnabled();
+            mNeedsToTransformTouchEventToHoverEvent =
+                    AccessibilityUtils.getInstance().isTouchExplorationEnabled();
             mIsDispatchingHoverEventToMoreSuggestions = false;
             return true;
         }
diff --git a/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java b/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java
index 08f5b0b..c2167a7 100644
--- a/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java
@@ -19,12 +19,13 @@
 import com.android.inputmethod.dictionarypack.DictionarySettingsFragment;
 import com.android.inputmethod.latin.about.AboutPreferences;
 import com.android.inputmethod.latin.settings.AdvancedSettingsFragment;
+import com.android.inputmethod.latin.settings.AppearanceSettingsFragment;
 import com.android.inputmethod.latin.settings.CorrectionSettingsFragment;
 import com.android.inputmethod.latin.settings.CustomInputStyleSettingsFragment;
 import com.android.inputmethod.latin.settings.DebugSettingsFragment;
 import com.android.inputmethod.latin.settings.GestureSettingsFragment;
-import com.android.inputmethod.latin.settings.InputSettingsFragment;
 import com.android.inputmethod.latin.settings.MultiLingualSettingsFragment;
+import com.android.inputmethod.latin.settings.PreferencesSettingsFragment;
 import com.android.inputmethod.latin.settings.SettingsFragment;
 import com.android.inputmethod.latin.settings.ThemeSettingsFragment;
 import com.android.inputmethod.latin.spellcheck.SpellCheckerSettingsFragment;
@@ -40,7 +41,8 @@
     static {
         sLatinImeFragments.add(DictionarySettingsFragment.class.getName());
         sLatinImeFragments.add(AboutPreferences.class.getName());
-        sLatinImeFragments.add(InputSettingsFragment.class.getName());
+        sLatinImeFragments.add(PreferencesSettingsFragment.class.getName());
+        sLatinImeFragments.add(AppearanceSettingsFragment.class.getName());
         sLatinImeFragments.add(ThemeSettingsFragment.class.getName());
         sLatinImeFragments.add(MultiLingualSettingsFragment.class.getName());
         sLatinImeFragments.add(CustomInputStyleSettingsFragment.class.getName());
diff --git a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java
index 8b70778..ea406fa 100644
--- a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java
@@ -23,15 +23,24 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.R;
 
+import java.util.concurrent.TimeUnit;
+
 public final class ImportantNoticeUtils {
     private static final String TAG = ImportantNoticeUtils.class.getSimpleName();
 
     // {@link SharedPreferences} name to save the last important notice version that has been
     // displayed to users.
     private static final String PREFERENCE_NAME = "important_notice_pref";
-    private static final String KEY_IMPORTANT_NOTICE_VERSION = "important_notice_version";
+    @UsedForTesting
+    static final String KEY_IMPORTANT_NOTICE_VERSION = "important_notice_version";
+    @UsedForTesting
+    static final String KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE =
+            "timestamp_of_first_important_notice";
+    @UsedForTesting
+    static final long TIMEOUT_OF_IMPORTANT_NOTICE = TimeUnit.HOURS.toMillis(23);
     public static final int VERSION_TO_ENABLE_PERSONALIZED_SUGGESTIONS = 1;
 
     // Copy of the hidden {@link Settings.Secure#USER_SETUP_COMPLETE} settings key.
@@ -56,15 +65,18 @@
         }
     }
 
-    private static SharedPreferences getImportantNoticePreferences(final Context context) {
+    @UsedForTesting
+    static SharedPreferences getImportantNoticePreferences(final Context context) {
         return context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
     }
 
-    private static int getCurrentImportantNoticeVersion(final Context context) {
+    @UsedForTesting
+    static int getCurrentImportantNoticeVersion(final Context context) {
         return context.getResources().getInteger(R.integer.config_important_notice_version);
     }
 
-    private static int getLastImportantNoticeVersion(final Context context) {
+    @UsedForTesting
+    static int getLastImportantNoticeVersion(final Context context) {
         return getImportantNoticePreferences(context).getInt(KEY_IMPORTANT_NOTICE_VERSION, 0);
     }
 
@@ -77,6 +89,20 @@
         return getCurrentImportantNoticeVersion(context) > lastVersion;
     }
 
+    @UsedForTesting
+    static boolean hasTimeoutPassed(final Context context, final long currentTimeInMillis) {
+        final SharedPreferences prefs = getImportantNoticePreferences(context);
+        if (!prefs.contains(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE)) {
+            prefs.edit()
+                    .putLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE, currentTimeInMillis)
+                    .apply();
+        }
+        final long firstDisplayTimeInMillis = prefs.getLong(
+                KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE, currentTimeInMillis);
+        final long elapsedTime = currentTimeInMillis - firstDisplayTimeInMillis;
+        return elapsedTime >= TIMEOUT_OF_IMPORTANT_NOTICE;
+    }
+
     public static boolean shouldShowImportantNotice(final Context context) {
         if (!hasNewImportantNotice(context)) {
             return false;
@@ -88,6 +114,10 @@
         if (isInSystemSetupWizard(context)) {
             return false;
         }
+        if (hasTimeoutPassed(context, System.currentTimeMillis())) {
+            updateLastImportantNoticeVersion(context);
+            return false;
+        }
         return true;
     }
 
@@ -95,11 +125,12 @@
         getImportantNoticePreferences(context)
                 .edit()
                 .putInt(KEY_IMPORTANT_NOTICE_VERSION, getNextImportantNoticeVersion(context))
+                .remove(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE)
                 .apply();
     }
 
     public static String getNextImportantNoticeTitle(final Context context) {
-        final int nextVersion = getCurrentImportantNoticeVersion(context);
+        final int nextVersion = getNextImportantNoticeVersion(context);
         final String[] importantNoticeTitleArray = context.getResources().getStringArray(
                 R.array.important_notice_title_array);
         if (nextVersion > 0 && nextVersion < importantNoticeTitleArray.length) {
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index 38f0b3f..79128db 100644
--- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
@@ -37,6 +37,14 @@
 
     private static final String EMPTY_STRING = "";
 
+    private static final char CHAR_LINE_FEED = 0X000A;
+    private static final char CHAR_VERTICAL_TAB = 0X000B;
+    private static final char CHAR_FORM_FEED = 0X000C;
+    private static final char CHAR_CARRIAGE_RETURN = 0X000D;
+    private static final char CHAR_NEXT_LINE = 0X0085;
+    private static final char CHAR_LINE_SEPARATOR = 0X2028;
+    private static final char CHAR_PARAGRAPH_SEPARATOR = 0X2029;
+
     private StringUtils() {
         // This utility class is not publicly instantiable.
     }
@@ -123,20 +131,20 @@
 
     public static String capitalizeFirstCodePoint(final String s, final Locale locale) {
         if (s.length() <= 1) {
-            return s.toUpperCase(locale);
+            return toUpperCaseOfStringForLocale(s, true /* needsToUpperCase */, locale);
         }
         // Please refer to the comment below in
         // {@link #capitalizeFirstAndDowncaseRest(String,Locale)} as this has the same shortcomings
         final int cutoff = s.offsetByCodePoints(0, 1);
-        return s.substring(0, cutoff).toUpperCase(locale) + s.substring(cutoff);
+        return toUpperCaseOfStringForLocale(
+                s.substring(0, cutoff), true /* needsToUpperCase */, locale) + s.substring(cutoff);
     }
 
     public static String capitalizeFirstAndDowncaseRest(final String s, final Locale locale) {
         if (s.length() <= 1) {
-            return s.toUpperCase(locale);
+            return toUpperCaseOfStringForLocale(s, true /* needsToUpperCase */, locale);
         }
         // TODO: fix the bugs below
-        // - This does not work for Greek, because it returns upper case instead of title case.
         // - It does not work for Serbian, because it fails to account for the "lj" character,
         // which should be "Lj" in title case and "LJ" in upper case.
         // - It does not work for Dutch, because it fails to account for the "ij" digraph when it's
@@ -144,7 +152,9 @@
         // be capitalized as "IJ" as if they were a single letter in most words (not all). If the
         // unicode char for the ligature is used however, it works.
         final int cutoff = s.offsetByCodePoints(0, 1);
-        return s.substring(0, cutoff).toUpperCase(locale) + s.substring(cutoff).toLowerCase(locale);
+        final String titleCaseFirstLetter = toUpperCaseOfStringForLocale(
+                s.substring(0, cutoff), true /* needsToUpperCase */, locale);
+        return titleCaseFirstLetter + s.substring(cutoff).toLowerCase(locale);
     }
 
     private static final int[] EMPTY_CODEPOINTS = {};
@@ -481,10 +491,23 @@
         return bytes;
     }
 
+    private static final String LANGUAGE_GREEK = "el";
+
+    private static Locale getLocaleUsedForToTitleCase(final Locale locale) {
+        // In Greek locale {@link String#toUpperCase(Locale)} eliminates accents from its result.
+        // In order to get accented upper case letter, {@link Locale#ROOT} should be used.
+        if (LANGUAGE_GREEK.equals(locale.getLanguage())) {
+            return Locale.ROOT;
+        }
+        return locale;
+    }
+
     public static String toUpperCaseOfStringForLocale(final String text,
             final boolean needsToUpperCase, final Locale locale) {
-        if (text == null || !needsToUpperCase) return text;
-        return text.toUpperCase(locale);
+        if (text == null || !needsToUpperCase) {
+            return text;
+        }
+        return text.toUpperCase(getLocaleUsedForToTitleCase(locale));
     }
 
     public static int toUpperCaseOfCodeForLocale(final int code, final boolean needsToUpperCase,
@@ -594,4 +617,30 @@
             return sb + "]";
         }
     }
+
+    /**
+     * Returns whether the last composed word contains line-breaking character (e.g. CR or LF).
+     * @param text the text to be examined.
+     * @return {@code true} if the last composed word contains line-breaking separator.
+     */
+    @UsedForTesting
+    public static boolean hasLineBreakCharacter(final String text) {
+        if (TextUtils.isEmpty(text)) {
+            return false;
+        }
+        for (int i = text.length() - 1; i >= 0; --i) {
+            final char c = text.charAt(i);
+            switch (c) {
+                case CHAR_LINE_FEED:
+                case CHAR_VERTICAL_TAB:
+                case CHAR_FORM_FEED:
+                case CHAR_CARRIAGE_RETURN:
+                case CHAR_NEXT_LINE:
+                case CHAR_LINE_SEPARATOR:
+                case CHAR_PARAGRAPH_SEPARATOR:
+                    return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java
index 7170bd7..8cd4950 100644
--- a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java
+++ b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java
@@ -32,14 +32,18 @@
 public final class SuggestionResults extends TreeSet<SuggestedWordInfo> {
     public final Locale mLocale;
     public final ArrayList<SuggestedWordInfo> mRawSuggestions;
+    // TODO: Instead of a boolean , we may want to include the context of this suggestion results,
+    // such as {@link PrevWordsInfo}.
+    public final boolean mIsBeginningOfSentence;
     private final int mCapacity;
 
-    public SuggestionResults(final Locale locale, final int capacity) {
-        this(locale, sSuggestedWordInfoComparator, capacity);
+    public SuggestionResults(final Locale locale, final int capacity,
+            final boolean isBeginningOfSentence) {
+        this(locale, sSuggestedWordInfoComparator, capacity, isBeginningOfSentence);
     }
 
-    public SuggestionResults(final Locale locale, final Comparator<SuggestedWordInfo> comparator,
-            final int capacity) {
+    private SuggestionResults(final Locale locale, final Comparator<SuggestedWordInfo> comparator,
+            final int capacity, final boolean isBeginningOfSentence) {
         super(comparator);
         mLocale = locale;
         mCapacity = capacity;
@@ -48,6 +52,7 @@
         } else {
             mRawSuggestions = null;
         }
+        mIsBeginningOfSentence = isBeginningOfSentence;
     }
 
     @Override
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java
index 737d169..8b2bb42 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java
@@ -63,7 +63,8 @@
                 final String codeString = StringUtils.newSingleCodePointString(mCode);
                 // A letter may have an upper case counterpart that consists of multiple code
                 // points, for instance the upper case of "ß" is "SS".
-                return newInstance(codeString.toUpperCase(locale));
+                return newInstance(StringUtils.toUpperCaseOfStringForLocale(
+                        codeString, true /* needsToUpperCase */, locale));
             }
             // A special negative value has no upper case.
             return this;
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyVisual.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyVisual.java
index facdf70..34024a5 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyVisual.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyVisual.java
@@ -19,6 +19,7 @@
 import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
 import com.android.inputmethod.keyboard.internal.MoreKeySpec;
+import com.android.inputmethod.latin.utils.StringUtils;
 
 import java.util.Locale;
 
@@ -105,7 +106,8 @@
 
         @Override
         ExpectedKeyVisual toUpperCase(final Locale locale) {
-            return new Label(mLabel.toUpperCase(locale));
+            return new Label(StringUtils.toUpperCaseOfStringForLocale(
+                    mLabel, true /* needsToUpperCase */, locale));
         }
 
         @Override
diff --git a/tests/src/com/android/inputmethod/latin/utils/ImportantNoticeUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/ImportantNoticeUtilsTests.java
new file mode 100644
index 0000000..819d763
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/utils/ImportantNoticeUtilsTests.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.utils;
+
+import static com.android.inputmethod.latin.utils.ImportantNoticeUtils.KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE;
+import static com.android.inputmethod.latin.utils.ImportantNoticeUtils.KEY_IMPORTANT_NOTICE_VERSION;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
+
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+public class ImportantNoticeUtilsTests extends AndroidTestCase {
+    // This should be aligned with R.integer.config_important_notice_version.
+    private static final int CURRENT_IMPORTANT_NOTICE_VERSION = 1;
+
+    private ImportantNoticePreferences mImportantNoticePreferences;
+
+    private static class ImportantNoticePreferences {
+        private final SharedPreferences mPref;
+
+        private Integer mVersion;
+        private Long mLastTime;
+
+        public ImportantNoticePreferences(final Context context) {
+            mPref = ImportantNoticeUtils.getImportantNoticePreferences(context);
+        }
+
+        private Integer getInt(final String key) {
+            if (mPref.contains(key)) {
+                return mPref.getInt(key, 0);
+            }
+            return null;
+        }
+
+        public Long getLong(final String key) {
+            if (mPref.contains(key)) {
+                return mPref.getLong(key, 0);
+            }
+            return null;
+        }
+
+        private void putInt(final String key, final Integer value) {
+            if (value == null) {
+                removePreference(key);
+            } else {
+                mPref.edit().putInt(key, value).apply();
+            }
+        }
+
+        private void putLong(final String key, final Long value) {
+            if (value == null) {
+                removePreference(key);
+            } else {
+                mPref.edit().putLong(key, value).apply();
+            }
+        }
+
+        private void removePreference(final String key) {
+            mPref.edit().remove(key).apply();
+        }
+
+        public void save() {
+            mVersion = getInt(KEY_IMPORTANT_NOTICE_VERSION);
+            mLastTime = getLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE);
+        }
+
+        public void restore() {
+            putInt(KEY_IMPORTANT_NOTICE_VERSION, mVersion);
+            putLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE, mLastTime);
+        }
+
+        public void clear() {
+            removePreference(KEY_IMPORTANT_NOTICE_VERSION);
+            removePreference(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE);
+        }
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mImportantNoticePreferences = new ImportantNoticePreferences(getContext());
+        mImportantNoticePreferences.save();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mImportantNoticePreferences.restore();
+    }
+
+    public void testCurrentVersion() {
+        assertEquals("Current version", CURRENT_IMPORTANT_NOTICE_VERSION,
+                ImportantNoticeUtils.getCurrentImportantNoticeVersion(getContext()));
+    }
+
+    public void testUpdateVersion() {
+        mImportantNoticePreferences.clear();
+
+        assertEquals("Current boolean before update", true,
+                ImportantNoticeUtils.shouldShowImportantNotice(getContext()));
+        assertEquals("Last version before update", 0,
+                ImportantNoticeUtils.getLastImportantNoticeVersion(getContext()));
+        assertEquals("Next version before update ", 1,
+                ImportantNoticeUtils.getNextImportantNoticeVersion(getContext()));
+        assertEquals("Current title before update", false, TextUtils.isEmpty(
+                ImportantNoticeUtils.getNextImportantNoticeTitle(getContext())));
+        assertEquals("Current contents before update", false, TextUtils.isEmpty(
+                ImportantNoticeUtils.getNextImportantNoticeContents(getContext())));
+
+        ImportantNoticeUtils.updateLastImportantNoticeVersion(getContext());
+
+        assertEquals("Current boolean after update", false,
+                ImportantNoticeUtils.shouldShowImportantNotice(getContext()));
+        assertEquals("Last version after update", 1,
+                ImportantNoticeUtils.getLastImportantNoticeVersion(getContext()));
+        assertEquals("Next version after update", 2,
+                ImportantNoticeUtils.getNextImportantNoticeVersion(getContext()));
+        assertEquals("Current title after update", true, TextUtils.isEmpty(
+                ImportantNoticeUtils.getNextImportantNoticeTitle(getContext())));
+        assertEquals("Current contents after update", true, TextUtils.isEmpty(
+                ImportantNoticeUtils.getNextImportantNoticeContents(getContext())));
+    }
+
+    private static void sleep(final long millseconds) {
+        try { Thread.sleep(millseconds); } catch (final Exception e) { /* ignore */ }
+    }
+
+    public void testTimeout() {
+        final long lastTime = System.currentTimeMillis()
+                - ImportantNoticeUtils.TIMEOUT_OF_IMPORTANT_NOTICE
+                + TimeUnit.MILLISECONDS.toMillis(1000);
+        mImportantNoticePreferences.clear();
+        assertEquals("Before set last time", null,
+                mImportantNoticePreferences.getLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE));
+        assertEquals("Set last time", false,
+                ImportantNoticeUtils.hasTimeoutPassed(getContext(), lastTime));
+        assertEquals("After set last time", (Long)lastTime,
+                mImportantNoticePreferences.getLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE));
+
+        // Call {@link ImportantNoticeUtils#shouldShowImportantNotice(Context)} before timeout.
+        assertEquals("Current boolean before timeout 1", true,
+                ImportantNoticeUtils.shouldShowImportantNotice(getContext()));
+        assertEquals("Last version before timeout 1", 0,
+                ImportantNoticeUtils.getLastImportantNoticeVersion(getContext()));
+        assertEquals("Next version before timeout 1", 1,
+                ImportantNoticeUtils.getNextImportantNoticeVersion(getContext()));
+        assertEquals("Last time before timeout 1", (Long)lastTime,
+                mImportantNoticePreferences.getLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE));
+        assertEquals("Current title before timeout 1", false, TextUtils.isEmpty(
+                ImportantNoticeUtils.getNextImportantNoticeTitle(getContext())));
+        assertEquals("Current contents before timeout 1", false, TextUtils.isEmpty(
+                ImportantNoticeUtils.getNextImportantNoticeContents(getContext())));
+
+        sleep(TimeUnit.MILLISECONDS.toMillis(600));
+
+        // Call {@link ImportantNoticeUtils#shouldShowImportantNotice(Context)} before timeout
+        // again.
+        assertEquals("Current boolean before timeout 2", true,
+                ImportantNoticeUtils.shouldShowImportantNotice(getContext()));
+        assertEquals("Last version before timeout 2", 0,
+                ImportantNoticeUtils.getLastImportantNoticeVersion(getContext()));
+        assertEquals("Next version before timeout 2", 1,
+                ImportantNoticeUtils.getNextImportantNoticeVersion(getContext()));
+        assertEquals("Last time before timeout 2", (Long)lastTime,
+                mImportantNoticePreferences.getLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE));
+        assertEquals("Current title before timeout 2", false, TextUtils.isEmpty(
+                ImportantNoticeUtils.getNextImportantNoticeTitle(getContext())));
+        assertEquals("Current contents before timeout 2", false, TextUtils.isEmpty(
+                ImportantNoticeUtils.getNextImportantNoticeContents(getContext())));
+
+        sleep(TimeUnit.MILLISECONDS.toMillis(600));
+
+        // Call {@link ImportantNoticeUtils#shouldShowImportantNotice(Context)} after timeout.
+        assertEquals("Current boolean after timeout 1", false,
+                ImportantNoticeUtils.shouldShowImportantNotice(getContext()));
+        assertEquals("Last version after timeout 1", 1,
+                ImportantNoticeUtils.getLastImportantNoticeVersion(getContext()));
+        assertEquals("Next version after timeout 1", 2,
+                ImportantNoticeUtils.getNextImportantNoticeVersion(getContext()));
+        assertEquals("Last time aflter timeout 1", null,
+                mImportantNoticePreferences.getLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE));
+        assertEquals("Current title after timeout 1", true, TextUtils.isEmpty(
+                ImportantNoticeUtils.getNextImportantNoticeTitle(getContext())));
+        assertEquals("Current contents after timeout 1", true, TextUtils.isEmpty(
+                ImportantNoticeUtils.getNextImportantNoticeContents(getContext())));
+
+        sleep(TimeUnit.MILLISECONDS.toMillis(600));
+
+        // Call {@link ImportantNoticeUtils#shouldShowImportantNotice(Context)} after timeout again.
+        assertEquals("Current boolean after timeout 2", false,
+                ImportantNoticeUtils.shouldShowImportantNotice(getContext()));
+        assertEquals("Last version after timeout 2", 1,
+                ImportantNoticeUtils.getLastImportantNoticeVersion(getContext()));
+        assertEquals("Next version after timeout 2", 2,
+                ImportantNoticeUtils.getNextImportantNoticeVersion(getContext()));
+        assertEquals("Last time aflter timeout 2", null,
+                mImportantNoticePreferences.getLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE));
+        assertEquals("Current title after timeout 2", true, TextUtils.isEmpty(
+                ImportantNoticeUtils.getNextImportantNoticeTitle(getContext())));
+        assertEquals("Current contents after timeout 2", true, TextUtils.isEmpty(
+                ImportantNoticeUtils.getNextImportantNoticeContents(getContext())));
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java
new file mode 100644
index 0000000..ba2e998
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.utils;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.utils.StringUtils;
+
+import java.util.Locale;
+
+@SmallTest
+public class StringUtilsTests extends AndroidTestCase {
+    private static final Locale US = Locale.US;
+    private static final Locale GERMAN = Locale.GERMAN;
+    private static final Locale TURKEY = new Locale("tr", "TR");
+    private static final Locale GREECE = new Locale("el", "GR");
+
+    private static void assert_toUpperCaseOfStringForLocale(final Locale locale,
+            final String lowerCase, final String expected) {
+        assertEquals(lowerCase + " in " + locale, expected,
+                StringUtils.toUpperCaseOfStringForLocale(
+                        lowerCase, true /* needsToUpperCase */, locale));
+    }
+
+    public void test_toUpperCaseOfStringForLocale() {
+        assert_toUpperCaseOfStringForLocale(US, null, null);
+        assert_toUpperCaseOfStringForLocale(US, "", "");
+        assert_toUpperCaseOfStringForLocale(US, "aeiou", "AEIOU");
+        // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+        // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+        // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+        // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+        // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+        // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+        // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+        // U+00C0: "À" LATIN CAPITAL LETTER A WITH GRAVE
+        // U+00C8: "È" LATIN CAPITAL LETTER E WITH GRAVE
+        // U+00CE: "Î" LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+        // U+00D6: "Ö" LATIN CAPITAL LETTER O WITH DIAERESIS
+        // U+016A: "Ū" LATIN CAPITAL LETTER U WITH MACRON
+        // U+00D1: "Ñ" LATIN CAPITAL LETTER N WITH TILDE
+        // U+00C7: "Ç" LATIN CAPITAL LETTER C WITH CEDILLA
+        assert_toUpperCaseOfStringForLocale(US,
+                "\u00E0\u00E8\u00EE\u00F6\u016B\u00F1\u00E7",
+                "\u00C0\u00C8\u00CE\u00D6\u016A\u00D1\u00C7");
+        // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+        // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+        // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+        // U+015A: "Ś" LATIN CAPITAL LETTER S WITH ACUTE
+        // U+0160: "Š" LATIN CAPITAL LETTER S WITH CARONZ
+        assert_toUpperCaseOfStringForLocale(GERMAN,
+                "\u00DF\u015B\u0161",
+                "SS\u015A\u0160");
+        // U+0259: "ə" LATIN SMALL LETTER SCHWA
+        // U+0069: "i" LATIN SMALL LETTER I
+        // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
+        // U+018F: "Ə" LATIN SMALL LETTER SCHWA
+        // U+0130: "İ" LATIN SMALL LETTER I WITH DOT ABOVE
+        // U+0049: "I" LATIN SMALL LETTER I
+        assert_toUpperCaseOfStringForLocale(TURKEY,
+                "\u0259\u0069\u0131",
+                "\u018F\u0130\u0049");
+        // U+03C3: "σ" GREEK SMALL LETTER SIGMA
+        // U+03C2: "ς" GREEK SMALL LETTER FINAL SIGMA
+        // U+03A3: "Σ" GREEK CAPITAL LETTER SIGMA
+        assert_toUpperCaseOfStringForLocale(GREECE,
+                "\u03C3\u03C2",
+                "\u03A3\u03A3");
+        // U+03AC: "ά" GREEK SMALL LETTER ALPHA WITH TONOS
+        // U+03AD: "έ" GREEK SMALL LETTER EPSILON WITH TONOS
+        // U+03AE: "ή" GREEK SMALL LETTER ETA WITH TONOS
+        // U+03AF: "ί" GREEK SMALL LETTER IOTA WITH TONOS
+        // U+03CC: "ό" GREEK SMALL LETTER OMICRON WITH TONOS
+        // U+03CD: "ύ" GREEK SMALL LETTER UPSILON WITH TONOS
+        // U+03CE: "ώ" GREEK SMALL LETTER OMEGA WITH TONOS
+        // U+0386: "Ά" GREEK CAPITAL LETTER ALPHA WITH TONOS
+        // U+0388: "Έ" GREEK CAPITAL LETTER EPSILON WITH TONOS
+        // U+0389: "Ή" GREEK CAPITAL LETTER ETA WITH TONOS
+        // U+038A: "Ί" GREEK CAPITAL LETTER IOTA WITH TONOS
+        // U+038C: "Ό" GREEK CAPITAL LETTER OMICRON WITH TONOS
+        // U+038E: "Ύ" GREEK CAPITAL LETTER UPSILON WITH TONOS
+        // U+038F: "Ώ" GREEK CAPITAL LETTER OMEGA WITH TONOS
+        assert_toUpperCaseOfStringForLocale(GREECE,
+                "\u03AC\u03AD\u03AE\u03AF\u03CC\u03CD\u03CE",
+                "\u0386\u0388\u0389\u038A\u038C\u038E\u038F");
+        // U+03CA: "ϊ" GREEK SMALL LETTER IOTA WITH DIALYTIKA
+        // U+03CB: "ϋ" GREEK SMALL LETTER UPSILON WITH DIALYTIKA
+        // U+0390: "ΐ" GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+        // U+03B0: "ΰ" GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+        // U+03AA: "Ϊ" GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+        // U+03AB: "Ϋ" GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+        // U+0399: "Ι" GREEK CAPITAL LETTER IOTA
+        // U+03A5: "Υ" GREEK CAPITAL LETTER UPSILON
+        // U+0308: COMBINING DIAERESIS
+        // U+0301: COMBINING GRAVE ACCENT
+        assert_toUpperCaseOfStringForLocale(GREECE,
+                "\u03CA\u03CB\u0390\u03B0",
+                "\u03AA\u03AB\u0399\u0308\u0301\u03A5\u0308\u0301");
+    }
+
+    private static void assert_toUpperCaseOfCodeForLocale(final Locale locale, final int lowerCase,
+            final int expected) {
+        assertEquals(lowerCase + " in " + locale, expected,
+                StringUtils.toUpperCaseOfCodeForLocale(
+                        lowerCase, true /* needsToUpperCase */, locale));
+    }
+
+    public void test_toUpperCaseOfCodeForLocale() {
+        assert_toUpperCaseOfCodeForLocale(US, Constants.CODE_ENTER, Constants.CODE_ENTER);
+        assert_toUpperCaseOfCodeForLocale(US, Constants.CODE_SPACE, Constants.CODE_SPACE);
+        assert_toUpperCaseOfCodeForLocale(US, Constants.CODE_COMMA, Constants.CODE_COMMA);
+        // U+0069: "i" LATIN SMALL LETTER I
+        // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
+        // U+0130: "İ" LATIN SMALL LETTER I WITH DOT ABOVE
+        // U+0049: "I" LATIN SMALL LETTER I
+        assert_toUpperCaseOfCodeForLocale(US, 0x0069, 0x0049); // i -> I
+        assert_toUpperCaseOfCodeForLocale(US, 0x0131, 0x0049); // ı -> I
+        assert_toUpperCaseOfCodeForLocale(TURKEY, 0x0069, 0x0130); // i -> İ
+        assert_toUpperCaseOfCodeForLocale(TURKEY, 0x0131, 0x0049); // ı -> I
+        // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+        // The title case of "ß" is "SS".
+        assert_toUpperCaseOfCodeForLocale(US, 0x00DF, Constants.CODE_UNSPECIFIED);
+        // U+03AC: "ά" GREEK SMALL LETTER ALPHA WITH TONOS
+        // U+0386: "Ά" GREEK CAPITAL LETTER ALPHA WITH TONOS
+        assert_toUpperCaseOfCodeForLocale(GREECE, 0x03AC, 0x0386);
+        // U+03CA: "ϊ" GREEK SMALL LETTER IOTA WITH DIALYTIKA
+        // U+03AA: "Ϊ" GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+        assert_toUpperCaseOfCodeForLocale(GREECE, 0x03CA, 0x03AA);
+        // U+03B0: "ΰ" GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+        // The title case of "ΰ" is "\u03A5\u0308\u0301".
+        assert_toUpperCaseOfCodeForLocale(GREECE, 0x03B0, Constants.CODE_UNSPECIFIED);
+    }
+
+    private static void assert_capitalizeFirstCodePoint(final Locale locale, final String text,
+            final String expected) {
+        assertEquals(text + " in " + locale, expected,
+                StringUtils.capitalizeFirstCodePoint(text, locale));
+    }
+
+    public void test_capitalizeFirstCodePoint() {
+        assert_capitalizeFirstCodePoint(US, "", "");
+        assert_capitalizeFirstCodePoint(US, "a", "A");
+        assert_capitalizeFirstCodePoint(US, "à", "À");
+        assert_capitalizeFirstCodePoint(US, "ß", "SS");
+        assert_capitalizeFirstCodePoint(US, "text", "Text");
+        assert_capitalizeFirstCodePoint(US, "iGoogle", "IGoogle");
+        assert_capitalizeFirstCodePoint(TURKEY, "iyi", "İyi");
+        assert_capitalizeFirstCodePoint(TURKEY, "ısırdı", "Isırdı");
+        assert_capitalizeFirstCodePoint(GREECE, "ά", "Ά");
+        assert_capitalizeFirstCodePoint(GREECE, "άνεση", "Άνεση");
+    }
+
+    private static void assert_capitalizeFirstAndDowncaseRest(final Locale locale,
+            final String text, final String expected) {
+        assertEquals(text + " in " + locale, expected,
+                StringUtils.capitalizeFirstAndDowncaseRest(text, locale));
+    }
+
+    public void test_capitalizeFirstAndDowncaseRest() {
+        assert_capitalizeFirstAndDowncaseRest(US, "", "");
+        assert_capitalizeFirstAndDowncaseRest(US, "a", "A");
+        assert_capitalizeFirstAndDowncaseRest(US, "à", "À");
+        assert_capitalizeFirstAndDowncaseRest(US, "ß", "SS");
+        assert_capitalizeFirstAndDowncaseRest(US, "text", "Text");
+        assert_capitalizeFirstAndDowncaseRest(US, "iGoogle", "Igoogle");
+        assert_capitalizeFirstAndDowncaseRest(US, "invite", "Invite");
+        assert_capitalizeFirstAndDowncaseRest(US, "INVITE", "Invite");
+        assert_capitalizeFirstAndDowncaseRest(TURKEY, "iyi", "İyi");
+        assert_capitalizeFirstAndDowncaseRest(TURKEY, "İYİ", "İyi");
+        assert_capitalizeFirstAndDowncaseRest(TURKEY, "ısırdı", "Isırdı");
+        assert_capitalizeFirstAndDowncaseRest(TURKEY, "ISIRDI", "Isırdı");
+        assert_capitalizeFirstAndDowncaseRest(GREECE, "ά", "Ά");
+        assert_capitalizeFirstAndDowncaseRest(GREECE, "άνεση", "Άνεση");
+        assert_capitalizeFirstAndDowncaseRest(GREECE, "ΆΝΕΣΗ", "Άνεση");
+    }
+}