am 38046a0a: am 7abeccfa: am 1711f986: Merge "Add shadow to gesture preview trail"

* commit '38046a0a4ee4a89b6966ad3416a85117b56893d6':
diff --git a/java/res/drawable-hdpi/unbundled_check_01.png b/java/res/drawable-hdpi/unbundled_check_01.png
new file mode 100644
index 0000000..8234399
--- /dev/null
+++ b/java/res/drawable-hdpi/unbundled_check_01.png
Binary files differ
diff --git a/java/res/drawable-hdpi/unbundled_check_02.png b/java/res/drawable-hdpi/unbundled_check_02.png
new file mode 100644
index 0000000..6ccd07b
--- /dev/null
+++ b/java/res/drawable-hdpi/unbundled_check_02.png
Binary files differ
diff --git a/java/res/drawable-hdpi/unbundled_earth_01.png b/java/res/drawable-hdpi/unbundled_earth_01.png
new file mode 100644
index 0000000..3d22f3b
--- /dev/null
+++ b/java/res/drawable-hdpi/unbundled_earth_01.png
Binary files differ
diff --git a/java/res/drawable-hdpi/unbundled_earth_02.png b/java/res/drawable-hdpi/unbundled_earth_02.png
new file mode 100644
index 0000000..1998aea
--- /dev/null
+++ b/java/res/drawable-hdpi/unbundled_earth_02.png
Binary files differ
diff --git a/java/res/drawable-hdpi/unbundled_key_01.png b/java/res/drawable-hdpi/unbundled_key_01.png
new file mode 100644
index 0000000..84591ec
--- /dev/null
+++ b/java/res/drawable-hdpi/unbundled_key_01.png
Binary files differ
diff --git a/java/res/drawable-hdpi/unbundled_key_02.png b/java/res/drawable-hdpi/unbundled_key_02.png
new file mode 100644
index 0000000..f366e52
--- /dev/null
+++ b/java/res/drawable-hdpi/unbundled_key_02.png
Binary files differ
diff --git a/java/res/drawable-hdpi/unbundled_select_01.png b/java/res/drawable-hdpi/unbundled_select_01.png
new file mode 100644
index 0000000..3887fe4
--- /dev/null
+++ b/java/res/drawable-hdpi/unbundled_select_01.png
Binary files differ
diff --git a/java/res/drawable-hdpi/unbundled_select_02.png b/java/res/drawable-hdpi/unbundled_select_02.png
new file mode 100644
index 0000000..6a99b6b
--- /dev/null
+++ b/java/res/drawable-hdpi/unbundled_select_02.png
Binary files differ
diff --git a/java/res/drawable/ic_setup_step1.xml b/java/res/drawable/ic_setup_step1.xml
new file mode 100644
index 0000000..e26afb3
--- /dev/null
+++ b/java/res/drawable/ic_setup_step1.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:drawable="@drawable/unbundled_key_01" />
+    <item
+        android:state_pressed="true"
+        android:drawable="@drawable/unbundled_key_01" />
+    <item
+        android:drawable="@drawable/unbundled_key_02" />
+</selector>
diff --git a/java/res/drawable/ic_setup_step2.xml b/java/res/drawable/ic_setup_step2.xml
new file mode 100644
index 0000000..46db293
--- /dev/null
+++ b/java/res/drawable/ic_setup_step2.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:drawable="@drawable/unbundled_select_01" />
+    <item
+        android:state_pressed="true"
+        android:drawable="@drawable/unbundled_select_01" />
+    <item
+        android:drawable="@drawable/unbundled_select_02" />
+</selector>
diff --git a/java/res/drawable/ic_setup_step3.xml b/java/res/drawable/ic_setup_step3.xml
new file mode 100644
index 0000000..4ff9fd9
--- /dev/null
+++ b/java/res/drawable/ic_setup_step3.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:drawable="@drawable/unbundled_earth_01" />
+    <item
+        android:state_pressed="true"
+        android:drawable="@drawable/unbundled_earth_01" />
+    <item
+        android:drawable="@drawable/unbundled_earth_02" />
+</selector>
diff --git a/java/res/drawable/ic_setup_step3_finish.xml b/java/res/drawable/ic_setup_step3_finish.xml
new file mode 100644
index 0000000..8ac8a86
--- /dev/null
+++ b/java/res/drawable/ic_setup_step3_finish.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:drawable="@drawable/unbundled_check_01" />
+    <item
+        android:state_pressed="true"
+        android:drawable="@drawable/unbundled_check_01" />
+    <item
+        android:drawable="@drawable/unbundled_check_02" />
+</selector>
diff --git a/java/res/drawable/setup_step_action_background.xml b/java/res/drawable/setup_step_action_background.xml
new file mode 100644
index 0000000..25738e3
--- /dev/null
+++ b/java/res/drawable/setup_step_action_background.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:drawable="@color/setup_text_action" />
+    <item
+        android:state_pressed="true"
+        android:drawable="@color/setup_text_action" />
+    <item
+        android:drawable="@color/setup_step_background" />
+</selector>
diff --git a/java/res/drawable/setup_step_action_color.xml b/java/res/drawable/setup_step_action_color.xml
new file mode 100644
index 0000000..c53e026
--- /dev/null
+++ b/java/res/drawable/setup_step_action_color.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:color="@color/setup_step_background" />
+    <item
+        android:state_pressed="true"
+        android:color="@color/setup_step_background" />
+    <item
+        android:color="@color/setup_text_action" />
+</selector>
diff --git a/java/res/layout/setup_step.xml b/java/res/layout/setup_step.xml
index 26d7fe7..c15d07b 100644
--- a/java/res/layout/setup_step.xml
+++ b/java/res/layout/setup_step.xml
@@ -42,7 +42,7 @@
     <View
         android:layout_width="match_parent"
         android:layout_height="2dp" />
-    <TextView
+    <Button
         android:id="@+id/setup_step_action_label"
         style="@style/setupStepActionLabelStyle"
         android:gravity="center_vertical"
diff --git a/java/res/values/setup-styles.xml b/java/res/values/setup-styles.xml
index cfc689a..420adcb 100644
--- a/java/res/values/setup-styles.xml
+++ b/java/res/values/setup-styles.xml
@@ -38,8 +38,8 @@
         <item name="android:textSize">14sp</item>
     </style>
     <style name="setupStepActionLabelStyle">
-        <item name="android:background">@color/setup_step_background</item>
-        <item name="android:textColor">@color/setup_text_action</item>
+        <item name="android:background">@drawable/setup_step_action_background</item>
+        <item name="android:textColor">@drawable/setup_step_action_color</item>
         <item name="android:textSize">18sp</item>
     </style>
 </resources>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index dbadfab..3d283de 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -442,31 +442,40 @@
     <!-- Title of the button to revert to the default value of the device in the settings dialog [CHAR LIMIT=15] -->
     <string name="button_default">Default</string>
 
-    <!-- TODO: Remove translatable="false" once wordings are finalized. -->
+    <!-- Title of the setup wizard welcome screen. [CHAR LIMT=40] -->
+    <string name="setup_welcome_title">"Welcome to <xliff:g id="application_name">%s</xliff:g>"</string>
+    <!-- Additional title of the setup wizard welcome screen, just below the setup_welcome_title. [CHAR_LIMIT=64] -->
+    <string name="setup_welcome_additional_description">with Gesture Typing</string>
+    <!-- The label of the button that starts the setup wizard. [CHAR_LIMIT=64] -->
+    <string name="setup_start_action">Get started</string>
     <!-- Title of the setup wizard. [CHAR LIMT=40] -->
-    <string name="setup_title" translatable="false">"Installing <xliff:g id="application_name">%s</xliff:g>"</string>
+    <string name="setup_steps_title">"Setting up <xliff:g id="application_name">%s</xliff:g>"</string>
     <!-- Ordinal number of the 1st step in the setup wizard. [CHAR LIMIT=5] -->
     <string name="setup_step1_bullet" translatable="false">1</string>
     <!-- Title of the 1st step in the setup wizard. [CHAR LIMIT=64] -->
-    <string name="setup_step1_title" translatable="false">"Enable <xliff:g id="application_name">%s</xliff:g> in settings."</string>
+    <string name="setup_step1_title">"Enable <xliff:g id="application_name">%s</xliff:g>"</string>
     <!-- Detailed instruction of the 1st step in the setup wizard. [CHAR LIMIT=80] -->
-    <string name="setup_step1_instruction" translatable="false">"For security, please check \"<xliff:g id="application_name">%s</xliff:g>\""</string>
+    <string name="setup_step1_instruction">"Please check \"<xliff:g id="application_name">%s</xliff:g>\" in your Language &amp; input settings. This will authorize it to run on your device."</string>
+    <!-- Title of the Language & input settings. This should be aligned with msgid="5292716747264442359" -->
+    <string name="setup_step1_action">Language &amp; input</string>
     <!-- Ordinal number of the 2nd step in the setup wizard. [CHAR LIMIT=5] -->
     <string name="setup_step2_bullet" translatable="false">2</string>
     <!-- Title of the 2nd step in the setup wizard. [CHAR LIMIT=64] -->
-    <string name="setup_step2_title" translatable="false">"Switch to <xliff:g id="application_name">%s</xliff:g>."</string>
+    <string name="setup_step2_title">"Switch to <xliff:g id="application_name">%s</xliff:g>"</string>
     <!-- Detailed instruction of the 2nd step in the setup wizard. [CHAR LIMIT=80] -->
