Merge "Separate NormalDistribution from ProximityInfoUtils."
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index a7e64bf..6f4e602 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -57,6 +57,7 @@
         </service>
 
         <activity android:name=".setup.SetupActivity"
+                android:theme="@style/platformActivityTheme"
                 android:label="@string/english_ime_name"
                 android:icon="@drawable/ic_launcher_keyboard"
                 android:launchMode="singleTask"
@@ -68,6 +69,7 @@
         </activity>
 
         <activity android:name=".setup.SetupWizardActivity"
+                android:theme="@style/platformActivityTheme"
                 android:label="@string/english_ime_name"
                 android:clearTaskOnLaunch="true">
             <intent-filter>
@@ -84,6 +86,7 @@
         </receiver>
 
         <activity android:name=".settings.SettingsActivity"
+                android:theme="@style/platformActivityTheme"
                 android:label="@string/english_ime_settings"
                 android:uiOptions="splitActionBarWhenNarrow">
             <intent-filter>
@@ -92,6 +95,7 @@
         </activity>
 
         <activity android:name=".spellcheck.SpellCheckerSettingsActivity"
+                  android:theme="@style/platformActivityTheme"
                   android:label="@string/android_spell_checker_settings">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -99,6 +103,7 @@
         </activity>
 
         <activity android:name=".settings.DebugSettingsActivity"
+                android:theme="@style/platformActivityTheme"
                 android:label="@string/english_ime_debug_settings">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -145,8 +150,8 @@
         </receiver>
 
         <activity android:name="com.android.inputmethod.dictionarypack.DictionarySettingsActivity"
+                android:theme="@style/platformActivityTheme"
                 android:label="@string/dictionary_settings_title"
-                android:theme="@android:style/Theme.Holo"
                 android:uiOptions="splitActionBarWhenNarrow">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
@@ -154,8 +159,8 @@
         </activity>
 
         <activity android:name="com.android.inputmethod.dictionarypack.DownloadOverMeteredDialog"
-                android:label="@string/dictionary_install_over_metered_network_prompt"
-                android:theme="@android:style/Theme.Holo">
+                android:theme="@style/platformActivityTheme"
+                android:label="@string/dictionary_install_over_metered_network_prompt">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
             </intent-filter>
diff --git a/java/proguard.flags b/java/proguard.flags
index c08a968..f7b7f28 100644
--- a/java/proguard.flags
+++ b/java/proguard.flags
@@ -14,3 +14,10 @@
 -keepclassmembers class * {
     native <methods>;
 }
+
+# Keep classes that are used as a parameter type of methods that are also marked as keep
+# to preserve changing those methods' signature.
+-keep class com.android.inputmethod.latin.utils.LanguageModelParam
+-keep class com.android.inputmethod.latin.AssetFileAddress
+-keep class com.android.inputmethod.latin.makedict.ProbabilityInfo
+-keep class com.android.inputmethod.latin.Dictionary
diff --git a/java/res/values-v20/platform-theme.xml b/java/res/values-v20/platform-theme.xml
new file mode 100644
index 0000000..b8c1d96
--- /dev/null
+++ b/java/res/values-v20/platform-theme.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<!-- TODO: This file is temporarily placed under values-v20. -->
+<!-- TODO: It might be moved under values-v21. -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- TODO: Use Theme.Quantum.Light once it is fixed.
+    <style name="platformActivityTheme" parent="@android:style/Theme.Quantum.Light" />
+    -->
+    <style name="platformActivityTheme" parent="@android:style/Theme.Quantum.Light.NoActionBar.Fullscreen" />
+    <style name="platformDialogTheme" parent="@android:style/Theme.Quantum.Light.Dialog" />
+</resources>
diff --git a/java/res/values/platform-theme.xml b/java/res/values/platform-theme.xml
new file mode 100644
index 0000000..8e131a2
--- /dev/null
+++ b/java/res/values/platform-theme.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <style name="platformActivityTheme" parent="@android:style/Theme.Holo" />
+    <style name="platformDialogTheme" parent="@android:style/Theme.Holo.Dialog" />
+</resources>
diff --git a/java/src/com/android/inputmethod/latin/ImportantNoticeDialog.java b/java/src/com/android/inputmethod/latin/ImportantNoticeDialog.java
index 9870faa..be54b66 100644
--- a/java/src/com/android/inputmethod/latin/ImportantNoticeDialog.java
+++ b/java/src/com/android/inputmethod/latin/ImportantNoticeDialog.java
@@ -23,6 +23,7 @@
 import android.content.DialogInterface.OnDismissListener;
 import android.content.DialogInterface.OnShowListener;
 
