Merge "Quit use bigram probability diff for ver4 dict."
diff --git a/java/res/drawable-hdpi/sym_keyboard_delete_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_delete_lxx_dark.png
new file mode 100644
index 0000000..5f5eb3f
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_delete_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_done_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_done_lxx_dark.png
new file mode 100644
index 0000000..f81130d
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_done_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_go_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_go_lxx_dark.png
new file mode 100644
index 0000000..516a7f1
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_go_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_language_switch_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_language_switch_lxx_dark.png
new file mode 100644
index 0000000..edf9a20
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_language_switch_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_next_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_next_lxx_dark.png
new file mode 100644
index 0000000..99ac309
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_next_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_previous_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_previous_lxx_dark.png
new file mode 100644
index 0000000..9019ebd
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_previous_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_search_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_search_lxx_dark.png
new file mode 100644
index 0000000..de117d3
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_search_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_send_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_send_lxx_dark.png
new file mode 100644
index 0000000..6db6d56
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_send_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_settings_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_settings_lxx_dark.png
new file mode 100644
index 0000000..f261302
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_settings_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_smiley_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_smiley_lxx_dark.png
new file mode 100644
index 0000000..3999d1e
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_smiley_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_voice_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_voice_lxx_dark.png
new file mode 100644
index 0000000..6b68c8a
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_voice_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_voice_off_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_voice_off_lxx_dark.png
new file mode 100644
index 0000000..e67697b
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_voice_off_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_delete_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_delete_lxx_dark.png
new file mode 100644
index 0000000..2d3ac97
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_delete_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_done_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_done_lxx_dark.png
new file mode 100644
index 0000000..8a63c11
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_done_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_go_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_go_lxx_dark.png
new file mode 100644
index 0000000..8905172
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_go_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_language_switch_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_language_switch_lxx_dark.png
new file mode 100644
index 0000000..a90bf75
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_language_switch_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_next_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_next_lxx_dark.png
new file mode 100644
index 0000000..61c1014
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_next_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_previous_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_previous_lxx_dark.png
new file mode 100644
index 0000000..f176956
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_previous_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_search_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_search_lxx_dark.png
new file mode 100644
index 0000000..722d402
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_search_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_send_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_send_lxx_dark.png
new file mode 100644
index 0000000..a75a608
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_send_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_settings_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_settings_lxx_dark.png
new file mode 100644
index 0000000..dea7add
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_settings_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_smiley_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_smiley_lxx_dark.png
new file mode 100644
index 0000000..8276d99
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_smiley_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_voice_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_voice_lxx_dark.png
new file mode 100644
index 0000000..5661f6b
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_voice_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_voice_off_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_voice_off_lxx_dark.png
new file mode 100644
index 0000000..5182f1e
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_voice_off_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_delete_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_delete_lxx_dark.png
new file mode 100644
index 0000000..7c9f34f
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_delete_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_done_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_done_lxx_dark.png
new file mode 100644
index 0000000..f25e3df
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_done_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_go_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_go_lxx_dark.png
new file mode 100644
index 0000000..6cd43cf
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_go_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_language_switch_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_language_switch_lxx_dark.png
new file mode 100644
index 0000000..f964346
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_language_switch_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_next_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_next_lxx_dark.png
new file mode 100644
index 0000000..9e05e84
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_next_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_previous_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_previous_lxx_dark.png
new file mode 100644
index 0000000..8995d87
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_previous_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_search_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_search_lxx_dark.png
new file mode 100644
index 0000000..28a4bd3
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_search_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_send_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_send_lxx_dark.png
new file mode 100644
index 0000000..9927af4
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_send_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_settings_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_settings_lxx_dark.png
new file mode 100644
index 0000000..8c83d9f
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_settings_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_smiley_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_smiley_lxx_dark.png
new file mode 100644
index 0000000..78923fa
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_smiley_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_voice_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_voice_lxx_dark.png
new file mode 100644
index 0000000..4e9631e
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_voice_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_voice_off_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_voice_off_lxx_dark.png
new file mode 100644
index 0000000..66c0e3c
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_voice_off_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_delete_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_delete_lxx_dark.png
new file mode 100644
index 0000000..dd95be6
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_delete_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_done_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_done_lxx_dark.png
new file mode 100644
index 0000000..ccd270e
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_done_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_go_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_go_lxx_dark.png
new file mode 100644
index 0000000..0617c15
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_go_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_language_switch_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_language_switch_lxx_dark.png
new file mode 100644
index 0000000..fc85e51
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_language_switch_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_next_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_next_lxx_dark.png
new file mode 100644
index 0000000..a04a41c
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_next_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_previous_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_previous_lxx_dark.png
new file mode 100644
index 0000000..c87d8f0
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_previous_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_search_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_search_lxx_dark.png
new file mode 100644
index 0000000..a32b6c5
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_search_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_send_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_send_lxx_dark.png
new file mode 100644
index 0000000..8073e2c
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_send_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_settings_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_settings_lxx_dark.png
new file mode 100644
index 0000000..d513ad9
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_settings_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_smiley_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_smiley_lxx_dark.png
new file mode 100644
index 0000000..a4df3bb
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_smiley_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_voice_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_voice_lxx_dark.png
new file mode 100644
index 0000000..a36724c
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_voice_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_voice_off_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_voice_off_lxx_dark.png
new file mode 100644
index 0000000..2706dca
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_voice_off_lxx_dark.png
Binary files differ
diff --git a/java/res/values/keyboard-icons-lxx.xml b/java/res/values/keyboard-icons-lxx.xml
index ea4b6e6..8a38863 100644
--- a/java/res/values/keyboard-icons-lxx.xml
+++ b/java/res/values/keyboard-icons-lxx.xml
@@ -21,27 +21,27 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
     <style name="KeyboardIcons.LXX">
         <!-- Keyboard icons -->