-    <string name="setup_step2_instruction" translatable="false">"Now that you've enabled <xliff:g id="application_name">%s</xliff:g>, you can switch to it."</string>
+    <string name="setup_step2_instruction">"Now that it's enabled, select \"<xliff:g id="application_name">%s</xliff:g>\", one more time to activate it."</string>
+    <!-- Title of the Input method picker. This should be aligned with msgid="4653387336791222978" -->
+    <string name="setup_step2_action">Choose input method</string>
     <!-- Ordinal number of the 3rd step in the setup wizard. [CHAR LIMIT=5] -->
     <string name="setup_step3_bullet" translatable="false">3</string>
     <!-- Title of the 3rd step in the setup wizard. [CHAR LIMIT=64] -->
-    <string name="setup_step3_title" translatable="false">"Congratulations, you're all set!"</string>
+    <string name="setup_step3_title">"Congratulations, you're all set!"</string>
     <!-- Detailed instruction of the 3rd step in the setup wizard. [CHAR LIMIT=80] -->
-    <string name="setup_step3_instruction" translatable="false">Configure additional languages</string>
-    <!-- Title of the Language & input settings. This should be aligned with msgid="5292716747264442359" -->
-    <string name="language_settings">Language &amp; input</string>
-    <!-- Title of the Input method picker. This should be aligned with msgid="4653387336791222978" -->
-    <string name="select_input_method">Choose input method</string>
+    <string name="setup_step3_instruction">Now you can type in all your favorite apps with <xliff:g id="application_name">%s</xliff:g>.</string>
+    <!-- The label of the button that triggers the screen for configuaring additional languages of the keyboard. [CHAR_LIMIT=64] -->
+    <string name="setup_step3_action">Configure additional languages</string>
+    <!-- The label of the button that finishes the setup wizard. [CHAR_LIMIT=64] -->
+    <string name="setup_finish_action">Finished</string>
     <!-- Option to show setup wizard icon. [CHAR LIMIT=30]-->
     <string name="show_setup_wizard_icon" translatable="false">Show setup wizard icon</string>
 
@@ -498,9 +507,9 @@
     <!-- Message about some dictionary indicating the file is installed, but the dictionary is disabled -->
     <string name="dictionary_disabled">Installed, disabled</string>
 
-    <!-- Message to display in the dictionaries setting screen when some error prevented us to list installed dictionaries [CHAR LIMIT=50] -->
+    <!-- Message to display in the dictionaries setting screen when some error prevented us to list installed dictionaries [CHAR LIMIT=20] -->
     <string name="cannot_connect_to_dict_service">Problem connecting to dictionary service</string>
-    <!-- Message to display in the dictionaries setting screen when we found that no dictionaries are available [CHAR LIMIT=50]-->
+    <!-- Message to display in the dictionaries setting screen when we found that no dictionaries are available [CHAR LIMIT=20]-->
     <string name="no_dictionaries_available">No dictionaries available</string>
 
     <!-- Title of the options to press to refresh the list (as in, check for updates now) [CHAR_LIMIT=50] -->
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index ba78d01..d74644d 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -1189,10 +1189,12 @@
                 if (ENABLE_USABILITY_STUDY_LOG) {
                     writeUsabilityStudyLog(me, action, eventTime, i, pointerId, px, py);
                 }
-                if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
-                    ResearchLogger.mainKeyboardView_processMotionEvent(
-                            me, action, eventTime, i, pointerId, px, py);
-                }
+                // TODO: This seems to be no longer necessary, and confusing because it leads to
+                // duplicate MotionEvents being recorded.
+                // if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
+                //     ResearchLogger.mainKeyboardView_processMotionEvent(
+                //             me, action, eventTime, i, pointerId, px, py);
+                // }
             }
         } else {
             final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java
index 0c05061..7fd1bed 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java
@@ -108,7 +108,7 @@
     }
 
     private void addStrokeLocked(final GestureStrokeWithPreviewPoints stroke, final long downTime) {
-            final int trailSize = mEventTimes.getLength();
+        final int trailSize = mEventTimes.getLength();
         stroke.appendPreviewStroke(mEventTimes, mXCoordinates, mYCoordinates);
         if (mEventTimes.getLength() == trailSize) {
             return;
@@ -261,14 +261,14 @@
                 System.arraycopy(eventTimes, startIndex, eventTimes, 0, newSize);
                 System.arraycopy(xCoords, startIndex, xCoords, 0, newSize);
                 System.arraycopy(yCoords, startIndex, yCoords, 0, newSize);
-                // The start index of the last segment of the stroke
-                // {@link mLastInterpolatedDrawIndex} should also be updated because all array
-                // elements have just been shifted for compaction.
-                mLastInterpolatedDrawIndex = Math.max(mLastInterpolatedDrawIndex - startIndex, 0);
             }
             mEventTimes.setLength(newSize);
             mXCoordinates.setLength(newSize);
             mYCoordinates.setLength(newSize);
+            // The start index of the last segment of the stroke
+            // {@link mLastInterpolatedDrawIndex} should also be updated because all array
+            // elements have just been shifted for compaction or been zeroed.
+            mLastInterpolatedDrawIndex = Math.max(mLastInterpolatedDrawIndex - startIndex, 0);
         }
         return newSize > 0;
     }
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index 50e5023..86bb255 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -160,6 +160,8 @@
     public static final int CODE_DOUBLE_QUOTE = '"';
     public static final int CODE_QUESTION_MARK = '?';
     public static final int CODE_EXCLAMATION_MARK = '!';
