diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index 6e941baaf..5b5656d84 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -25,4 +25,14 @@
90
0
100
+
+
+
+
+ - 0.22
+
+ - 0
+
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index a4ebe4650..94fe76d54 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -86,11 +86,6 @@
Display suggested words while typing
-
- Auto-complete
-
- Spacebar and punctuation automatically insert highlighted word
-
Show settings key
@@ -112,6 +107,31 @@
- @string/settings_key_mode_always_hide_name
+
+
+ Auto-complete
+
+ Spacebar and punctuation automatically insert highlighted word
+ 0
+ 1
+ 2
+
+ - @string/auto_completion_threshold_mode_value_off
+ - @string/auto_completion_threshold_mode_value_modest
+ - @string/auto_completion_threshold_mode_value_aggeressive
+
+
+ Off
+
+ Modest
+
+ Aggressive
+
+ - @string/auto_completion_threshold_mode_off
+ - @string/auto_completion_threshold_mode_modest
+ - @string/auto_completion_threshold_mode_aggeressive
+
+
Bigram Suggestions
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index 8a971092f..e7a394529 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -97,13 +97,14 @@
android:defaultValue="true"
/>
-
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index b41c2aa23..d0c8af5c4 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -68,6 +68,7 @@ import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -94,7 +95,7 @@ public class LatinIME extends InputMethodService
private static final String PREF_AUTO_CAP = "auto_cap";
private static final String PREF_QUICK_FIXES = "quick_fixes";
private static final String PREF_SHOW_SUGGESTIONS = "show_suggestions";
- private static final String PREF_AUTO_COMPLETE = "auto_complete";
+ private static final String PREF_AUTO_COMPLETION_THRESHOLD = "auto_completion_threshold";
private static final String PREF_BIGRAM_SUGGESTIONS = "bigram_suggestion";
private static final String PREF_VOICE_MODE = "voice_mode";
@@ -445,6 +446,7 @@ public class LatinIME extends InputMethodService
int[] dictionaries = getDictionary(orig);
mSuggest = new Suggest(this, dictionaries);
+ loadAndSetAutoCompletionThreshold(sp);
updateAutoTextEnabled(saveLocale);
if (mUserDictionary != null) mUserDictionary.close();
mUserDictionary = new UserDictionary(this, mInputLocale);
@@ -2469,6 +2471,9 @@ public class LatinIME extends InputMethodService
mLocaleSupportedForVoiceInput = voiceInputSupportedLocales.contains(mInputLocale);
mShowSuggestions = sp.getBoolean(PREF_SHOW_SUGGESTIONS, true);
+ mAutoCorrectEnabled = mShowSuggestions && isAutoCorrectEnabled(sp);
+ mBigramSuggestionEnabled = mAutoCorrectEnabled && isBigramSuggestionEnabled(sp);
+ loadAndSetAutoCompletionThreshold(sp);
if (VOICE_INSTALLED) {
final String voiceMode = sp.getString(PREF_VOICE_MODE,
@@ -2483,14 +2488,61 @@ public class LatinIME extends InputMethodService
mEnableVoice = enableVoice;
mVoiceOnPrimary = voiceOnPrimary;
}
- mAutoCorrectEnabled = sp.getBoolean(PREF_AUTO_COMPLETE,
- mResources.getBoolean(R.bool.enable_autocorrect)) & mShowSuggestions;
- mBigramSuggestionEnabled = sp.getBoolean(PREF_BIGRAM_SUGGESTIONS, true) & mShowSuggestions;
updateCorrectionMode();
updateAutoTextEnabled(mResources.getConfiguration().locale);
mLanguageSwitcher.loadLocales(sp);
}
+ /**
+ * load Auto completion threshold from SharedPreferences,
+ * and modify mSuggest's threshold.
+ */
+ private void loadAndSetAutoCompletionThreshold(SharedPreferences sp) {
+ // When mSuggest is not initialized, cannnot modify mSuggest's threshold.
+ if (mSuggest == null) return;
+ // When auto completion setting is turned off, the threshold is ignored.
+ if (!isAutoCorrectEnabled(sp)) return;
+
+ final String currentAutoCompletionSetting = sp.getString(PREF_AUTO_COMPLETION_THRESHOLD,
+ mResources.getString(R.string.auto_completion_threshold_mode_value_modest));
+ final String[] autoCompletionThresholdValues = mResources.getStringArray(
+ R.array.auto_complete_threshold_values);
+ // When autoCompletionThreshold is greater than 1.0,
+ // auto completion is virtually turned off.
+ double autoCompletionThreshold = Double.MAX_VALUE;
+ try {
+ final int arrayIndex = Integer.valueOf(currentAutoCompletionSetting);
+ if (arrayIndex >= 0 && arrayIndex < autoCompletionThresholdValues.length) {
+ autoCompletionThreshold = Double.parseDouble(
+ autoCompletionThresholdValues[arrayIndex]);
+ }
+ } catch (NumberFormatException e) {
+ // Whenever the threshold settings are correct,
+ // never come here.
+ autoCompletionThreshold = Double.MAX_VALUE;
+ Log.w(TAG, "Cannot load auto completion threshold setting."
+ + " currentAutoCompletionSetting: " + currentAutoCompletionSetting
+ + ", autoCompletionThresholdValues: "
+ + Arrays.toString(autoCompletionThresholdValues));
+ }
+ // TODO: This should be refactored :
+ // setAutoCompleteThreshold should be called outside of this method.
+ mSuggest.setAutoCompleteThreshold(autoCompletionThreshold);
+ }
+
+ private boolean isAutoCorrectEnabled(SharedPreferences sp) {
+ final String currentAutoCompletionSetting = sp.getString(PREF_AUTO_COMPLETION_THRESHOLD,
+ mResources.getString(R.string.auto_completion_threshold_mode_value_modest));
+ final String autoCompletionOff = mResources.getString(
+ R.string.auto_completion_threshold_mode_value_off);
+ return !currentAutoCompletionSetting.equals(autoCompletionOff);
+ }
+
+ private boolean isBigramSuggestionEnabled(SharedPreferences sp) {
+ // TODO: Define default value instead of 'true'.
+ return sp.getBoolean(PREF_BIGRAM_SUGGESTIONS, true);
+ }
+
private void initSuggestPuncList() {
mSuggestPuncList = new ArrayList();
mSuggestPuncs = mResources.getString(R.string.suggested_punctuations);
diff --git a/java/src/com/android/inputmethod/latin/LatinIMESettings.java b/java/src/com/android/inputmethod/latin/LatinIMESettings.java
index ffff33da2..99d8a622e 100644
--- a/java/src/com/android/inputmethod/latin/LatinIMESettings.java
+++ b/java/src/com/android/inputmethod/latin/LatinIMESettings.java
@@ -43,6 +43,9 @@ public class LatinIMESettings extends PreferenceActivity
private static final String QUICK_FIXES_KEY = "quick_fixes";
private static final String PREDICTION_SETTINGS_KEY = "prediction_settings";
private static final String VOICE_SETTINGS_KEY = "voice_mode";
+ private static final String PREF_SHOW_SUGGESTIONS = "show_suggestions";
+ private static final String PREF_AUTO_COMPLETION_THRESHOLD = "auto_completion_threshold";
+ private static final String PREF_BIGRAM_SUGGESTIONS = "bigram_suggestion";
/* package */ static final String PREF_SETTINGS_KEY = "settings_key";
private static final String TAG = "LatinIMESettings";
@@ -53,6 +56,9 @@ public class LatinIMESettings extends PreferenceActivity
private CheckBoxPreference mQuickFixes;
private ListPreference mVoicePreference;
private ListPreference mSettingsKeyPreference;
+ private CheckBoxPreference mShowSuggestions;
+ private ListPreference mAutoCompletionThreshold;
+ private CheckBoxPreference mBigramSuggestion;
private boolean mVoiceOn;
private VoiceInputLogger mLogger;
@@ -60,6 +66,18 @@ public class LatinIMESettings extends PreferenceActivity
private boolean mOkClicked = false;
private String mVoiceModeOff;
+ private void ensureConsistencyOfAutoCompletionSettings() {
+ if (mShowSuggestions.isChecked()) {
+ mAutoCompletionThreshold.setEnabled(true);
+ final String autoCompletionOff = getResources().getString(
+ R.string.auto_completion_threshold_mode_value_off);
+ final String currentSetting = mAutoCompletionThreshold.getValue();
+ mBigramSuggestion.setEnabled(!currentSetting.equals(autoCompletionOff));
+ } else {
+ mAutoCompletionThreshold.setEnabled(false);
+ mBigramSuggestion.setEnabled(false);
+ }
+ }
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -73,6 +91,11 @@ public class LatinIMESettings extends PreferenceActivity
mVoiceModeOff = getString(R.string.voice_mode_off);
mVoiceOn = !(prefs.getString(VOICE_SETTINGS_KEY, mVoiceModeOff).equals(mVoiceModeOff));
mLogger = VoiceInputLogger.getLogger(this);
+
+ mShowSuggestions = (CheckBoxPreference) findPreference(PREF_SHOW_SUGGESTIONS);
+ mAutoCompletionThreshold = (ListPreference) findPreference(PREF_AUTO_COMPLETION_THRESHOLD);
+ mBigramSuggestion = (CheckBoxPreference) findPreference(PREF_BIGRAM_SUGGESTIONS);
+ ensureConsistencyOfAutoCompletionSettings();
}
@Override
@@ -108,6 +131,7 @@ public class LatinIMESettings extends PreferenceActivity
showVoiceConfirmation();
}
}
+ ensureConsistencyOfAutoCompletionSettings();
mVoiceOn = !(prefs.getString(VOICE_SETTINGS_KEY, mVoiceModeOff).equals(mVoiceModeOff));
updateVoiceModeSummary();
updateSettingsKeySummary();
diff --git a/java/src/com/android/inputmethod/latin/LatinIMEUtil.java b/java/src/com/android/inputmethod/latin/LatinIMEUtil.java
index 85ecaee50..d93639063 100644
--- a/java/src/com/android/inputmethod/latin/LatinIMEUtil.java
+++ b/java/src/com/android/inputmethod/latin/LatinIMEUtil.java
@@ -168,4 +168,58 @@ public class LatinIMEUtil {
mLength = 0;
}
}
+
+ public static int editDistance(CharSequence s, CharSequence t) {
+ if (s == null || t == null) {
+ throw new IllegalArgumentException("editDistance: Arguments should not be null.");
+ }
+ final int sl = s.length();
+ final int tl = t.length();
+ int[][] dp = new int [sl + 1][tl + 1];
+ for (int i = 0; i <= sl; i++) {
+ dp[i][0] = i;
+ }
+ for (int j = 0; j <= tl; j++) {
+ dp[0][j] = j;
+ }
+ for (int i = 0; i < sl; ++i) {
+ for (int j = 0; j < tl; ++j) {
+ if (s.charAt(i) == t.charAt(j)) {
+ dp[i + 1][j + 1] = dp[i][j];
+ } else {
+ dp[i + 1][j + 1] = 1 + Math.min(dp[i][j],
+ Math.min(dp[i + 1][j], dp[i][j + 1]));
+ }
+ }
+ }
+ return dp[sl][tl];
+ }
+
+ // In dictionary.cpp, getSuggestion() method,
+ // suggestion scores are computed using the below formula.
+ // original score (called 'frequency')
+ // := pow(mTypedLetterMultiplier (this is defined 2),
+ // (the number of matched characters between typed word and suggested word))
+ // * (individual word's score which defined in the unigram dictionary,
+ // and this score is defined in range [0, 255].)
+ // * (when before.length() == after.length(),
+ // mFullWordMultiplier (this is defined 2))
+ // So, maximum original score is pow(2, before.length()) * 255 * 2
+ // So, we can normalize original score by dividing this value.
+ private static final int MAX_INITIAL_SCORE = 255;
+ private static final int TYPED_LETTER_MULTIPLIER = 2;
+ private static final int FULL_WORD_MULTIPLYER = 2;
+ public static double calcNormalizedScore(CharSequence before, CharSequence after, int score) {
+ final int beforeLength = before.length();
+ final int afterLength = after.length();
+ final int distance = editDistance(before, after);
+ final double maximumScore = MAX_INITIAL_SCORE
+ * Math.pow(TYPED_LETTER_MULTIPLIER, beforeLength)
+ * FULL_WORD_MULTIPLYER;
+ // add a weight based on edit distance.
+ // distance <= max(afterLength, beforeLength) == afterLength,
+ // so, 0 <= distance / afterLength <= 1
+ final double weight = 1.0 - (double) distance / afterLength;
+ return (score / maximumScore) * weight;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 3b898941f..01782339f 100755
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -81,6 +81,7 @@ public class Suggest implements Dictionary.WordCallback {
private boolean mAutoTextEnabled;
+ private double mAutoCompleteThreshold;
private int[] mPriorities = new int[mPrefMaxSuggestions];
private int[] mBigramPriorities = new int[PREF_MAX_BIGRAMS];
@@ -163,6 +164,10 @@ public class Suggest implements Dictionary.WordCallback {
mUserBigramDictionary = userBigramDictionary;
}
+ public void setAutoCompleteThreshold(double threshold) {
+ mAutoCompleteThreshold = threshold;
+ }
+
/**
* Number of suggestions to generate from the input key sequence. This has
* to be a number between 1 and 100 (inclusive).
@@ -301,8 +306,14 @@ public class Suggest implements Dictionary.WordCallback {
}
mMainDict.getWords(wordComposer, this, mNextLettersFrequencies);
if ((mCorrectionMode == CORRECTION_FULL || mCorrectionMode == CORRECTION_FULL_BIGRAM)
- && mSuggestions.size() > 0) {
- mHaveCorrection = true;
+ && mSuggestions.size() > 0 && mPriorities.length > 0) {
+ // TODO: when the normalized score of the first suggestion is nearly equals to
+ // the normalized score of the second suggestion, behave less aggressive.
+ final double normalizedScore = LatinIMEUtil.calcNormalizedScore(
+ mOriginalWord, mSuggestions.get(0), mPriorities[0]);
+ if (normalizedScore >= mAutoCompleteThreshold) {
+ mHaveCorrection = true;
+ }
}
}
if (mOriginalWord != null) {
diff --git a/tests/src/com/android/inputmethod/latin/EditDistanceTests.java b/tests/src/com/android/inputmethod/latin/EditDistanceTests.java
new file mode 100644
index 000000000..a9ed89df7
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/EditDistanceTests.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.test.AndroidTestCase;
+
+public class EditDistanceTests extends AndroidTestCase {
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /*
+ * dist(kitten, sitting) == 3
+ *
+ * kitten-
+ * .|||.|
+ * sitting
+ */
+ public void testExample1() {
+ final int dist = LatinIMEUtil.editDistance("kitten", "sitting");
+ assertEquals("edit distance between 'kitten' and 'sitting' is 3",
+ 3, dist);
+ }
+
+ /*
+ * dist(Sunday, Saturday) == 3
+ *
+ * Saturday
+ * | |.|||
+ * S--unday
+ */
+ public void testExample2() {
+ final int dist = LatinIMEUtil.editDistance("Saturday", "Sunday");
+ assertEquals("edit distance between 'Saturday' and 'Sunday' is 3",
+ 3, dist);
+ }
+
+ public void testBothEmpty() {
+ final int dist = LatinIMEUtil.editDistance("", "");
+ assertEquals("when both string are empty, no edits are needed",
+ 0, dist);
+ }
+
+ public void testFirstArgIsEmpty() {
+ final int dist = LatinIMEUtil.editDistance("", "aaaa");
+ assertEquals("when only one string of the arguments is empty,"
+ + " the edit distance is the length of the other.",
+ 4, dist);
+ }
+
+ public void testSecoondArgIsEmpty() {
+ final int dist = LatinIMEUtil.editDistance("aaaa", "");
+ assertEquals("when only one string of the arguments is empty,"
+ + " the edit distance is the length of the other.",
+ 4, dist);
+ }
+
+ public void testSameStrings() {
+ final String arg1 = "The quick brown fox jumps over the lazy dog.";
+ final String arg2 = "The quick brown fox jumps over the lazy dog.";
+ final int dist = LatinIMEUtil.editDistance(arg1, arg2);
+ assertEquals("when same strings are passed, distance equals 0.",
+ 0, dist);
+ }
+
+ public void testSameReference() {
+ final String arg = "The quick brown fox jumps over the lazy dog.";
+ final int dist = LatinIMEUtil.editDistance(arg, arg);
+ assertEquals("when same string references are passed, the distance equals 0.",
+ 0, dist);
+ }
+
+ public void testNullArg() {
+ try {
+ LatinIMEUtil.editDistance(null, "aaa");
+ fail("IllegalArgumentException should be thrown.");
+ } catch (Exception e) {
+ assertTrue(e instanceof IllegalArgumentException);
+ }
+ try {
+ LatinIMEUtil.editDistance("aaa", null);
+ fail("IllegalArgumentException should be thrown.");
+ } catch (Exception e) {
+ assertTrue(e instanceof IllegalArgumentException);
+ }
+ }
+}