+        <!-- TODO: Update those icons for LXX theme. -->
         <item name="iconShiftKey">@drawable/sym_keyboard_shift_holo_dark</item>
-        <item name="iconDeleteKey">@drawable/sym_keyboard_delete_holo_dark</item>
-        <item name="iconSettingsKey">@drawable/sym_keyboard_settings_holo_dark</item>
+        <item name="iconDeleteKey">@drawable/sym_keyboard_delete_lxx_dark</item>
+        <item name="iconSettingsKey">@drawable/sym_keyboard_settings_lxx_dark</item>
         <item name="iconSpaceKey">@drawable/sym_keyboard_space_holo_dark</item>
         <item name="iconEnterKey">@drawable/sym_keyboard_return_holo_dark</item>
-        <!-- TODO: Uncomment those icon definitions once we have those icon assets. -->
-        <!-- <item name="iconGoKey">@drawable/sym_keyboard_go_holo_dark</item> -->
-        <item name="iconSearchKey">@drawable/sym_keyboard_search_holo_dark</item>
-        <!-- <item name="iconSendKey">@drawable/sym_keyboard_send_holo_dark</item> -->
-        <!-- <item name="iconNextKey">@drawable/sym_keyboard_next_holo_dark</item> -->
-        <!-- <item name="iconDoneKey">@drawable/sym_keyboard_done_holo_dark</item> -->
-        <!-- <item name="iconPreviousKey">@drawable/sym_keyboard_previous_holo_dark</item> -->
+        <item name="iconGoKey">@drawable/sym_keyboard_go_lxx_dark</item>
+        <item name="iconSearchKey">@drawable/sym_keyboard_search_lxx_dark</item>
+        <item name="iconSendKey">@drawable/sym_keyboard_send_lxx_dark</item>
+        <item name="iconNextKey">@drawable/sym_keyboard_next_lxx_dark</item>
+        <item name="iconDoneKey">@drawable/sym_keyboard_done_lxx_dark</item>
+        <item name="iconPreviousKey">@drawable/sym_keyboard_previous_lxx_dark</item>
         <item name="iconTabKey">@drawable/sym_keyboard_tab_holo_dark</item>
-        <item name="iconShortcutKey">@drawable/sym_keyboard_voice_holo_dark</item>
+        <item name="iconShortcutKey">@drawable/sym_keyboard_voice_lxx_dark</item>
         <item name="iconSpaceKeyForNumberLayout">@drawable/sym_keyboard_space_holo_dark</item>
         <item name="iconShiftKeyShifted">@drawable/sym_keyboard_shift_locked_holo_dark</item>