+import com.android.inputmethod.latin.utils.DialogUtils;
 import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
 
 /**
@@ -40,7 +41,7 @@
 
     public ImportantNoticeDialog(
             final Context context, final ImportantNoticeDialogListener listener) {
-        super(context, THEME_HOLO_DARK);
+        super(DialogUtils.getPlatformDialogThemeContext(context));
         mListener = listener;
         mNextImportantNoticeVersion = ImportantNoticeUtils.getNextImportantNoticeVersion(context);
         setMessage(ImportantNoticeUtils.getNextImportantNoticeContents(context));
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index b4807b0..d08f920 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -83,6 +83,7 @@
 import com.android.inputmethod.latin.utils.ApplicationUtils;
 import com.android.inputmethod.latin.utils.CapsModeUtils;
 import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.utils.DialogUtils;
 import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
 import com.android.inputmethod.latin.utils.IntentUtils;
 import com.android.inputmethod.latin.utils.JniUtils;
@@ -1662,8 +1663,9 @@
                 }
             }
         };
-        final AlertDialog.Builder builder =
-                new AlertDialog.Builder(this).setItems(items, listener).setTitle(title);
+        final AlertDialog.Builder builder = new AlertDialog.Builder(
+                DialogUtils.getPlatformDialogThemeContext(this));
+        builder.setItems(items, listener).setTitle(title);
         showOptionDialog(builder.create());
     }
 
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 3f9d57e..5d21554 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -169,31 +169,7 @@
      * coordinates.
      */
     public void add(final Event event) {
-        final int primaryCode = event.mCodePoint;
-        final int keyX = event.mX;
-        final int keyY = event.mY;
-        final int newIndex = size();
         processEvent(event);
-        if (newIndex < MAX_WORD_LENGTH) {
-            mPrimaryKeyCodes[newIndex] = primaryCode >= Constants.CODE_SPACE
-                    ? Character.toLowerCase(primaryCode) : primaryCode;
-            // In the batch input mode, the {@code mInputPointers} holds batch input points and
-            // shouldn't be overridden by the "typed key" coordinates
-            // (See {@link #setBatchInputWord}).
-            if (!mIsBatchMode) {
-                // TODO: Set correct pointer id and time
-                mInputPointers.addPointerAt(newIndex, keyX, keyY, 0, 0);
-            }
-        }
-        mIsFirstCharCapitalized = isFirstCharCapitalized(
-                newIndex, primaryCode, mIsFirstCharCapitalized);
-        if (Character.isUpperCase(primaryCode)) mCapsCount++;
-        if (Character.isDigit(primaryCode)) mDigitsCount++;
-        if (Constants.CODE_SINGLE_QUOTE == primaryCode) {
-            ++mTrailingSingleQuotesCount;
-        } else {
-            mTrailingSingleQuotesCount = 0;
-        }
     }
 
     private void processEvent(final Event event) {
@@ -209,6 +185,39 @@
         if (0 == mCodePointSize) {
             mIsFirstCharCapitalized = false;
         }
+        if (Constants.CODE_DELETE == event.mKeyCode) {
+            if (mTrailingSingleQuotesCount > 0) {
+                --mTrailingSingleQuotesCount;
+            } else {
+                // Delete, but we didn't end in a quote: must recompute mTrailingSingleQuotesCount
+                // We're only searching for single quotes, so no need to account for code points
+                for (int i = mTypedWordCache.length() - 1; i > 0; --i) {
+                    if (Constants.CODE_SINGLE_QUOTE != mTypedWordCache.charAt(i)) {
+                        break;
+                    }
+                    ++mTrailingSingleQuotesCount;
+                }
+            }
+        } else {
+            if (Constants.CODE_SINGLE_QUOTE == primaryCode) {
+                ++mTrailingSingleQuotesCount;
+            } else {
+                mTrailingSingleQuotesCount = 0;
+            }
+            if (newIndex < MAX_WORD_LENGTH) {
+                // In the batch input mode, the {@code mInputPointers} holds batch input points and
+                // shouldn't be overridden by the "typed key" coordinates
+                // (See {@link #setBatchInputWord}).
+                if (!mIsBatchMode) {
+                    // TODO: Set correct pointer id and time
+                    mInputPointers.addPointerAt(newIndex, keyX, keyY, 0, 0);
+                }
+            }
+            mIsFirstCharCapitalized = isFirstCharCapitalized(
+                    newIndex, primaryCode, mIsFirstCharCapitalized);
+            if (Character.isUpperCase(primaryCode)) mCapsCount++;
+            if (Character.isDigit(primaryCode)) mDigitsCount++;
+        }
         mAutoCorrection = null;
     }
 
