Merge "Fix missing Telugu letters"
diff --git a/java/res/values/donottranslate-debug-settings.xml b/java/res/values/donottranslate-debug-settings.xml
index 199f977..cc8c1a0 100644
--- a/java/res/values/donottranslate-debug-settings.xml
+++ b/java/res/values/donottranslate-debug-settings.xml
@@ -28,8 +28,6 @@
     <string name="sliding_key_input_preview">Show slide indicator</string>
     <!-- Option summary to enable sliding key input indicator. The user can see a rubber band-like effect during sliding key input. [CHAR LIMIT=66]-->
     <string name="sliding_key_input_preview_summary">Display visual cue while sliding from Shift or Symbol keys</string>
-    <!-- Title of the settings for key long press delay [CHAR LIMIT=35] -->
-    <string name="prefs_key_longpress_timeout_settings">Key long press delay</string>
     <!-- Title of the settings for customize key popup animation parameters [CHAR LIMIT=35] -->
     <string name="prefs_customize_key_preview_animation">Customize key preview animation</string>
     <!-- Title of the settings for key popup show up animation duration (in milliseconds) [CHAR LIMIT=35] -->
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index ed05e91..54bfc51 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -361,6 +361,8 @@
     <string name="prefs_keypress_vibration_duration_settings">Keypress vibration duration</string>
     <!-- Title of the settings for keypress sound volume [CHAR LIMIT=35] -->
     <string name="prefs_keypress_sound_volume_settings">Keypress sound volume</string>
+    <!-- Title of the settings for key long press delay [CHAR LIMIT=35] -->
+    <string name="prefs_key_longpress_timeout_settings">Key long press delay</string>
 
     <!-- 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>
diff --git a/java/res/xml/prefs_screen_advanced.xml b/java/res/xml/prefs_screen_advanced.xml
index 5aefcc8..3298220 100644
--- a/java/res/xml/prefs_screen_advanced.xml
+++ b/java/res/xml/prefs_screen_advanced.xml
@@ -31,6 +31,12 @@
         android:key="pref_keypress_sound_volume"
         android:title="@string/prefs_keypress_sound_volume_settings"
         latin:maxValue="100" /> <!-- percent -->
+    <com.android.inputmethod.latin.settings.SeekBarDialogPreference
+        android:key="pref_key_longpress_timeout"
+        android:title="@string/prefs_key_longpress_timeout_settings"
+        latin:minValue="@integer/config_min_longpress_timeout"
+        latin:maxValue="@integer/config_max_longpress_timeout"
+        latin:stepValue="@integer/config_longpress_timeout_step" />
     <!-- The settings for showing setup wizard application icon shouldn't be persistent and
          the default value is added programmatically. -->
     <CheckBoxPreference
diff --git a/java/res/xml/prefs_screen_debug.xml b/java/res/xml/prefs_screen_debug.xml
index 25f7c66..486d236 100644
--- a/java/res/xml/prefs_screen_debug.xml
+++ b/java/res/xml/prefs_screen_debug.xml
@@ -46,12 +46,6 @@
         android:summary="@string/sliding_key_input_preview_summary"
         android:defaultValue="true"
         android:persistent="true" />