-        <item name="iconShortcutKeyDisabled">@drawable/sym_keyboard_voice_off_holo_dark</item>
+        <item name="iconShortcutKeyDisabled">@drawable/sym_keyboard_voice_off_lxx_dark</item>
         <item name="iconTabKeyPreview">@drawable/sym_keyboard_feedback_tab</item>
-        <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch_dark</item>
+        <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch_lxx_dark</item>
         <item name="iconZwnjKey">@drawable/sym_keyboard_zwnj_holo_dark</item>
         <item name="iconZwjKey">@drawable/sym_keyboard_zwj_holo_dark</item>
-        <item name="iconEmojiKey">@drawable/sym_keyboard_smiley_holo_dark</item>
+        <item name="iconEmojiKey">@drawable/sym_keyboard_smiley_lxx_dark</item>
     </style>
 </resources>
diff --git a/java/src/com/android/inputmethod/event/CombinerChain.java b/java/src/com/android/inputmethod/event/CombinerChain.java
index 990f7de..9e7f04d 100644
--- a/java/src/com/android/inputmethod/event/CombinerChain.java
+++ b/java/src/com/android/inputmethod/event/CombinerChain.java
@@ -56,18 +56,20 @@
      *
      * The combiner chain takes events as inputs and outputs code points and combining state.
      * For example, if the input language is Japanese, the combining chain will typically perform
-     * kana conversion.
+     * kana conversion. This takes a string for initial text, taken to be present before the
+     * cursor: we'll start after this.
      *
+     * @param initialText The text that has already been combined so far.
      * @param combinerList A list of combiners to be applied in order.
      */
-    public CombinerChain(final Combiner... combinerList) {
+    public CombinerChain(final String initialText, final Combiner... combinerList) {
         mCombiners = CollectionUtils.newArrayList();
         // The dead key combiner is always active, and always first
         mCombiners.add(new DeadKeyCombiner());
         for (final Combiner combiner : combinerList) {
             mCombiners.add(combiner);
         }
-        mCombinedText = new StringBuilder();
+        mCombinedText = new StringBuilder(initialText);
         mStateFeedback = new SpannableStringBuilder();
     }
 
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 8a2ed10..1ccf585 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -84,6 +84,7 @@
 import com.android.inputmethod.latin.utils.CoordinateUtils;
 import com.android.inputmethod.latin.utils.DialogUtils;
 import com.android.inputmethod.latin.utils.DistracterFilter;
+import com.android.inputmethod.latin.utils.DistracterFilterUtils;
 import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
 import com.android.inputmethod.latin.utils.IntentUtils;
 import com.android.inputmethod.latin.utils.JniUtils;
@@ -480,6 +481,7 @@
         KeyboardSwitcher.init(this);
         AudioAndHapticFeedbackManager.init(this);
         AccessibilityUtils.init(this);
+        StatsUtils.init(this);
 
         super.onCreate();
 
@@ -519,7 +521,7 @@
 
         DictionaryDecayBroadcastReciever.setUpIntervalAlarmForDictionaryDecaying(this);
 
-        StatsUtils.onCreateCompleted(this);
+        StatsUtils.onCreate(mSettings.getCurrent());
     }
 
     // Has to be package-visible for unit tests
@@ -538,6 +540,7 @@
             resetSuggestForLocale(locale);
         }
         refreshPersonalizationDictionarySession();