@@ -217,18 +226,6 @@
      */
     public void deleteLast(final Event event) {
         processEvent(event);
-        if (mTrailingSingleQuotesCount > 0) {
-            --mTrailingSingleQuotesCount;
-        } else {
-            int i = mTypedWordCache.length();
-            while (i > 0) {
-                i = Character.offsetByCodePoints(mTypedWordCache, i, -1);
-                if (Constants.CODE_SINGLE_QUOTE != Character.codePointAt(mTypedWordCache, i)) {
-                    break;
-                }
-                ++mTrailingSingleQuotesCount;
-            }
-        }
     }
 
     public void setCursorPositionWithinWord(final int posWithinWord) {
diff --git a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java
index 800f565..139e73a 100644
--- a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java
+++ b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java
@@ -28,6 +28,7 @@
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.makedict.DictionaryHeader;
 import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.utils.DialogUtils;
 import com.android.inputmethod.latin.utils.DictionaryInfoUtils;
 import com.android.inputmethod.latin.utils.LocaleUtils;
 
@@ -70,7 +71,7 @@
     }
 
     private static void showNoFileDialog(final Context context) {
-        new AlertDialog.Builder(context)
+        new AlertDialog.Builder(DialogUtils.getPlatformDialogThemeContext(context))
                 .setMessage(R.string.read_external_dictionary_no_files_message)
                 .setPositiveButton(android.R.string.ok, new OnClickListener() {
                     @Override
@@ -81,8 +82,8 @@
     }
 
     private static void showChooseFileDialog(final Context context, final String[] fileNames) {
-        final AlertDialog.Builder builder = new AlertDialog.Builder(context);
-        builder.setTitle(R.string.read_external_dictionary_multiple_files_title)
+        new AlertDialog.Builder(DialogUtils.getPlatformDialogThemeContext(context))
+                .setTitle(R.string.read_external_dictionary_multiple_files_title)
                 .setItems(fileNames, new OnClickListener() {
                     @Override
                     public void onClick(final DialogInterface dialog, final int which) {
@@ -111,7 +112,7 @@
         final String title = String.format(
                 context.getString(R.string.read_external_dictionary_confirm_install_message),
                 languageName);
-        new AlertDialog.Builder(context)
+        new AlertDialog.Builder(DialogUtils.getPlatformDialogThemeContext(context))
                 .setTitle(title)
                 .setMessage(message)
                 .setNegativeButton(android.R.string.cancel, new OnClickListener() {
@@ -167,7 +168,7 @@
             }
         } catch (IOException e) {
             // There was an error: show a dialog
-            new AlertDialog.Builder(context)
+            new AlertDialog.Builder(DialogUtils.getPlatformDialogThemeContext(context))
                     .setTitle(R.string.error)
                     .setMessage(e.toString())
                     .setPositiveButton(android.R.string.ok, new OnClickListener() {
diff --git a/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java b/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java
index 6dae620..39977e7 100644
--- a/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java
+++ b/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java
@@ -48,6 +48,7 @@
 import com.android.inputmethod.latin.RichInputMethodManager;
 import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
 import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.utils.DialogUtils;
 import com.android.inputmethod.latin.utils.IntentUtils;
 import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
 
@@ -517,7 +518,8 @@
 
     private AlertDialog createDialog(
             @SuppressWarnings("unused") final SubtypePreference subtypePref) {
-        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        final AlertDialog.Builder builder = new AlertDialog.Builder(
+                DialogUtils.getPlatformDialogThemeContext(getActivity()));
         builder.setTitle(R.string.custom_input_styles_title)
                 .setMessage(R.string.custom_input_style_note_message)
                 .setNegativeButton(R.string.not_now, null)
diff --git a/java/src/com/android/inputmethod/latin/utils/DialogUtils.java b/java/src/com/android/inputmethod/latin/utils/DialogUtils.java
new file mode 100644
index 0000000..a05c932
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/utils/DialogUtils.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.utils;
+
+import android.content.Context;
+import android.view.ContextThemeWrapper;
+
+import com.android.inputmethod.latin.R;
+
+public final class DialogUtils {
+    private DialogUtils() {
+        // This utility class is not publicly instantiable.
+    }
+
+    public static Context getPlatformDialogThemeContext(final Context context) {
+        // Because {@link AlertDialog.Builder.create()} doesn't honor the specified theme with
+        // createThemeContextWrapper=false, the result dialog box has unneeded paddings around it.
+        return new ContextThemeWrapper(context, R.style.platformDialogTheme);
+    }
+}