+    public static final int CODE_SLASH = '/';
+    public static final int CODE_COMMERCIAL_AT = '@';
     // TODO: Check how this should work for right-to-left languages. It seems to stand
     // that for rtl languages, a closing parenthesis is a left parenthesis. Is this
     // managed by the font? Or is it a different char?
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index 22d1899..75c2cf2 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -252,7 +252,7 @@
     }
 
     private static boolean isValidName(final String name) {
-        if (name != null && -1 == name.indexOf('@')) {
+        if (name != null && -1 == name.indexOf(Constants.CODE_COMMERCIAL_AT)) {
             return true;
         }
         return false;
diff --git a/java/src/com/android/inputmethod/latin/InputTypeUtils.java b/java/src/com/android/inputmethod/latin/InputTypeUtils.java
index ecb2014..46194f6 100644
--- a/java/src/com/android/inputmethod/latin/InputTypeUtils.java
+++ b/java/src/com/android/inputmethod/latin/InputTypeUtils.java
@@ -33,7 +33,6 @@
     private static final int[] SUPPRESSING_AUTO_SPACES_FIELD_VARIATION = {
         InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS,
         InputType.TYPE_TEXT_VARIATION_PASSWORD,
-        InputType.TYPE_TEXT_VARIATION_URI,
         InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD,
         InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD };
     public static final int IME_ACTION_CUSTOM_LABEL = EditorInfo.IME_MASK_ACTION + 1;
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 0a6f2ab..0e1c4dc 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -2559,7 +2559,8 @@
 
     // This essentially inserts a space, and that's it.
     public void promotePhantomSpace() {
-        if (mSettings.getCurrent().shouldInsertSpacesAutomatically()) {
+        if (mSettings.getCurrent().shouldInsertSpacesAutomatically()
+                && !mConnection.textBeforeCursorLooksLikeURL()) {
             sendKeyCodePoint(Constants.CODE_SPACE);
             if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
                 ResearchLogger.latinIME_promotePhantomSpace();
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index e178466..8ed7ab2 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -19,7 +19,6 @@
 import android.inputmethodservice.InputMethodService;
 import android.text.SpannableString;
 import android.text.TextUtils;
-import android.text.style.SuggestionSpan;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.inputmethod.CompletionInfo;
@@ -721,4 +720,15 @@
         // position and the expected position, then it must be a belated update.
         return (newSelStart - oldSelStart) * (mCurrentCursorPosition - newSelStart) >= 0;
     }
+
+    /**
+     * Looks at the text just before the cursor to find out if it looks like a URL.
+     *
+     * The weakest point here is, if we don't have enough text bufferized, we may fail to realize
+     * we are in URL situation, but other places in this class have the same limitation and it
+     * does not matter too much in the practice.
+     */
+    public boolean textBeforeCursorLooksLikeURL() {
+        return StringUtils.lastPartLooksLikeURL(mCommittedTextBeforeComposingText);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/SettingsFragment.java b/java/src/com/android/inputmethod/latin/SettingsFragment.java
index a96c997..79036c2 100644
--- a/java/src/com/android/inputmethod/latin/SettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/SettingsFragment.java
@@ -77,10 +77,13 @@
         final Resources res = getResources();
         final Context context = getActivity();
 
-        // When we are called from the Settings application but we are not already running, the
-        // {@link SubtypeLocale} class may not have been initialized. It is safe to call
-        // {@link SubtypeLocale#init(Context)} multiple times.
+        // When we are called from the Settings application but we are not already running, some
+        // singleton and utility classes may not have been initialized.  We have to call
+        // initialization method of these classes here. See {@link LatinIME#onCreate()}.
+        SubtypeSwitcher.init(context);
         SubtypeLocale.init(context);
+        AudioAndHapticFeedbackManager.init(context);
+
         mVoicePreference = (ListPreference) findPreference(Settings.PREF_VOICE_MODE);
         mShowCorrectionSuggestionsPreference =
                 (ListPreference) findPreference(Settings.PREF_SHOW_SUGGESTIONS_SETTING);
diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java
index 7f1e7c6..d5ee58a 100644
--- a/java/src/com/android/inputmethod/latin/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/StringUtils.java
@@ -282,4 +282,69 @@
         }
         return builder.toString();
     }
+
+    /**
+     * Approximates whether the text before the cursor looks like a URL.
+     *
+     * This is not foolproof, but it should work well in the practice.
+     * Essentially it walks backward from the cursor until it finds something that's not a letter,
+     * digit, or common URL symbol like underscore. If it hasn't found a period yet, then it
+     * does not look like a URL.
+     * If the text:
+     * - starts with www and contains a period
+     * - starts with a slash preceded by either a slash, whitespace, or start-of-string
+     * Then it looks like a URL and we return true. Otherwise, we return false.
+     *
+     * Note: this method is called quite often, and should be fast.
+     *
+     * TODO: This will return that "abc./def" and ".abc/def" look like URLs to keep down the
+     * code complexity, but ideally it should not. It's acceptable for now.
+     */
+    public static boolean lastPartLooksLikeURL(final CharSequence text) {
+        int i = text.length();
+        if (0 == i) return false;
+        int wCount = 0;
+        int slashCount = 0;
+        boolean hasSlash = false;
+        boolean hasPeriod = false;
+        int codePoint = 0;
+        while (i > 0) {
+            codePoint =  Character.codePointBefore(text, i);
+            if (codePoint < Constants.CODE_PERIOD || codePoint > 'z') {
+                // Handwavy heuristic to see if that's a URL character. Anything between period
+                // and z. This includes all lower- and upper-case ascii letters, period,
+                // underscore, arrobase, question mark, equal sign. It excludes spaces, exclamation
+                // marks, double quotes...
+                // Anything that's not a URL-like character causes us to break from here and
+                // evaluate normally.
+                break;
+            }
+            if (Constants.CODE_PERIOD == codePoint) {
+                hasPeriod = true;
+            }
+            if (Constants.CODE_SLASH == codePoint) {
+                hasSlash = true;
+                if (2 == ++slashCount) {
+                    return true;
+                }
+            } else {
+                slashCount = 0;
+            }
+            if ('w' == codePoint) {
+                ++wCount;
+            } else {
+                wCount = 0;
+            }
+            i = Character.offsetByCodePoints(text, i, -1);
+        }
+        // End of the text run.
+        // If it starts with www and includes a period, then it looks like a URL.
+        if (wCount >= 3 && hasPeriod) return true;
+        // If it starts with a slash, and the code point before is whitespace, it looks like an URL.
+        if (1 == slashCount && (0 == i || Character.isWhitespace(codePoint))) return true;
+        // If it has both a period and a slash, it looks like an URL.
+        if (hasPeriod && hasSlash) return true;
+        // Otherwise, it doesn't look like an URL.
+        return false;
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 2f9e34f..bef8a3c 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -80,6 +80,7 @@
 
     public static void init(final Context context) {
         SubtypeLocale.init(context);
+        RichInputMethodManager.init(context);
         sInstance.initialize(context);
     }
 
@@ -87,10 +88,13 @@
         // Intentional empty constructor for singleton.
     }
 
-    private void initialize(final Context service) {
-        mResources = service.getResources();
+    private void initialize(final Context context) {
+        if (mResources != null) {
+            return;
+        }
+        mResources = context.getResources();
         mRichImm = RichInputMethodManager.getInstance();
-        mConnectivityManager = (ConnectivityManager) service.getSystemService(
+        mConnectivityManager = (ConnectivityManager) context.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
         mNoLanguageSubtype = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
                 SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY);
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupActivity.java
index 15d0bac..099169a 100644
--- a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java
+++ b/java/src/com/android/inputmethod/latin/setup/SetupActivity.java
@@ -20,7 +20,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
-import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Message;
@@ -113,13 +112,13 @@
         // the SDK version.
         final TextView titleView = (TextView)findViewById(R.id.setup_title);
         final int appName = getApplicationInfo().labelRes;
-        titleView.setText(getString(R.string.setup_title, getString(appName)));
+        titleView.setText(getString(R.string.setup_steps_title, getString(appName)));
 
         mStepIndicatorView = (SetupStepIndicatorView)findViewById(R.id.setup_step_indicator);
 
         final SetupStep step1 = new SetupStep(findViewById(R.id.setup_step1),
                 appName, R.string.setup_step1_title, R.string.setup_step1_instruction,
-                R.drawable.ic_settings_language, R.string.language_settings);
+                R.drawable.ic_setup_step1, R.string.setup_step1_action);
         step1.setAction(new Runnable() {
             @Override
             public void run() {
@@ -131,7 +130,7 @@
 
         final SetupStep step2 = new SetupStep(findViewById(R.id.setup_step2),
                 appName, R.string.setup_step2_title, R.string.setup_step2_instruction,
-                0 /* actionIcon */, R.string.select_input_method);
+                R.drawable.ic_setup_step2, R.string.setup_step2_action);
         step2.setAction(new Runnable() {
             @Override
             public void run() {
@@ -143,8 +142,8 @@
         mSetupSteps.addStep(STEP_2, step2);
 
         final SetupStep step3 = new SetupStep(findViewById(R.id.setup_step3),
-                appName, R.string.setup_step3_title, 0 /* instruction */,
-                R.drawable.sym_keyboard_language_switch, R.string.setup_step3_instruction);
+                appName, R.string.setup_step3_title, R.string.setup_step3_instruction,
+                R.drawable.ic_setup_step3, R.string.setup_step3_action);
         step3.setAction(new Runnable() {
             @Override
             public void run() {
@@ -314,9 +313,7 @@
                 final int paddingEnd = ViewCompatUtils.getPaddingEnd(mActionLabel);
                 ViewCompatUtils.setPaddingRelative(mActionLabel, paddingEnd, 0, paddingEnd, 0);
             } else {
-                final int overrideColor = res.getColor(R.color.setup_text_action);
                 final Drawable icon = res.getDrawable(actionIcon);
-                icon.setColorFilter(overrideColor, PorterDuff.Mode.MULTIPLY);
                 icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
                 TextViewCompatUtils.setCompoundDrawablesRelative(
                         mActionLabel, icon, null, null, null);
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
index 96b2c81..da86572 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -189,10 +189,12 @@
         int letterCount = 0;
         for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) {
             final int codePoint = text.codePointAt(i);
-            // Any word containing a '@' is probably an e-mail address
-            // Any word containing a '/' is probably either an ad-hoc combination of two
+            // Any word containing a COMMERCIAL_AT is probably an e-mail address
+            // Any word containing a SLASH is probably either an ad-hoc combination of two
             // words or a URI - in either case we don't want to spell check that
-            if ('@' == codePoint || '/' == codePoint) return true;
+            if (Constants.CODE_COMMERCIAL_AT == codePoint || Constants.CODE_SLASH == codePoint) {
+                return true;
+            }
             if (isLetterCheckableByLanguage(codePoint, script)) ++letterCount;
         }
         // Guestimate heuristic: perform spell checking if at least 3/4 of the characters
diff --git a/java/src/com/android/inputmethod/research/FeedbackFragment.java b/java/src/com/android/inputmethod/research/FeedbackFragment.java
index 39f9c87..a073829 100644
--- a/java/src/com/android/inputmethod/research/FeedbackFragment.java
+++ b/java/src/com/android/inputmethod/research/FeedbackFragment.java
@@ -65,12 +65,10 @@
         mCancelButton.setOnClickListener(this);
 
         if (savedInstanceState != null) {
-            Log.d(TAG, "restoring from savedInstanceState");
             restoreState(savedInstanceState);
         } else {
             final Bundle bundle = getActivity().getIntent().getExtras();
             if (bundle != null) {
-                Log.d(TAG, "restoring from getArguments()");
                 restoreState(bundle);
             }
         }
@@ -81,10 +79,7 @@
     public void onClick(final View view) {
         final ResearchLogger researchLogger = ResearchLogger.getInstance();
         if (view == mIncludingUserRecordingCheckBox) {
-            if (hasUserRecording()) {
-                // Remove the recording
-                setHasUserRecording(false);
-            } else {
+            if (mIncludingUserRecordingCheckBox.isChecked()) {
                 final Bundle bundle = new Bundle();
                 onSaveInstanceState(bundle);
 
@@ -103,9 +98,9 @@
                         R.string.research_feedback_empty_feedback_error_message,
                         Toast.LENGTH_LONG).show();
             } else {
-                final boolean isIncludingAccountName = isIncludingAccountName();
-                researchLogger.sendFeedback(feedbackContents,
-                        false /* isIncludingHistory */, isIncludingAccountName, hasUserRecording());
+                final boolean isIncludingAccountName = mIncludingAccountNameCheckBox.isChecked();
+                researchLogger.sendFeedback(feedbackContents, false /* isIncludingHistory */,
+                        isIncludingAccountName, mIncludingUserRecordingCheckBox.isChecked());
                 getActivity().finish();
                 researchLogger.setFeedbackDialogBundle(null);
                 researchLogger.onLeavingSendFeedbackDialog();
@@ -125,29 +120,13 @@
         final String savedFeedbackString = mEditText.getText().toString();
 
         bundle.putString(KEY_FEEDBACK_STRING, savedFeedbackString);
-        bundle.putBoolean(KEY_INCLUDE_ACCOUNT_NAME, isIncludingAccountName());
-        bundle.putBoolean(KEY_HAS_USER_RECORDING, hasUserRecording());
+        bundle.putBoolean(KEY_INCLUDE_ACCOUNT_NAME, mIncludingAccountNameCheckBox.isChecked());
+        bundle.putBoolean(KEY_HAS_USER_RECORDING, mIncludingUserRecordingCheckBox.isChecked());
     }
 
-    public void restoreState(final Bundle bundle) {
+    private void restoreState(final Bundle bundle) {
         mEditText.setText(bundle.getString(KEY_FEEDBACK_STRING));
-        setIsIncludingAccountName(bundle.getBoolean(KEY_INCLUDE_ACCOUNT_NAME));
-        setHasUserRecording(bundle.getBoolean(KEY_HAS_USER_RECORDING));
-    }
-
-    private boolean hasUserRecording() {
-        return mIncludingUserRecordingCheckBox.isChecked();
-    }
-
-    private void setHasUserRecording(final boolean hasRecording) {
-        mIncludingUserRecordingCheckBox.setChecked(hasRecording);
-    }
-
-    private boolean isIncludingAccountName() {
-        return mIncludingAccountNameCheckBox.isChecked();
-    }
-
-    private void setIsIncludingAccountName(final boolean isIncludingAccountName) {
-        mIncludingAccountNameCheckBox.setChecked(isIncludingAccountName);
+        mIncludingAccountNameCheckBox.setChecked(bundle.getBoolean(KEY_INCLUDE_ACCOUNT_NAME));
+        mIncludingUserRecordingCheckBox.setChecked(bundle.getBoolean(KEY_HAS_USER_RECORDING));
     }
 }
diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java
index 35a491f..18bf7ba 100644
--- a/java/src/com/android/inputmethod/research/ResearchLog.java
+++ b/java/src/com/android/inputmethod/research/ResearchLog.java
@@ -108,10 +108,14 @@
             @Override
             public Object call() throws Exception {
                 try {
-                    if (mHasWrittenData) {
-                        mJsonWriter.endArray();
-                        mHasWrittenData = false;
+                    // TODO: This is necessary to avoid an exception.  Better would be to not even
+                    // open the JsonWriter if the file is not even opened unless there is valid data
+                    // to write.
+                    if (!mHasWrittenData) {
+                        mJsonWriter.beginArray();
                     }
+                    mJsonWriter.endArray();
+                    mHasWrittenData = false;
                     mJsonWriter.flush();
                     mJsonWriter.close();
                     if (DEBUG) {
@@ -159,6 +163,12 @@
             public Object call() throws Exception {
                 try {
                     if (mHasWrittenData) {
+                        // TODO: This is necessary to avoid an exception.  Better would be to not
+                        // even open the JsonWriter if the file is not even opened unless there is
+                        // valid data to write.
+                        if (!mHasWrittenData) {
+                            mJsonWriter.beginArray();
+                        }
                         mJsonWriter.endArray();
                         mJsonWriter.close();
                         mHasWrittenData = false;
diff --git a/java/src/com/android/inputmethod/research/ResearchLogDirectory.java b/java/src/com/android/inputmethod/research/ResearchLogDirectory.java
index 291dea5..d156068 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogDirectory.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogDirectory.java
@@ -97,15 +97,17 @@
         }
     }
 
-    public File getLogFilePath(final long time) {
-        return new File(mFilesDir, getUniqueFilename(LOG_FILENAME_PREFIX, time));
+    public File getLogFilePath(final long time, final long nanoTime) {
+        return new File(mFilesDir, getUniqueFilename(LOG_FILENAME_PREFIX, time, nanoTime));
     }
 
-    public File getUserRecordingFilePath(final long time) {
-        return new File(mFilesDir, getUniqueFilename(USER_RECORDING_FILENAME_PREFIX, time));
+    public File getUserRecordingFilePath(final long time, final long nanoTime) {
+        return new File(mFilesDir, getUniqueFilename(USER_RECORDING_FILENAME_PREFIX, time,
+                nanoTime));
     }
 
-    private static String getUniqueFilename(final String prefix, final long time) {
-        return prefix + "-" + time + FILENAME_SUFFIX;
+    private static String getUniqueFilename(final String prefix, final long time,
+            final long nanoTime) {
+        return prefix + "-" + time + "-" + nanoTime + FILENAME_SUFFIX;
     }
 }
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
index 7a23ddb..cd18e3d 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -389,7 +389,7 @@
         }
         if (mMainLogBuffer == null) {
             mMainResearchLog = new ResearchLog(mResearchLogDirectory.getLogFilePath(
-                    System.currentTimeMillis()), mLatinIME);
+                    System.currentTimeMillis(), System.nanoTime()), mLatinIME);
             final int numWordsToIgnore = new Random().nextInt(NUMBER_OF_WORDS_BETWEEN_SAMPLES + 1);
             mMainLogBuffer = new MainLogBuffer(NUMBER_OF_WORDS_BETWEEN_SAMPLES, numWordsToIgnore,
                     mSuggest) {
@@ -420,7 +420,7 @@
 
     private void resetFeedbackLogging() {
         mFeedbackLog = new ResearchLog(mResearchLogDirectory.getLogFilePath(
-                System.currentTimeMillis()), mLatinIME);
+                System.currentTimeMillis(), System.nanoTime()), mLatinIME);
         mFeedbackLogBuffer = new FixedLogBuffer(FEEDBACK_WORD_BUFFER_SIZE);
     }
 
@@ -545,7 +545,7 @@
             mUserRecordingLog.blockingAbort(RESEARCHLOG_ABORT_TIMEOUT_IN_MS);
         }
         mUserRecordingFile = mResearchLogDirectory.getUserRecordingFilePath(
-                System.currentTimeMillis());
+                System.currentTimeMillis(), System.nanoTime());
         mUserRecordingLog = new ResearchLog(mUserRecordingFile, mLatinIME);
         mUserRecordingLogBuffer = new LogBuffer();
         resetRecordingTimer();
@@ -813,7 +813,7 @@
                 // enabled.  The dot is actually a zero-width, zero-height rectangle, placed at the
                 // lower-right corner of the canvas, painted with a non-zero border width.
                 paint.setStrokeWidth(3);
-                canvas.drawRect(width, height, width, height, paint);
+                canvas.drawRect(width - 1, height - 1, width, height, paint);
             }
             paint.setColor(savedColor);
             paint.setStyle(savedStyle);
@@ -894,7 +894,7 @@
         // Check that expected word matches.
         if (oldLogUnit != null) {
             final String oldLogUnitWord = oldLogUnit.getWord();
-            if (!oldLogUnitWord.equals(expectedWord)) {
+            if (oldLogUnitWord != null && !oldLogUnitWord.equals(expectedWord)) {
                 return;
             }
         }
@@ -1107,7 +1107,7 @@
             packageInfo = mLatinIME.getPackageManager().getPackageInfo(mLatinIME.getPackageName(),
                     0);
             final String versionName = packageInfo.versionName;
-            return !(developerBuildRegex.matcher(versionName).find());
+            return developerBuildRegex.matcher(versionName).find();
         } catch (final NameNotFoundException e) {
             Log.e(TAG, "Could not determine package name", e);
             return false;
@@ -1826,6 +1826,9 @@
     public static void latinIME_onEndBatchInput(final CharSequence enteredText,
             final int enteredWordPos, final SuggestedWords suggestedWords) {
         final ResearchLogger researchLogger = getInstance();
+        if (!TextUtils.isEmpty(enteredText) && hasLetters(enteredText.toString())) {
+            researchLogger.mCurrentLogUnit.setWord(enteredText.toString());
+        }
         researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_ONENDBATCHINPUT, enteredText,
                 enteredWordPos);
         researchLogger.mCurrentLogUnit.initializeSuggestions(suggestedWords);
diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java
index 6a9717b..d2db349 100644
--- a/java/src/com/android/inputmethod/research/UploaderService.java
+++ b/java/src/com/android/inputmethod/research/UploaderService.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.SystemClock;
 
 import com.android.inputmethod.latin.define.ProductionFlag;
 
@@ -79,28 +80,14 @@
      */
     public static void cancelAndRescheduleUploadingService(final Context context,
             final boolean needsRescheduling) {
-        final PendingIntent pendingIntent = getPendingIntentForService(context);
+        final Intent intent = new Intent(context, UploaderService.class);
+        final PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0);
         final AlarmManager alarmManager = (AlarmManager) context.getSystemService(
                 Context.ALARM_SERVICE);
-        cancelAnyScheduledServiceAlarm(alarmManager, pendingIntent);
-        if (needsRescheduling) {
-            scheduleServiceAlarm(alarmManager, pendingIntent);
-        }
-    }
-
-    private static PendingIntent getPendingIntentForService(final Context context) {
-        final Intent intent = new Intent(context, UploaderService.class);
-        return PendingIntent.getService(context, 0, intent, 0);
-    }
-
-    private static void cancelAnyScheduledServiceAlarm(final AlarmManager alarmManager,
-            final PendingIntent pendingIntent) {
         alarmManager.cancel(pendingIntent);
-    }
-
-    private static void scheduleServiceAlarm(final AlarmManager alarmManager,
-            final PendingIntent pendingIntent) {
-        alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, UploaderService.RUN_INTERVAL,
-                pendingIntent);
+        if (needsRescheduling) {
+            alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()
+                    + UploaderService.RUN_INTERVAL, pendingIntent);
+        }
     }
 }
diff --git a/native/jni/src/char_utils.cpp b/native/jni/src/char_utils.cpp
index 8d917ea..e219beb 100644
--- a/native/jni/src/char_utils.cpp
+++ b/native/jni/src/char_utils.cpp
@@ -45,18 +45,16 @@
 
 extern "C" int main() {
     for (unsigned short c = 0; c < 0xFFFF; c++) {
-        const unsigned short baseC = c < NELEMS(BASE_CHARS) ? BASE_CHARS[c] : c;
-        if (baseC <= 0x7F) continue;
-        const unsigned short icu4cLowerBaseC = u_tolower(baseC);
-        const unsigned short myLowerBaseC = latin_tolower(baseC);
-        if (baseC != icu4cLowerBaseC) {
+        if (c <= 0x7F) continue;
+        const unsigned short icu4cLowerC = u_tolower(c);
+        const unsigned short myLowerC = latin_tolower(c);
+        if (c != icu4cLowerC) {
 #ifdef CONFIRMING_CHAR_UTILS
-            if (icu4cLowerBaseC != myLowerBaseC) {
-                fprintf(stderr, "icu4cLowerBaseC != myLowerBaseC, 0x%04X, 0x%04X\n",
-                        icu4cLowerBaseC, myLowerBaseC);
+            if (icu4cLowerC != myLowerC) {
+                fprintf(stderr, "icu4cLowerC != myLowerC, 0x%04X, 0x%04X\n", icu4cLowerC, myLowerC);
             }
 #else // CONFIRMING_CHAR_UTILS
-            printf("0x%04X, 0x%04X\n", baseC, icu4cLowerBaseC);
+            printf("0x%04X, 0x%04X\n", c, icu4cLowerC);
 #endif // CONFIRMING_CHAR_UTILS
         }
     }
@@ -77,14 +75,99 @@
  *    $
  */
 static const struct LatinCapitalSmallPair SORTED_CHAR_MAP[] = {
+    { 0x00C0, 0x00E0 },  // LATIN CAPITAL LETTER A WITH GRAVE
+    { 0x00C1, 0x00E1 },  // LATIN CAPITAL LETTER A WITH ACUTE
+    { 0x00C2, 0x00E2 },  // LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+    { 0x00C3, 0x00E3 },  // LATIN CAPITAL LETTER A WITH TILDE
+    { 0x00C4, 0x00E4 },  // LATIN CAPITAL LETTER A WITH DIAERESIS
+    { 0x00C5, 0x00E5 },  // LATIN CAPITAL LETTER A WITH RING ABOVE
     { 0x00C6, 0x00E6 },  // LATIN CAPITAL LETTER AE
+    { 0x00C7, 0x00E7 },  // LATIN CAPITAL LETTER C WITH CEDILLA
+    { 0x00C8, 0x00E8 },  // LATIN CAPITAL LETTER E WITH GRAVE
+    { 0x00C9, 0x00E9 },  // LATIN CAPITAL LETTER E WITH ACUTE
+    { 0x00CA, 0x00EA },  // LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+    { 0x00CB, 0x00EB },  // LATIN CAPITAL LETTER E WITH DIAERESIS
+    { 0x00CC, 0x00EC },  // LATIN CAPITAL LETTER I WITH GRAVE
+    { 0x00CD, 0x00ED },  // LATIN CAPITAL LETTER I WITH ACUTE
+    { 0x00CE, 0x00EE },  // LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+    { 0x00CF, 0x00EF },  // LATIN CAPITAL LETTER I WITH DIAERESIS
     { 0x00D0, 0x00F0 },  // LATIN CAPITAL LETTER ETH
+    { 0x00D1, 0x00F1 },  // LATIN CAPITAL LETTER N WITH TILDE
+    { 0x00D2, 0x00F2 },  // LATIN CAPITAL LETTER O WITH GRAVE
+    { 0x00D3, 0x00F3 },  // LATIN CAPITAL LETTER O WITH ACUTE
+    { 0x00D4, 0x00F4 },  // LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+    { 0x00D5, 0x00F5 },  // LATIN CAPITAL LETTER O WITH TILDE
+    { 0x00D6, 0x00F6 },  // LATIN CAPITAL LETTER O WITH DIAERESIS
+    { 0x00D8, 0x00F8 },  // LATIN CAPITAL LETTER O WITH STROKE
+    { 0x00D9, 0x00F9 },  // LATIN CAPITAL LETTER U WITH GRAVE
+    { 0x00DA, 0x00FA },  // LATIN CAPITAL LETTER U WITH ACUTE
+    { 0x00DB, 0x00FB },  // LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+    { 0x00DC, 0x00FC },  // LATIN CAPITAL LETTER U WITH DIAERESIS
+    { 0x00DD, 0x00FD },  // LATIN CAPITAL LETTER Y WITH ACUTE
     { 0x00DE, 0x00FE },  // LATIN CAPITAL LETTER THORN
+    { 0x0100, 0x0101 },  // LATIN CAPITAL LETTER A WITH MACRON
+    { 0x0102, 0x0103 },  // LATIN CAPITAL LETTER A WITH BREVE
+    { 0x0104, 0x0105 },  // LATIN CAPITAL LETTER A WITH OGONEK
+    { 0x0106, 0x0107 },  // LATIN CAPITAL LETTER C WITH ACUTE
+    { 0x0108, 0x0109 },  // LATIN CAPITAL LETTER C WITH CIRCUMFLEX
+    { 0x010A, 0x010B },  // LATIN CAPITAL LETTER C WITH DOT ABOVE
+    { 0x010C, 0x010D },  // LATIN CAPITAL LETTER C WITH CARON
+    { 0x010E, 0x010F },  // LATIN CAPITAL LETTER D WITH CARON
     { 0x0110, 0x0111 },  // LATIN CAPITAL LETTER D WITH STROKE
+    { 0x0112, 0x0113 },  // LATIN CAPITAL LETTER E WITH MACRON
+    { 0x0114, 0x0115 },  // LATIN CAPITAL LETTER E WITH BREVE
+    { 0x0116, 0x0117 },  // LATIN CAPITAL LETTER E WITH DOT ABOVE
+    { 0x0118, 0x0119 },  // LATIN CAPITAL LETTER E WITH OGONEK
+    { 0x011A, 0x011B },  // LATIN CAPITAL LETTER E WITH CARON
+    { 0x011C, 0x011D },  // LATIN CAPITAL LETTER G WITH CIRCUMFLEX
+    { 0x011E, 0x011F },  // LATIN CAPITAL LETTER G WITH BREVE
+    { 0x0120, 0x0121 },  // LATIN CAPITAL LETTER G WITH DOT ABOVE
+    { 0x0122, 0x0123 },  // LATIN CAPITAL LETTER G WITH CEDILLA
+    { 0x0124, 0x0125 },  // LATIN CAPITAL LETTER H WITH CIRCUMFLEX
     { 0x0126, 0x0127 },  // LATIN CAPITAL LETTER H WITH STROKE
+    { 0x0128, 0x0129 },  // LATIN CAPITAL LETTER I WITH TILDE
+    { 0x012A, 0x012B },  // LATIN CAPITAL LETTER I WITH MACRON
+    { 0x012C, 0x012D },  // LATIN CAPITAL LETTER I WITH BREVE
+    { 0x012E, 0x012F },  // LATIN CAPITAL LETTER I WITH OGONEK
+    { 0x0130, 0x0069 },  // LATIN CAPITAL LETTER I WITH DOT ABOVE
+    { 0x0132, 0x0133 },  // LATIN CAPITAL LIGATURE IJ
+    { 0x0134, 0x0135 },  // LATIN CAPITAL LETTER J WITH CIRCUMFLEX
+    { 0x0136, 0x0137 },  // LATIN CAPITAL LETTER K WITH CEDILLA
+    { 0x0139, 0x013A },  // LATIN CAPITAL LETTER L WITH ACUTE
+    { 0x013B, 0x013C },  // LATIN CAPITAL LETTER L WITH CEDILLA
+    { 0x013D, 0x013E },  // LATIN CAPITAL LETTER L WITH CARON
+    { 0x013F, 0x0140 },  // LATIN CAPITAL LETTER L WITH MIDDLE DOT
+    { 0x0141, 0x0142 },  // LATIN CAPITAL LETTER L WITH STROKE
+    { 0x0143, 0x0144 },  // LATIN CAPITAL LETTER N WITH ACUTE
+    { 0x0145, 0x0146 },  // LATIN CAPITAL LETTER N WITH CEDILLA
+    { 0x0147, 0x0148 },  // LATIN CAPITAL LETTER N WITH CARON
     { 0x014A, 0x014B },  // LATIN CAPITAL LETTER ENG
+    { 0x014C, 0x014D },  // LATIN CAPITAL LETTER O WITH MACRON
+    { 0x014E, 0x014F },  // LATIN CAPITAL LETTER O WITH BREVE
+    { 0x0150, 0x0151 },  // LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
     { 0x0152, 0x0153 },  // LATIN CAPITAL LIGATURE OE
+    { 0x0154, 0x0155 },  // LATIN CAPITAL LETTER R WITH ACUTE
+    { 0x0156, 0x0157 },  // LATIN CAPITAL LETTER R WITH CEDILLA
+    { 0x0158, 0x0159 },  // LATIN CAPITAL LETTER R WITH CARON
+    { 0x015A, 0x015B },  // LATIN CAPITAL LETTER S WITH ACUTE
+    { 0x015C, 0x015D },  // LATIN CAPITAL LETTER S WITH CIRCUMFLEX
+    { 0x015E, 0x015F },  // LATIN CAPITAL LETTER S WITH CEDILLA
+    { 0x0160, 0x0161 },  // LATIN CAPITAL LETTER S WITH CARON
+    { 0x0162, 0x0163 },  // LATIN CAPITAL LETTER T WITH CEDILLA
+    { 0x0164, 0x0165 },  // LATIN CAPITAL LETTER T WITH CARON
     { 0x0166, 0x0167 },  // LATIN CAPITAL LETTER T WITH STROKE
+    { 0x0168, 0x0169 },  // LATIN CAPITAL LETTER U WITH TILDE
+    { 0x016A, 0x016B },  // LATIN CAPITAL LETTER U WITH MACRON
+    { 0x016C, 0x016D },  // LATIN CAPITAL LETTER U WITH BREVE
+    { 0x016E, 0x016F },  // LATIN CAPITAL LETTER U WITH RING ABOVE
+    { 0x0170, 0x0171 },  // LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+    { 0x0172, 0x0173 },  // LATIN CAPITAL LETTER U WITH OGONEK
+    { 0x0174, 0x0175 },  // LATIN CAPITAL LETTER W WITH CIRCUMFLEX
+    { 0x0176, 0x0177 },  // LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
+    { 0x0178, 0x00FF },  // LATIN CAPITAL LETTER Y WITH DIAERESIS
+    { 0x0179, 0x017A },  // LATIN CAPITAL LETTER Z WITH ACUTE
+    { 0x017B, 0x017C },  // LATIN CAPITAL LETTER Z WITH DOT ABOVE
+    { 0x017D, 0x017E },  // LATIN CAPITAL LETTER Z WITH CARON
     { 0x0181, 0x0253 },  // LATIN CAPITAL LETTER B WITH HOOK
     { 0x0182, 0x0183 },  // LATIN CAPITAL LETTER B WITH TOPBAR
     { 0x0184, 0x0185 },  // LATIN CAPITAL LETTER TONE SIX
@@ -105,6 +188,7 @@
     { 0x019C, 0x026F },  // LATIN CAPITAL LETTER TURNED M
     { 0x019D, 0x0272 },  // LATIN CAPITAL LETTER N WITH LEFT HOOK
     { 0x019F, 0x0275 },  // LATIN CAPITAL LETTER O WITH MIDDLE TILDE
+    { 0x01A0, 0x01A1 },  // LATIN CAPITAL LETTER O WITH HORN
     { 0x01A2, 0x01A3 },  // LATIN CAPITAL LETTER OI
     { 0x01A4, 0x01A5 },  // LATIN CAPITAL LETTER P WITH HOOK
     { 0x01A6, 0x0280 },  // LATIN LETTER YR
@@ -112,6 +196,7 @@
     { 0x01A9, 0x0283 },  // LATIN CAPITAL LETTER ESH
     { 0x01AC, 0x01AD },  // LATIN CAPITAL LETTER T WITH HOOK
     { 0x01AE, 0x0288 },  // LATIN CAPITAL LETTER T WITH RETROFLEX HOOK
+    { 0x01AF, 0x01B0 },  // LATIN CAPITAL LETTER U WITH HORN
     { 0x01B1, 0x028A },  // LATIN CAPITAL LETTER UPSILON
     { 0x01B2, 0x028B },  // LATIN CAPITAL LETTER V WITH HOOK
     { 0x01B3, 0x01B4 },  // LATIN CAPITAL LETTER Y WITH HOOK
@@ -119,13 +204,64 @@
     { 0x01B7, 0x0292 },  // LATIN CAPITAL LETTER EZH
     { 0x01B8, 0x01B9 },  // LATIN CAPITAL LETTER EZH REVERSED
     { 0x01BC, 0x01BD },  // LATIN CAPITAL LETTER TONE FIVE
+    { 0x01C4, 0x01C6 },  // LATIN CAPITAL LETTER DZ WITH CARON
+    { 0x01C5, 0x01C6 },  // LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON
+    { 0x01C7, 0x01C9 },  // LATIN CAPITAL LETTER LJ
+    { 0x01C8, 0x01C9 },  // LATIN CAPITAL LETTER L WITH SMALL LETTER J
+    { 0x01CA, 0x01CC },  // LATIN CAPITAL LETTER NJ
+    { 0x01CB, 0x01CC },  // LATIN CAPITAL LETTER N WITH SMALL LETTER J
+    { 0x01CD, 0x01CE },  // LATIN CAPITAL LETTER A WITH CARON
+    { 0x01CF, 0x01D0 },  // LATIN CAPITAL LETTER I WITH CARON
+    { 0x01D1, 0x01D2 },  // LATIN CAPITAL LETTER O WITH CARON
+    { 0x01D3, 0x01D4 },  // LATIN CAPITAL LETTER U WITH CARON
+    { 0x01D5, 0x01D6 },  // LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON
+    { 0x01D7, 0x01D8 },  // LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE
+    { 0x01D9, 0x01DA },  // LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON
+    { 0x01DB, 0x01DC },  // LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE
+    { 0x01DE, 0x01DF },  // LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON
+    { 0x01E0, 0x01E1 },  // LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON
+    { 0x01E2, 0x01E3 },  // LATIN CAPITAL LETTER AE WITH MACRON
     { 0x01E4, 0x01E5 },  // LATIN CAPITAL LETTER G WITH STROKE
+    { 0x01E6, 0x01E7 },  // LATIN CAPITAL LETTER G WITH CARON
+    { 0x01E8, 0x01E9 },  // LATIN CAPITAL LETTER K WITH CARON
+    { 0x01EA, 0x01EB },  // LATIN CAPITAL LETTER O WITH OGONEK
+    { 0x01EC, 0x01ED },  // LATIN CAPITAL LETTER O WITH OGONEK AND MACRON
+    { 0x01EE, 0x01EF },  // LATIN CAPITAL LETTER EZH WITH CARON
+    { 0x01F1, 0x01F3 },  // LATIN CAPITAL LETTER DZ
+    { 0x01F2, 0x01F3 },  // LATIN CAPITAL LETTER D WITH SMALL LETTER Z
+    { 0x01F4, 0x01F5 },  // LATIN CAPITAL LETTER G WITH ACUTE
     { 0x01F6, 0x0195 },  // LATIN CAPITAL LETTER HWAIR
     { 0x01F7, 0x01BF },  // LATIN CAPITAL LETTER WYNN
+    { 0x01F8, 0x01F9 },  // LATIN CAPITAL LETTER N WITH GRAVE
+    { 0x01FA, 0x01FB },  // LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE
+    { 0x01FC, 0x01FD },  // LATIN CAPITAL LETTER AE WITH ACUTE
+    { 0x01FE, 0x01FF },  // LATIN CAPITAL LETTER O WITH STROKE AND ACUTE
+    { 0x0200, 0x0201 },  // LATIN CAPITAL LETTER A WITH DOUBLE GRAVE
+    { 0x0202, 0x0203 },  // LATIN CAPITAL LETTER A WITH INVERTED BREVE
+    { 0x0204, 0x0205 },  // LATIN CAPITAL LETTER E WITH DOUBLE GRAVE
+    { 0x0206, 0x0207 },  // LATIN CAPITAL LETTER E WITH INVERTED BREVE
+    { 0x0208, 0x0209 },  // LATIN CAPITAL LETTER I WITH DOUBLE GRAVE
+    { 0x020A, 0x020B },  // LATIN CAPITAL LETTER I WITH INVERTED BREVE
+    { 0x020C, 0x020D },  // LATIN CAPITAL LETTER O WITH DOUBLE GRAVE
+    { 0x020E, 0x020F },  // LATIN CAPITAL LETTER O WITH INVERTED BREVE
+    { 0x0210, 0x0211 },  // LATIN CAPITAL LETTER R WITH DOUBLE GRAVE
+    { 0x0212, 0x0213 },  // LATIN CAPITAL LETTER R WITH INVERTED BREVE
+    { 0x0214, 0x0215 },  // LATIN CAPITAL LETTER U WITH DOUBLE GRAVE
+    { 0x0216, 0x0217 },  // LATIN CAPITAL LETTER U WITH INVERTED BREVE
+    { 0x0218, 0x0219 },  // LATIN CAPITAL LETTER S WITH COMMA BELOW
+    { 0x021A, 0x021B },  // LATIN CAPITAL LETTER T WITH COMMA BELOW
     { 0x021C, 0x021D },  // LATIN CAPITAL LETTER YOGH
+    { 0x021E, 0x021F },  // LATIN CAPITAL LETTER H WITH CARON
     { 0x0220, 0x019E },  // LATIN CAPITAL LETTER N WITH LONG RIGHT LEG
     { 0x0222, 0x0223 },  // LATIN CAPITAL LETTER OU
     { 0x0224, 0x0225 },  // LATIN CAPITAL LETTER Z WITH HOOK
+    { 0x0226, 0x0227 },  // LATIN CAPITAL LETTER A WITH DOT ABOVE
+    { 0x0228, 0x0229 },  // LATIN CAPITAL LETTER E WITH CEDILLA
+    { 0x022A, 0x022B },  // LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON
+    { 0x022C, 0x022D },  // LATIN CAPITAL LETTER O WITH TILDE AND MACRON
+    { 0x022E, 0x022F },  // LATIN CAPITAL LETTER O WITH DOT ABOVE
+    { 0x0230, 0x0231 },  // LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON
+    { 0x0232, 0x0233 },  // LATIN CAPITAL LETTER Y WITH MACRON
     { 0x023A, 0x2C65 },  // LATIN CAPITAL LETTER A WITH STROKE
     { 0x023B, 0x023C },  // LATIN CAPITAL LETTER C WITH STROKE
     { 0x023D, 0x019A },  // LATIN CAPITAL LETTER L WITH BAR
@@ -142,6 +278,13 @@
     { 0x0370, 0x0371 },  // GREEK CAPITAL LETTER HETA
     { 0x0372, 0x0373 },  // GREEK CAPITAL LETTER ARCHAIC SAMPI
     { 0x0376, 0x0377 },  // GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA
+    { 0x0386, 0x03AC },  // GREEK CAPITAL LETTER ALPHA WITH TONOS
+    { 0x0388, 0x03AD },  // GREEK CAPITAL LETTER EPSILON WITH TONOS
+    { 0x0389, 0x03AE },  // GREEK CAPITAL LETTER ETA WITH TONOS
+    { 0x038A, 0x03AF },  // GREEK CAPITAL LETTER IOTA WITH TONOS
+    { 0x038C, 0x03CC },  // GREEK CAPITAL LETTER OMICRON WITH TONOS
+    { 0x038E, 0x03CD },  // GREEK CAPITAL LETTER UPSILON WITH TONOS
+    { 0x038F, 0x03CE },  // GREEK CAPITAL LETTER OMEGA WITH TONOS
     { 0x0391, 0x03B1 },  // GREEK CAPITAL LETTER ALPHA
     { 0x0392, 0x03B2 },  // GREEK CAPITAL LETTER BETA
     { 0x0393, 0x03B3 },  // GREEK CAPITAL LETTER GAMMA
@@ -166,6 +309,8 @@
     { 0x03A7, 0x03C7 },  // GREEK CAPITAL LETTER CHI
     { 0x03A8, 0x03C8 },  // GREEK CAPITAL LETTER PSI
     { 0x03A9, 0x03C9 },  // GREEK CAPITAL LETTER OMEGA
+    { 0x03AA, 0x03CA },  // GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+    { 0x03AB, 0x03CB },  // GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
     { 0x03CF, 0x03D7 },  // GREEK CAPITAL KAI SYMBOL
     { 0x03D8, 0x03D9 },  // GREEK LETTER ARCHAIC KOPPA
     { 0x03DA, 0x03DB },  // GREEK LETTER STIGMA
@@ -179,19 +324,28 @@
     { 0x03EA, 0x03EB },  // COPTIC CAPITAL LETTER GANGIA
     { 0x03EC, 0x03ED },  // COPTIC CAPITAL LETTER SHIMA
     { 0x03EE, 0x03EF },  // COPTIC CAPITAL LETTER DEI
+    { 0x03F4, 0x03B8 },  // GREEK CAPITAL THETA SYMBOL
     { 0x03F7, 0x03F8 },  // GREEK CAPITAL LETTER SHO
+    { 0x03F9, 0x03F2 },  // GREEK CAPITAL LUNATE SIGMA SYMBOL
     { 0x03FA, 0x03FB },  // GREEK CAPITAL LETTER SAN
     { 0x03FD, 0x037B },  // GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL
     { 0x03FE, 0x037C },  // GREEK CAPITAL DOTTED LUNATE SIGMA SYMBOL
     { 0x03FF, 0x037D },  // GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL
+    { 0x0400, 0x0450 },  // CYRILLIC CAPITAL LETTER IE WITH GRAVE
+    { 0x0401, 0x0451 },  // CYRILLIC CAPITAL LETTER IO
     { 0x0402, 0x0452 },  // CYRILLIC CAPITAL LETTER DJE
+    { 0x0403, 0x0453 },  // CYRILLIC CAPITAL LETTER GJE
     { 0x0404, 0x0454 },  // CYRILLIC CAPITAL LETTER UKRAINIAN IE
     { 0x0405, 0x0455 },  // CYRILLIC CAPITAL LETTER DZE
     { 0x0406, 0x0456 },  // CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+    { 0x0407, 0x0457 },  // CYRILLIC CAPITAL LETTER YI
     { 0x0408, 0x0458 },  // CYRILLIC CAPITAL LETTER JE
     { 0x0409, 0x0459 },  // CYRILLIC CAPITAL LETTER LJE
     { 0x040A, 0x045A },  // CYRILLIC CAPITAL LETTER NJE
     { 0x040B, 0x045B },  // CYRILLIC CAPITAL LETTER TSHE
+    { 0x040C, 0x045C },  // CYRILLIC CAPITAL LETTER KJE
+    { 0x040D, 0x045D },  // CYRILLIC CAPITAL LETTER I WITH GRAVE
+    { 0x040E, 0x045E },  // CYRILLIC CAPITAL LETTER SHORT U
     { 0x040F, 0x045F },  // CYRILLIC CAPITAL LETTER DZHE
     { 0x0410, 0x0430 },  // CYRILLIC CAPITAL LETTER A
     { 0x0411, 0x0431 },  // CYRILLIC CAPITAL LETTER BE
@@ -236,6 +390,7 @@
     { 0x0470, 0x0471 },  // CYRILLIC CAPITAL LETTER PSI
     { 0x0472, 0x0473 },  // CYRILLIC CAPITAL LETTER FITA
     { 0x0474, 0x0475 },  // CYRILLIC CAPITAL LETTER IZHITSA
+    { 0x0476, 0x0477 },  // CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT
     { 0x0478, 0x0479 },  // CYRILLIC CAPITAL LETTER UK
     { 0x047A, 0x047B },  // CYRILLIC CAPITAL LETTER ROUND OMEGA
     { 0x047C, 0x047D },  // CYRILLIC CAPITAL LETTER OMEGA WITH TITLO
@@ -269,17 +424,34 @@
     { 0x04BC, 0x04BD },  // CYRILLIC CAPITAL LETTER ABKHASIAN CHE
     { 0x04BE, 0x04BF },  // CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER
     { 0x04C0, 0x04CF },  // CYRILLIC LETTER PALOCHKA
+    { 0x04C1, 0x04C2 },  // CYRILLIC CAPITAL LETTER ZHE WITH BREVE
     { 0x04C3, 0x04C4 },  // CYRILLIC CAPITAL LETTER KA WITH HOOK
     { 0x04C5, 0x04C6 },  // CYRILLIC CAPITAL LETTER EL WITH TAIL
     { 0x04C7, 0x04C8 },  // CYRILLIC CAPITAL LETTER EN WITH HOOK
     { 0x04C9, 0x04CA },  // CYRILLIC CAPITAL LETTER EN WITH TAIL
     { 0x04CB, 0x04CC },  // CYRILLIC CAPITAL LETTER KHAKASSIAN CHE
     { 0x04CD, 0x04CE },  // CYRILLIC CAPITAL LETTER EM WITH TAIL
+    { 0x04D0, 0x04D1 },  // CYRILLIC CAPITAL LETTER A WITH BREVE
+    { 0x04D2, 0x04D3 },  // CYRILLIC CAPITAL LETTER A WITH DIAERESIS
     { 0x04D4, 0x04D5 },  // CYRILLIC CAPITAL LIGATURE A IE
+    { 0x04D6, 0x04D7 },  // CYRILLIC CAPITAL LETTER IE WITH BREVE
     { 0x04D8, 0x04D9 },  // CYRILLIC CAPITAL LETTER SCHWA
+    { 0x04DA, 0x04DB },  // CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS
+    { 0x04DC, 0x04DD },  // CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS
+    { 0x04DE, 0x04DF },  // CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS
     { 0x04E0, 0x04E1 },  // CYRILLIC CAPITAL LETTER ABKHASIAN DZE
+    { 0x04E2, 0x04E3 },  // CYRILLIC CAPITAL LETTER I WITH MACRON
+    { 0x04E4, 0x04E5 },  // CYRILLIC CAPITAL LETTER I WITH DIAERESIS
+    { 0x04E6, 0x04E7 },  // CYRILLIC CAPITAL LETTER O WITH DIAERESIS
     { 0x04E8, 0x04E9 },  // CYRILLIC CAPITAL LETTER BARRED O
+    { 0x04EA, 0x04EB },  // CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS
+    { 0x04EC, 0x04ED },  // CYRILLIC CAPITAL LETTER E WITH DIAERESIS
+    { 0x04EE, 0x04EF },  // CYRILLIC CAPITAL LETTER U WITH MACRON
+    { 0x04F0, 0x04F1 },  // CYRILLIC CAPITAL LETTER U WITH DIAERESIS
+    { 0x04F2, 0x04F3 },  // CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE
+    { 0x04F4, 0x04F5 },  // CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS
     { 0x04F6, 0x04F7 },  // CYRILLIC CAPITAL LETTER GHE WITH DESCENDER
+    { 0x04F8, 0x04F9 },  // CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS
     { 0x04FA, 0x04FB },  // CYRILLIC CAPITAL LETTER GHE WITH STROKE AND HOOK
     { 0x04FC, 0x04FD },  // CYRILLIC CAPITAL LETTER HA WITH HOOK
     { 0x04FE, 0x04FF },  // CYRILLIC CAPITAL LETTER HA WITH STROKE
diff --git a/native/jni/src/char_utils.h b/native/jni/src/char_utils.h
index 58d388d..b429f40 100644
--- a/native/jni/src/char_utils.h
+++ b/native/jni/src/char_utils.h
@@ -58,7 +58,8 @@
 AK_FORCE_INLINE static int toLowerCase(const int c) {
     if (isAsciiUpper(c)) {
         return toAsciiLower(c);
-    } else if (isAscii(c)) {
+    }
+    if (isAscii(c)) {
         return c;
     }
     return static_cast<int>(latin_tolower(static_cast<unsigned short>(c)));
diff --git a/native/jni/src/digraph_utils.cpp b/native/jni/src/digraph_utils.cpp
index 6a1ab02..0834426 100644
--- a/native/jni/src/digraph_utils.cpp
+++ b/native/jni/src/digraph_utils.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "char_utils.h"
 #include "binary_format.h"
 #include "defines.h"
 #include "digraph_utils.h"
@@ -120,10 +121,11 @@
 /* static */ const DigraphUtils::digraph_t *DigraphUtils::getDigraphForDigraphTypeAndCodePoint(
         const DigraphUtils::DigraphType digraphType, const int compositeGlyphCodePoint) {
     const DigraphUtils::digraph_t *digraphs = 0;
+    const int compositeGlyphLowerCodePoint = toLowerCase(compositeGlyphCodePoint);
     const int digraphsSize =
             DigraphUtils::getAllDigraphsForDictionaryAndReturnSize(digraphType, &digraphs);
     for (int i = 0; i < digraphsSize; i++) {
-        if (digraphs[i].compositeGlyph == compositeGlyphCodePoint) {
+        if (digraphs[i].compositeGlyph == compositeGlyphLowerCodePoint) {
             return &digraphs[i];
         }
     }
diff --git a/native/jni/src/suggest/core/dicnode/dic_node.h b/native/jni/src/suggest/core/dicnode/dic_node.h
index 32faae5..f8d2df4 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node.h
+++ b/native/jni/src/suggest/core/dicnode/dic_node.h
@@ -360,11 +360,6 @@
         return mDicNodeState.mDicNodeStateScoring.getCompoundDistance(languageWeight);
     }
 
-    // Note that "cost" means delta for "distance" that is weighted.
-    float getTotalPrevWordsLanguageCost() const {
-        return mDicNodeState.mDicNodeStateScoring.getTotalPrevWordsLanguageCost();
-    }
-
     // Used to commit input partially
     int getPrevWordNodePos() const {
         return mDicNodeState.mDicNodeStatePrevWord.getPrevWordNodePos();
diff --git a/native/jni/src/suggest/core/dicnode/dic_node_state_scoring.h b/native/jni/src/suggest/core/dicnode/dic_node_state_scoring.h
index 8902d31..fd9d610 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node_state_scoring.h
+++ b/native/jni/src/suggest/core/dicnode/dic_node_state_scoring.h
@@ -31,7 +31,7 @@
               mDigraphIndex(DigraphUtils::NOT_A_DIGRAPH_INDEX),
               mEditCorrectionCount(0), mProximityCorrectionCount(0),
               mNormalizedCompoundDistance(0.0f), mSpatialDistance(0.0f), mLanguageDistance(0.0f),
-              mTotalPrevWordsLanguageCost(0.0f), mRawLength(0.0f) {
+              mRawLength(0.0f) {
     }
 
     virtual ~DicNodeStateScoring() {}
@@ -42,7 +42,6 @@
         mNormalizedCompoundDistance = 0.0f;
         mSpatialDistance = 0.0f;
         mLanguageDistance = 0.0f;
-        mTotalPrevWordsLanguageCost = 0.0f;
         mRawLength = 0.0f;
         mDoubleLetterLevel = NOT_A_DOUBLE_LETTER;
         mDigraphIndex = DigraphUtils::NOT_A_DIGRAPH_INDEX;
@@ -54,7 +53,6 @@
         mNormalizedCompoundDistance = scoring->mNormalizedCompoundDistance;
         mSpatialDistance = scoring->mSpatialDistance;
         mLanguageDistance = scoring->mLanguageDistance;
-        mTotalPrevWordsLanguageCost = scoring->mTotalPrevWordsLanguageCost;
         mRawLength = scoring->mRawLength;
         mDoubleLetterLevel = scoring->mDoubleLetterLevel;
         mDigraphIndex = scoring->mDigraphIndex;
@@ -70,9 +68,6 @@
         if (isProximityCorrection) {
             ++mProximityCorrectionCount;
         }
-        if (languageCost > 0.0f) {
-            setTotalPrevWordsLanguageCost(mTotalPrevWordsLanguageCost + languageCost);
-        }
     }
 
     void addRawLength(const float rawLength) {
@@ -148,10 +143,6 @@
         }
     }
 
-    float getTotalPrevWordsLanguageCost() const {
-        return mTotalPrevWordsLanguageCost;
-    }
-
  private:
     // Caution!!!
     // Use a default copy constructor and an assign operator because shallow copies are ok
@@ -165,7 +156,6 @@
     float mNormalizedCompoundDistance;
     float mSpatialDistance;
     float mLanguageDistance;
-    float mTotalPrevWordsLanguageCost;
     float mRawLength;
 
     AK_FORCE_INLINE void addDistance(float spatialDistance, float languageDistance,
@@ -179,11 +169,6 @@
                     / static_cast<float>(max(1, totalInputIndex));
         }
     }
-
-    //TODO: remove
-    AK_FORCE_INLINE void setTotalPrevWordsLanguageCost(float totalPrevWordsLanguageCost) {
-        mTotalPrevWordsLanguageCost = totalPrevWordsLanguageCost;
-    }
 };
 } // namespace latinime
 #endif // LATINIME_DIC_NODE_STATE_SCORING_H
diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
index 0fa684f..11ccf17 100644
--- a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
+++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
@@ -35,17 +35,17 @@
 const float ScoringParams::INSERTION_COST_SAME_CHAR = 0.526f;
 const float ScoringParams::INSERTION_COST_FIRST_CHAR = 0.563f;
 const float ScoringParams::TRANSPOSITION_COST = 0.494f;
-const float ScoringParams::SPACE_SUBSTITUTION_COST = 0.239f;
+const float ScoringParams::SPACE_SUBSTITUTION_COST = 0.289f;
 const float ScoringParams::ADDITIONAL_PROXIMITY_COST = 0.380f;
 const float ScoringParams::SUBSTITUTION_COST = 0.363f;
-const float ScoringParams::COST_NEW_WORD = 0.054f;
+const float ScoringParams::COST_NEW_WORD = 0.024f;
 const float ScoringParams::COST_NEW_WORD_CAPITALIZED = 0.174f;
 const float ScoringParams::DISTANCE_WEIGHT_LANGUAGE = 1.123f;
 const float ScoringParams::COST_FIRST_LOOKAHEAD = 0.462f;
 const float ScoringParams::COST_LOOKAHEAD = 0.092f;
 const float ScoringParams::HAS_PROXIMITY_TERMINAL_COST = 0.126f;
 const float ScoringParams::HAS_EDIT_CORRECTION_TERMINAL_COST = 0.056f;
-const float ScoringParams::HAS_MULTI_WORD_TERMINAL_COST = 0.136f;
+const float ScoringParams::HAS_MULTI_WORD_TERMINAL_COST = 0.536f;
 const float ScoringParams::TYPING_BASE_OUTPUT_SCORE = 1.0f;
 const float ScoringParams::TYPING_MAX_OUTPUT_SCORE_PER_INPUT = 0.1f;
 const float ScoringParams::MAX_NORM_DISTANCE_FOR_EDIT = 0.1f;
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
index 74e4e34..34d25ae 100644
--- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
+++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
@@ -140,7 +140,7 @@
             const DicTraverseSession *const traverseSession, const DicNode *const dicNode,
             hash_map_compat<int, int16_t> *const bigramCacheMap) const {
         return DicNodeUtils::getBigramNodeImprobability(traverseSession->getOffsetDict(),
-                dicNode, bigramCacheMap);
+                dicNode, bigramCacheMap) * ScoringParams::DISTANCE_WEIGHT_LANGUAGE;
     }
 
     float getCompletionCost(const DicTraverseSession *const traverseSession,
@@ -164,13 +164,8 @@
         // because the input word shouldn't be treated as perfect
         const bool isExactMatch = !hasEditCount && !hasMultipleWords
                 && !hasProximityErrors && isSameLength;
-
-        const float totalPrevWordsLanguageCost = dicNode->getTotalPrevWordsLanguageCost();
         const float languageImprobability = isExactMatch ? 0.0f : dicNodeLanguageImprobability;
-        const float languageWeight = ScoringParams::DISTANCE_WEIGHT_LANGUAGE;
-        // TODO: Caveat: The following equation should be:
-        // totalPrevWordsLanguageCost + (languageImprobability * languageWeight);
-        return (totalPrevWordsLanguageCost + languageImprobability) * languageWeight;
+        return languageImprobability * ScoringParams::DISTANCE_WEIGHT_LANGUAGE;
     }
 
     AK_FORCE_INLINE bool needsToNormalizeCompoundDistance() const {
diff --git a/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java b/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java
index dc8837d..aacd60f 100644
--- a/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java
+++ b/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java
@@ -85,6 +85,11 @@
         public boolean endBatchEdit() {
             return true;
         }
+
+        @Override
+        public boolean finishComposingText() {
+            return true;
+        }
     }
 
     private class MockInputMethodService extends InputMethodService {
diff --git a/tests/src/com/android/inputmethod/latin/StringUtilsTests.java b/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
index 98a50b7..1e3cc8a 100644
--- a/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
@@ -215,4 +215,26 @@
         checkCapitalize("Lorem!Ipsum (dolor) Sit * Amet", "Lorem!Ipsum (Dolor) Sit * Amet",
                 " \n,.;!?*()&", Locale.ENGLISH);
     }
+
+    public void testLooksLikeURL() {
+        assertTrue(StringUtils.lastPartLooksLikeURL("http://www.google."));
+        assertFalse(StringUtils.lastPartLooksLikeURL("word wo"));
+        assertTrue(StringUtils.lastPartLooksLikeURL("/etc/foo"));
+        assertFalse(StringUtils.lastPartLooksLikeURL("left/right"));
+        assertTrue(StringUtils.lastPartLooksLikeURL("www.goo"));
+        assertTrue(StringUtils.lastPartLooksLikeURL("www."));
+        assertFalse(StringUtils.lastPartLooksLikeURL("U.S.A"));
+        assertFalse(StringUtils.lastPartLooksLikeURL("U.S.A."));
+        assertTrue(StringUtils.lastPartLooksLikeURL("rtsp://foo."));
+        assertTrue(StringUtils.lastPartLooksLikeURL("://"));
+        assertFalse(StringUtils.lastPartLooksLikeURL("abc/"));
+        assertTrue(StringUtils.lastPartLooksLikeURL("abc.def/ghi"));
+        assertFalse(StringUtils.lastPartLooksLikeURL("abc.def"));
+        // TODO: ideally this would not look like a URL, but to keep down the complexity of the
+        // code for now True is acceptable.
+        assertTrue(StringUtils.lastPartLooksLikeURL("abc./def"));
+        // TODO: ideally this would not look like a URL, but to keep down the complexity of the
+        // code for now True is acceptable.
+        assertTrue(StringUtils.lastPartLooksLikeURL(".abc/def"));
+    }
 }