+        StatsUtils.onLoadSettings(currentSettingsValues);
     }
 
     private void refreshPersonalizationDictionarySession() {
@@ -1745,7 +1748,8 @@
 
     @UsedForTesting
     /* package for test */ DistracterFilter createDistracterFilter() {
-        return DistracterFilter.createDistracterFilter(mInputLogic.mSuggest, mKeyboardSwitcher);
+        return DistracterFilterUtils.createDistracterFilter(
+                mInputLogic.mSuggest, mKeyboardSwitcher);
     }
 
     public void dumpDictionaryForDebug(final String dictName) {
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index cdee496..ac69729 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -80,7 +80,7 @@
     private boolean mIsFirstCharCapitalized;
 
     public WordComposer() {
-        mCombinerChain = new CombinerChain();
+        mCombinerChain = new CombinerChain("");
         mEvents = CollectionUtils.newArrayList();
         mAutoCorrection = null;
         mIsResumed = false;
@@ -92,18 +92,17 @@
     }
 
     /**
-     * Restart input with a new combining spec.
+     * Restart the combiners, possibly with a new spec.
      * @param combiningSpec The spec string for combining. This is found in the extra value.
      */
-    public void restart(final String combiningSpec) {
+    public void restartCombining(final String combiningSpec) {
         final String nonNullCombiningSpec = null == combiningSpec ? "" : combiningSpec;
-        if (nonNullCombiningSpec.equals(mCombiningSpec)) {
-            mCombinerChain.reset();
-        } else {
-            mCombinerChain = new CombinerChain(CombinerChain.createCombiners(nonNullCombiningSpec));
+        if (!nonNullCombiningSpec.equals(mCombiningSpec)) {
+            mCombinerChain = new CombinerChain(
+                    mCombinerChain.getComposingWordWithCombiningFeedback().toString(),
+                    CombinerChain.createCombiners(nonNullCombiningSpec));
             mCombiningSpec = nonNullCombiningSpec;
         }
-        reset();
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 1156c77..ea58abc 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -127,7 +127,7 @@
     public void startInput(final boolean restarting, final EditorInfo editorInfo,
             final String combiningSpec) {
         mEnteredText = null;
-        mWordComposer.restart(combiningSpec);
+        mWordComposer.restartCombining(combiningSpec);
         resetComposingState(true /* alsoResetLastComposedWord */);
         mDeleteCount = 0;
         mSpaceState = SpaceState.NONE;
@@ -150,7 +150,7 @@
      * @param combiningSpec the spec string for the combining rules
      */
     public void onSubtypeChanged(final String combiningSpec) {
-        mWordComposer.restart(combiningSpec);
+        mWordComposer.restartCombining(combiningSpec);
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index 4e4c888..d4f6bcd 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -87,6 +87,8 @@
     public static final String PREF_DEBUG_SETTINGS = "debug_settings";
     public static final String PREF_KEY_IS_INTERNAL = "pref_key_is_internal";
 
+    public static final String PREF_ENABLE_METRICS_LOGGING = "pref_enable_metrics_logging";
+
     // This preference key is deprecated. Use {@link #PREF_SHOW_LANGUAGE_SWITCH_KEY} instead.
     // This is being used only for the backward compatibility.
     private static final String PREF_SUPPRESS_LANGUAGE_SWITCH_KEY =
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index de2eb95..16fd058 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -73,6 +73,7 @@
     public final boolean mPhraseGestureEnabled;
     public final int mKeyLongpressTimeout;
     public final Locale mLocale;
+    public final boolean mEnableMetricsLogging;
 
     // From the input box
     public final InputAttributes mInputAttributes;
@@ -134,7 +135,7 @@
         mAutoCorrectEnabled = Settings.readAutoCorrectEnabled(autoCorrectionThresholdRawValue, res);
         mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res);
         mDoubleSpacePeriodTimeout = res.getInteger(R.integer.config_double_space_period_timeout);
-
+        mEnableMetricsLogging = prefs.getBoolean(Settings.PREF_ENABLE_METRICS_LOGGING, true);
         // Compute other readable settings
         mKeyLongpressTimeout = Settings.readKeyLongpressTimeout(prefs, res);
         mKeypressVibrationDuration = Settings.readKeypressVibrationDuration(prefs, res);
diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java
index 55cbf79..0a03799 100644
--- a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java
+++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java
@@ -17,8 +17,6 @@
 package com.android.inputmethod.latin.utils;
 
 import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardSwitcher;
-import com.android.inputmethod.keyboard.MainKeyboardView;
 import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.Suggest;
 import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback;
@@ -54,18 +52,6 @@
         mKeyboard = keyboard;
     }
 
-    public static DistracterFilter createDistracterFilter(final Suggest suggest,
-            final KeyboardSwitcher keyboardSwitcher) {
-        final MainKeyboardView mainKeyboardView = keyboardSwitcher.getMainKeyboardView();
-        // TODO: Create Keyboard when mainKeyboardView is null.
-        // TODO: Figure out the most reasonable keyboard for the filter. Refer to the
-        // spellchecker's logic.
-        final Keyboard keyboard = (mainKeyboardView != null) ?
-                mainKeyboardView.getKeyboard() : null;
-        final DistracterFilter distracterFilter = new DistracterFilter(suggest, keyboard);
-        return distracterFilter;
-    }
-
     private static boolean suggestionExceedsDistracterThreshold(
             final SuggestedWordInfo suggestion, final String consideredWord,
             final float distracterThreshold) {
@@ -90,19 +76,14 @@
      */
     public boolean isDistracterToWordsInDictionaries(final String prevWord,
             final String testedWord) {
-        if (mSuggest == null) {
+        if (mSuggest == null || mKeyboard == null) {
             return false;
         }
 
         final WordComposer composer = new WordComposer();
         final int[] codePoints = StringUtils.toCodePointArray(testedWord);
         final int[] coordinates;
-        if (null == mKeyboard) {
-            coordinates = CoordinateUtils.newCoordinateArray(codePoints.length,
-                    Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
-        } else {
-            coordinates = mKeyboard.getCoordinates(codePoints);
-        }
+        coordinates = mKeyboard.getCoordinates(codePoints);
         composer.setComposingWord(codePoints, coordinates, prevWord);
 
         final int trailingSingleQuotesCount = composer.trailingSingleQuotesCount();
diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterUtils.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterUtils.java
new file mode 100644
index 0000000..df07f97
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterUtils.java
@@ -0,0 +1,40 @@
+/*
+ * 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 com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
+import com.android.inputmethod.keyboard.MainKeyboardView;
+import com.android.inputmethod.latin.Suggest;
+
+public class DistracterFilterUtils {
+    private DistracterFilterUtils() {
+        // This utility class is not publicly instantiable.
+    }
+
+    public static final DistracterFilter createDistracterFilter(final Suggest suggest,
+            final KeyboardSwitcher keyboardSwitcher) {
+        final MainKeyboardView mainKeyboardView = keyboardSwitcher.getMainKeyboardView();
+        // TODO: Create Keyboard when mainKeyboardView is null.
+        // TODO: Figure out the most reasonable keyboard for the filter. Refer to the
+        // spellchecker's logic.
+        final Keyboard keyboard = (mainKeyboardView != null) ?
+                mainKeyboardView.getKeyboard() : null;
+        final DistracterFilter distracterFilter = new DistracterFilter(suggest, keyboard);
+        return distracterFilter;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/utils/StatsUtils.java b/java/src/com/android/inputmethod/latin/utils/StatsUtils.java
index a059f87..79c19d0 100644
--- a/java/src/com/android/inputmethod/latin/utils/StatsUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StatsUtils.java
@@ -17,37 +17,18 @@
 package com.android.inputmethod.latin.utils;
 
 import android.content.Context;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-import android.util.Log;
-
-import com.android.inputmethod.latin.settings.Settings;
+import com.android.inputmethod.latin.settings.SettingsValues;
 
 public final class StatsUtils {
-    private static final String TAG = StatsUtils.class.getSimpleName();
-    private static final StatsUtils sInstance = new StatsUtils();
-
-    public static void onCreateCompleted(final Context context) {
-        sInstance.onCreateCompletedInternal(context);
+    public static void init(final Context context) {
     }
 
-    private void onCreateCompletedInternal(final Context context) {
-        mContext = context;
-        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
-        final Boolean usePersonalizedDict =
-                prefs.getBoolean(Settings.PREF_KEY_USE_PERSONALIZED_DICTS, true);
-        Log.d(TAG, "onCreateCompleted. context: " + context.toString() + "usePersonalizedDict: "
-                + usePersonalizedDict);
+    public static void onCreate(final SettingsValues settingsValues) {
+    }
+
+    public static void onLoadSettings(final SettingsValues settingsValues) {
     }
 
     public static void onDestroy() {
-        sInstance.onDestroyInternal();
     }
-
-    private void onDestroyInternal() {
-        Log.d(TAG, "onDestroy. context: " + mContext.toString());
-        mContext = null;
-    }
-
-    private Context mContext;
 }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.cpp
index 8f42df6..028e9ec 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.cpp
@@ -29,10 +29,10 @@
     // PtNode is useless when the PtNode is not a terminal and doesn't have any not useless
     // children.
     bool isUselessPtNode = !ptNodeParams->isTerminal();
-    if (ptNodeParams->isTerminal()) {
+    if (ptNodeParams->isTerminal() && !ptNodeParams->representsNonWordInfo()) {
         bool needsToKeepPtNode = true;
-        if (!mPtNodeWriter->updatePtNodeProbabilityAndGetNeedsToKeepPtNodeAfterGC(ptNodeParams,
-                &needsToKeepPtNode)) {
+        if (!mPtNodeWriter->updatePtNodeProbabilityAndGetNeedsToKeepPtNodeAfterGC(
+                ptNodeParams, &needsToKeepPtNode)) {
             AKLOGE("Cannot update PtNode probability or get needs to keep PtNode after GC.");
             return false;
         }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h
index bef401f..5704c2e 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h
@@ -160,7 +160,8 @@
     }
 
     AK_FORCE_INLINE bool representsNonWordInfo() const {
-        return getCodePointCount() > 0 && CharUtils::isInUnicodeSpace(getCodePoints()[0]);
+        return getCodePointCount() > 0 && CharUtils::isInUnicodeSpace(getCodePoints()[0])
+                && isNotAWord();
     }
 
     // Parent node position
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp
index f31c502..e868ddf 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp
@@ -213,13 +213,16 @@
     // Delete unigrams.
     while (static_cast<int>(priorityQueue.size()) > maxUnigramCount) {
         const int ptNodePos = priorityQueue.top().getDictPos();
+        priorityQueue.pop();
         const PtNodeParams ptNodeParams =
                 ptNodeReader->fetchNodeInfoInBufferFromPtNodePos(ptNodePos);
+        if (ptNodeParams.representsNonWordInfo()) {
+            continue;
+        }
         if (!ptNodeWriter->markPtNodeAsWillBecomeNonTerminal(&ptNodeParams)) {
             AKLOGE("Cannot mark PtNode as willBecomeNonterminal. PtNode pos: %d", ptNodePos);
             return false;
         }
-        priorityQueue.pop();
     }
     return true;
 }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp
index d3e0c23..4a126ff 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp
@@ -33,7 +33,7 @@
     const int mmapFd = open(path, O_RDONLY);
     if (mmapFd < 0) {
         AKLOGE("DICT: Can't open the source. path=%s errno=%d", path, errno);
-        return MmappedBufferPtr(nullptr);
+        return nullptr;
     }
     const int pagesize = sysconf(_SC_PAGESIZE);
     const int offset = bufferOffset % pagesize;
@@ -45,13 +45,13 @@
     if (mmappedBuffer == MAP_FAILED) {
         AKLOGE("DICT: Can't mmap dictionary. errno=%d", errno);
         close(mmapFd);
-        return MmappedBufferPtr(nullptr);
+        return nullptr;
     }
     uint8_t *const buffer = static_cast<uint8_t *>(mmappedBuffer) + offset;
     if (!buffer) {
         AKLOGE("DICT: buffer is null");
         close(mmapFd);
-        return MmappedBufferPtr(nullptr);
+        return nullptr;
     }
     return MmappedBufferPtr(new MmappedBuffer(buffer, bufferSize, mmappedBuffer, alignedSize,
             mmapFd, isUpdatable));
@@ -61,7 +61,7 @@
         const char *const path, const bool isUpdatable) {
     const int fileSize = FileUtils::getFileSize(path);
     if (fileSize == -1) {
-        return MmappedBufferPtr(nullptr);
+        return nullptr;
     } else if (fileSize == 0) {
         return MmappedBufferPtr(new MmappedBuffer(isUpdatable));
     } else {
@@ -76,7 +76,7 @@
     const int filePathLength = snprintf(filePath, filePathBufferSize, "%s%s", dirPath,
             fileName);
     if (filePathLength >= filePathBufferSize) {
-        return MmappedBufferPtr(nullptr);
+        return nullptr;
     }
     return openBuffer(filePath, isUpdatable);
 }
diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
index fa9600c..3fc566e 100644
--- a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
+++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
@@ -37,6 +37,7 @@
 const float ScoringParams::PROXIMITY_COST = 0.0694f;
 const float ScoringParams::FIRST_CHAR_PROXIMITY_COST = 0.072f;
 const float ScoringParams::FIRST_PROXIMITY_COST = 0.07788f;
+const float ScoringParams::INTENTIONAL_OMISSION_COST = 0.1f;
 const float ScoringParams::OMISSION_COST = 0.467f;
 const float ScoringParams::OMISSION_COST_SAME_CHAR = 0.345f;
 const float ScoringParams::OMISSION_COST_FIRST_CHAR = 0.5256f;
diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.h b/native/jni/src/suggest/policyimpl/typing/scoring_params.h
index b669620..b12de6d 100644
--- a/native/jni/src/suggest/policyimpl/typing/scoring_params.h
+++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.h
@@ -44,6 +44,7 @@
     static const float PROXIMITY_COST;
     static const float FIRST_CHAR_PROXIMITY_COST;
     static const float FIRST_PROXIMITY_COST;
+    static const float INTENTIONAL_OMISSION_COST;
     static const float OMISSION_COST;
     static const float OMISSION_COST_SAME_CHAR;
     static const float OMISSION_COST_FIRST_CHAR;
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
index 0ba439b..8407717 100644
--- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
+++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
@@ -54,12 +54,15 @@
 
     float getOmissionCost(const DicNode *const parentDicNode, const DicNode *const dicNode) const {
         const bool isZeroCostOmission = parentDicNode->isZeroCostOmission();
+        const bool isIntentionalOmission = parentDicNode->canBeIntentionalOmission();
         const bool sameCodePoint = dicNode->isSameNodeCodePoint(parentDicNode);
         // If the traversal omitted the first letter then the dicNode should now be on the second.
         const bool isFirstLetterOmission = dicNode->getNodeCodePointCount() == 2;
         float cost = 0.0f;
         if (isZeroCostOmission) {
             cost = 0.0f;
+        } else if (isIntentionalOmission) {
+            cost = ScoringParams::INTENTIONAL_OMISSION_COST;
         } else if (isFirstLetterOmission) {
             cost = ScoringParams::OMISSION_COST_FIRST_CHAR;
         } else {
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index 29423e8..a944416 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -600,4 +600,16 @@
         assertEquals("type words letter by letter", EXPECTED_RESULT,
                 mEditText.getText().toString());
     }
+
+    public void testSwitchLanguages() {
+        final String WORD_TO_TYPE_FIRST_PART = "com";
+        final String WORD_TO_TYPE_SECOND_PART = "md ";
+        final String EXPECTED_RESULT = "comme ";
+        changeLanguage("en");
+        type(WORD_TO_TYPE_FIRST_PART);
+        changeLanguage("fr");
+        type(WORD_TO_TYPE_SECOND_PART);
+        assertEquals("Composing continues after switching languages", EXPECTED_RESULT,
+                mEditText.getText().toString());
+    }
 }
diff --git a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java
index 47f781e..538d759 100644
--- a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java
+++ b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java
@@ -28,8 +28,10 @@
 // TODO: there should not be a dependency to this in dicttool, so there
 // should be a sensible way to separate them cleanly.
 public class CombinerChain {
-    private StringBuilder mComposingWord = new StringBuilder();
-    public CombinerChain(final Combiner... combinerList) {}
+    private StringBuilder mComposingWord;
+    public CombinerChain(final String initialText, final Combiner... combinerList) {
+        mComposingWord = new StringBuilder(initialText);
+    }
 
     public void processEvent(final ArrayList<Event> previousEvents, final Event newEvent) {
         mComposingWord.append(newEvent.getTextToCommit());