-    <com.android.inputmethod.latin.settings.SeekBarDialogPreference
-        android:key="pref_key_longpress_timeout"
-        android:title="@string/prefs_key_longpress_timeout_settings"
-        latin:minValue="@integer/config_min_longpress_timeout"
-        latin:maxValue="@integer/config_max_longpress_timeout"
-        latin:stepValue="@integer/config_longpress_timeout_step" />
     <CheckBoxPreference
         android:key="pref_has_custom_key_preview_animation_params"
         android:title="@string/prefs_customize_key_preview_animation"
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index e181237..e6c1384 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -28,6 +28,7 @@
 import com.android.inputmethod.latin.utils.SuggestionResults;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.Locale;
 
 /**
@@ -49,6 +50,16 @@
     private static final boolean DBG = DebugFlags.DEBUG_ENABLED;
     private final DictionaryFacilitator mDictionaryFacilitator;
 
+    private static final int MAXIMUM_AUTO_CORRECT_LENGTH_FOR_GERMAN = 12;
+    private static final HashMap<String, Integer> sLanguageToMaximumAutoCorrectionWithSpaceLength =
+            new HashMap<>();
+    static {
+        // TODO: should we add Finnish here?
+        // TODO: This should not be hardcoded here but be written in the dictionary header
+        sLanguageToMaximumAutoCorrectionWithSpaceLength.put(Locale.GERMAN.getLanguage(),
+                MAXIMUM_AUTO_CORRECT_LENGTH_FOR_GERMAN);
+    }
+
     private float mAutoCorrectionThreshold;
 
     public Suggest(final DictionaryFacilitator dictionaryFacilitator) {
@@ -168,8 +179,18 @@
             // TODO: we may want to have shortcut-only entries auto-correct in the future.
             hasAutoCorrection = false;
         } else {
-            hasAutoCorrection = AutoCorrectionUtils.suggestionExceedsAutoCorrectionThreshold(
-                    suggestionResults.first(), consideredWord, mAutoCorrectionThreshold);
+            final SuggestedWordInfo firstSuggestion = suggestionResults.first();
+            if (!AutoCorrectionUtils.suggestionExceedsAutoCorrectionThreshold(
+                    firstSuggestion, consideredWord, mAutoCorrectionThreshold)) {
+                // Score is too low for autocorrect
+                hasAutoCorrection = false;
+            } else {
+                // We have a high score, so we need to check if this suggestion is in the correct
+                // form to allow auto-correcting to it in this language. For details of how this
+                // is determined, see #isAllowedByAutoCorrectionWithSpaceFilter.
+                // TODO: this should not have its own logic here but be handled by the dictionary.
+                hasAutoCorrection = isAllowedByAutoCorrectionWithSpaceFilter(firstSuggestion);
+            }
         }
 
         if (!TextUtils.isEmpty(typedWord)) {
@@ -287,6 +308,41 @@
         return suggestionsList;
     }
 
+    /**
+     * Computes whether this suggestion should be blocked or not in this language
+     *
+     * This function implements a filter that avoids auto-correcting to suggestions that contain
+     * spaces that are above a certain language-dependent character limit. In languages like German
+     * where it's possible to concatenate many words, it often happens our dictionary does not
+     * have the longer words. In this case, we offer a lot of unhelpful suggestions that contain
+     * one or several spaces. Ideally we should understand what the user wants and display useful
+     * suggestions by improving the dictionary and possibly having some specific logic. Until
+     * that's possible we should avoid displaying unhelpful suggestions. But it's hard to tell
+     * whether a suggestion is useful or not. So at least for the time being we block
+     * auto-correction when the suggestion is long and contains a space, which should avoid the
+     * worst damage.
+     * This function is implementing that filter. If the language enforces no such limit, then it
+     * always returns true. If the suggestion contains no space, it also returns true. Otherwise,
+     * it checks the length against the language-specific limit.
+     *
+     * @param info the suggestion info
+     * @return whether it's fine to auto-correct to this.
+     */
+    private boolean isAllowedByAutoCorrectionWithSpaceFilter(final SuggestedWordInfo info) {
+        final Locale locale = info.mSourceDict.mLocale;
+        if (null == locale) {
+            return true;
+        }
+        final Integer maximumLengthForThisLanguage =
+                sLanguageToMaximumAutoCorrectionWithSpaceLength.get(locale.getLanguage());
+        if (null == maximumLengthForThisLanguage) {
+            // This language does not enforce a maximum length to auto-correction
+            return true;
+        }
+        return info.mWord.length() <= maximumLengthForThisLanguage
+                || -1 == info.mWord.indexOf(Constants.CODE_SPACE);
+    }
+
     /* package for test */ static SuggestedWordInfo getTransformedSuggestedWordInfo(
             final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase,
             final boolean isOnlyFirstCharCapitalized, final int trailingSingleQuotesCount) {
diff --git a/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java
index 3303ab0..d2c9dbb 100644
--- a/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java
@@ -113,6 +113,7 @@
 
         setupKeypressVibrationDurationSettings();
         setupKeypressSoundVolumeSettings();
+        setupKeyLongpressTimeoutSettings();
         refreshEnablingsOfKeypressSoundAndVibrationSettings();
     }
 
@@ -249,4 +250,43 @@
             }
         });
     }
+
+    private void setupKeyLongpressTimeoutSettings() {
+        final SharedPreferences prefs = getSharedPreferences();
+        final Resources res = getResources();
+        final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(
+                Settings.PREF_KEY_LONGPRESS_TIMEOUT);
+        if (pref == null) {
+            return;
+        }
+        pref.setInterface(new SeekBarDialogPreference.ValueProxy() {
+            @Override
+            public void writeValue(final int value, final String key) {
+                prefs.edit().putInt(key, value).apply();
+            }
+
+            @Override
+            public void writeDefaultValue(final String key) {
+                prefs.edit().remove(key).apply();
+            }
+
+            @Override
+            public int readValue(final String key) {
+                return Settings.readKeyLongpressTimeout(prefs, res);
+            }
+
+            @Override
+            public int readDefaultValue(final String key) {
+                return Settings.readDefaultKeyLongpressTimeout(res);
+            }
+
+            @Override
+            public String getValueText(final int value) {
+                return res.getString(R.string.abbreviation_unit_milliseconds, value);
+            }
+
+            @Override
+            public void feedbackValue(final int value) {}
+        });
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
index df0378e..768cba9 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
@@ -31,7 +31,6 @@
             "force_physical_keyboard_special_key";
     public static final String PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS =
             "pref_has_custom_key_preview_animation_params";
-    public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout";
     public static final String PREF_KEY_PREVIEW_DISMISS_DURATION =
             "pref_key_preview_dismiss_duration";
     public static final String PREF_KEY_PREVIEW_DISMISS_END_X_SCALE =
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
index e9f8d45..475f1de 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
@@ -73,7 +73,6 @@
             dictDumpPreferenceGroup.addPreference(pref);
         }
         final Resources res = getResources();
-        setupKeyLongpressTimeoutSettings();
         setupKeyPreviewAnimationDuration(DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION,
                 res.getInteger(R.integer.config_key_preview_show_up_duration));
         setupKeyPreviewAnimationDuration(DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION,
@@ -163,45 +162,6 @@
         }
     }
 
-    private void setupKeyLongpressTimeoutSettings() {
-        final SharedPreferences prefs = getSharedPreferences();
-        final Resources res = getResources();
-        final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(
-                DebugSettings.PREF_KEY_LONGPRESS_TIMEOUT);
-        if (pref == null) {
-            return;
-        }
-        pref.setInterface(new SeekBarDialogPreference.ValueProxy() {
-            @Override
-            public void writeValue(final int value, final String key) {
-                prefs.edit().putInt(key, value).apply();
-            }
-
-            @Override
-            public void writeDefaultValue(final String key) {
-                prefs.edit().remove(key).apply();
-            }
-
-            @Override
-            public int readValue(final String key) {
-                return Settings.readKeyLongpressTimeout(prefs, res);
-            }
-
-            @Override
-            public int readDefaultValue(final String key) {
-                return Settings.readDefaultKeyLongpressTimeout(res);
-            }
-
-            @Override
-            public String getValueText(final int value) {
-                return res.getString(R.string.abbreviation_unit_milliseconds, value);
-            }
-
-            @Override
-            public void feedbackValue(final int value) {}
-        });
-    }
-
     private void setupKeyPreviewAnimationScale(final String prefKey, final float defaultValue) {
         final SharedPreferences prefs = getSharedPreferences();
         final Resources res = getResources();
diff --git a/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java b/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java
index c171104..c9e9dc8 100644
--- a/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java
+++ b/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java
@@ -48,7 +48,6 @@
         DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH,
         DebugSettings.PREF_FORCE_PHYSICAL_KEYBOARD_SPECIAL_KEY,
         DebugSettings.PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS,
-        DebugSettings.PREF_KEY_LONGPRESS_TIMEOUT,
         DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION,
         DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_X_SCALE,
         DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE,
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index 103033c..391fc19 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -93,8 +93,8 @@
     public static final String PREF_GESTURE_INPUT = "gesture_input";
     public static final String PREF_VIBRATION_DURATION_SETTINGS =
             "pref_vibration_duration_settings";
-    public static final String PREF_KEYPRESS_SOUND_VOLUME =
-            "pref_keypress_sound_volume";
+    public static final String PREF_KEYPRESS_SOUND_VOLUME = "pref_keypress_sound_volume";
+    public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout";
     public static final String PREF_GESTURE_PREVIEW_TRAIL = "pref_gesture_preview_trail";
     public static final String PREF_GESTURE_FLOATING_PREVIEW_TEXT =
             "pref_gesture_floating_preview_text";
@@ -317,7 +317,7 @@
     public static int readKeyLongpressTimeout(final SharedPreferences prefs,
             final Resources res) {
         final int milliseconds = prefs.getInt(
-                DebugSettings.PREF_KEY_LONGPRESS_TIMEOUT, UNDEFINED_PREFERENCE_VALUE_INT);
+                PREF_KEY_LONGPRESS_TIMEOUT, UNDEFINED_PREFERENCE_VALUE_INT);
         return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds
                 : readDefaultKeyLongpressTimeout(res);
     }