aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/latin
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java53
-rw-r--r--java/src/com/android/inputmethod/latin/CandidateView.java38
-rw-r--r--java/src/com/android/inputmethod/latin/DebugSettings.java10
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java184
-rw-r--r--java/src/com/android/inputmethod/latin/Settings.java16
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeSwitcher.java108
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java73
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java96
-rw-r--r--java/src/com/android/inputmethod/latin/Utils.java30
9 files changed, 473 insertions, 135 deletions
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index b6035e15e..d87672c0e 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.util.Log;
+import java.io.File;
import java.util.Arrays;
/**
@@ -72,9 +73,40 @@ public class BinaryDictionary extends Dictionary {
public static BinaryDictionary initDictionary(Context context, int resId, int dicTypeId) {
synchronized (sInstance) {
sInstance.closeInternal();
- if (resId != 0) {
- sInstance.loadDictionary(context, resId);
+ try {
+ final AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId);
+ if (afd == null) {
+ Log.e(TAG, "Found the resource but it is compressed. resId=" + resId);
+ return null;
+ }
+ final String sourceDir = context.getApplicationInfo().sourceDir;
+ final File packagePath = new File(sourceDir);
+ // TODO: Come up with a way to handle a directory.
+ if (!packagePath.isFile()) {
+ Log.e(TAG, "sourceDir is not a file: " + sourceDir);
+ return null;
+ }
+ sInstance.loadDictionary(sourceDir, afd.getStartOffset(), afd.getLength());
sInstance.mDicTypeId = dicTypeId;
+ } catch (android.content.res.Resources.NotFoundException e) {
+ Log.e(TAG, "Could not find the resource. resId=" + resId);
+ return null;
+ }
+ }
+ return sInstance;
+ }
+
+ // For unit test
+ /* package */ static BinaryDictionary initDictionary(File dictionary, long startOffset,
+ long length, int dicTypeId) {
+ synchronized (sInstance) {
+ sInstance.closeInternal();
+ if (dictionary.isFile()) {
+ sInstance.loadDictionary(dictionary.getAbsolutePath(), startOffset, length);
+ sInstance.mDicTypeId = dicTypeId;
+ } else {
+ Log.e(TAG, "Could not find the file. path=" + dictionary.getAbsolutePath());
+ return null;
}
}
return sInstance;
@@ -92,22 +124,11 @@ public class BinaryDictionary extends Dictionary {
int[] inputCodes, int inputCodesLength, char[] outputChars, int[] frequencies,
int maxWordLength, int maxBigrams, int maxAlternatives);
- private final void loadDictionary(Context context, int resId) {
- try {
- final AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId);
- if (afd == null) {
- Log.e(TAG, "Found the resource but it is compressed. resId=" + resId);
- return;
- }
- mNativeDict = openNative(context.getApplicationInfo().sourceDir,
- afd.getStartOffset(), afd.getLength(),
+ private final void loadDictionary(String path, long startOffset, long length) {
+ mNativeDict = openNative(path, startOffset, length,
TYPED_LETTER_MULTIPLIER, FULL_WORD_FREQ_MULTIPLIER,
MAX_WORD_LENGTH, MAX_WORDS, MAX_ALTERNATIVES);
- mDictLength = afd.getLength();
- } catch (android.content.res.Resources.NotFoundException e) {
- Log.e(TAG, "Could not find the resource. resId=" + resId);
- return;
- }
+ mDictLength = length;
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java
index d2d1f22dd..9699ad136 100644
--- a/java/src/com/android/inputmethod/latin/CandidateView.java
+++ b/java/src/com/android/inputmethod/latin/CandidateView.java
@@ -16,8 +16,11 @@
package com.android.inputmethod.latin;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Handler;
import android.os.Message;
@@ -43,6 +46,7 @@ import android.widget.PopupWindow;
import android.widget.TextView;
import java.util.ArrayList;
+import java.util.List;
public class CandidateView extends LinearLayout implements OnClickListener, OnLongClickListener {
@@ -50,6 +54,8 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan();
private static final int MAX_SUGGESTIONS = 16;
+ private static final boolean DBG = LatinImeLogger.sDBG;
+
private final ArrayList<View> mWords = new ArrayList<View>();
private final boolean mConfigCandidateHighlightFontColorEnabled;
private final CharacterStyle mInvertedForegroundColorSpan;
@@ -175,11 +181,12 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
final SuggestedWords suggestions = mSuggestions;
clear();
final int count = suggestions.size();
- final Object[] debugInfo = suggestions.mDebugInfo;
for (int i = 0; i < count; i++) {
CharSequence word = suggestions.getWord(i);
if (word == null) continue;
final int wordLength = word.length();
+ final List<SuggestedWordInfo> suggestedWordInfoList =
+ suggestions.mSuggestedWordInfoList;
final View v = mWords.get(i);
final TextView tv = (TextView)v.findViewById(R.id.candidate_word);
@@ -209,10 +216,25 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
}
tv.setText(word);
tv.setClickable(true);
- if (debugInfo != null && i < debugInfo.length && debugInfo[i] != null
- && !TextUtils.isEmpty(debugInfo[i].toString())) {
- dv.setText(debugInfo[i].toString());
- dv.setVisibility(VISIBLE);
+
+ if (suggestedWordInfoList != null && suggestedWordInfoList.get(i) != null) {
+ final SuggestedWordInfo info = suggestedWordInfoList.get(i);
+ if (info.isPreviousSuggestedWord()) {
+ int color = tv.getCurrentTextColor();
+ tv.setTextColor(Color.argb((int)(Color.alpha(color) * 0.5f), Color.red(color),
+ Color.green(color), Color.blue(color)));
+ }
+ final String debugString = info.getDebugString();
+ if (DBG) {
+ if (TextUtils.isEmpty(debugString)) {
+ dv.setVisibility(GONE);
+ } else {
+ dv.setText(debugString);
+ dv.setVisibility(VISIBLE);
+ }
+ } else {
+ dv.setVisibility(GONE);
+ }
} else {
dv.setVisibility(GONE);
}
@@ -231,8 +253,10 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
final TextView tv = (TextView)mWords.get(1).findViewById(R.id.candidate_word);
final Spannable word = new SpannableString(autoCorrectedWord);
final int wordLength = word.length();
- word.setSpan(mInvertedBackgroundColorSpan, 0, wordLength, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
- word.setSpan(mInvertedForegroundColorSpan, 0, wordLength, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ word.setSpan(mInvertedBackgroundColorSpan, 0, wordLength,
+ Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ word.setSpan(mInvertedForegroundColorSpan, 0, wordLength,
+ Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
tv.setText(word);
mShowingAutoCorrectionInverted = true;
}
diff --git a/java/src/com/android/inputmethod/latin/DebugSettings.java b/java/src/com/android/inputmethod/latin/DebugSettings.java
index 03211f36b..2f1e7c2b8 100644
--- a/java/src/com/android/inputmethod/latin/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/DebugSettings.java
@@ -20,6 +20,7 @@ import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
+import android.os.Process;
import android.preference.CheckBoxPreference;
import android.preference.PreferenceActivity;
import android.util.Log;
@@ -30,6 +31,7 @@ public class DebugSettings extends PreferenceActivity
private static final String TAG = "DebugSettings";
private static final String DEBUG_MODE_KEY = "debug_mode";
+ private boolean mServiceNeedsRestart = false;
private CheckBoxPreference mDebugMode;
@Override
@@ -39,16 +41,24 @@ public class DebugSettings extends PreferenceActivity
SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
prefs.registerOnSharedPreferenceChangeListener(this);
+ mServiceNeedsRestart = false;
mDebugMode = (CheckBoxPreference) findPreference(DEBUG_MODE_KEY);
updateDebugMode();
}
@Override
+ protected void onStop() {
+ super.onStop();
+ if (mServiceNeedsRestart) Process.killProcess(Process.myPid());
+ }
+
+ @Override
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
if (key.equals(DEBUG_MODE_KEY)) {
if (mDebugMode != null) {
mDebugMode.setChecked(prefs.getBoolean(DEBUG_MODE_KEY, false));
updateDebugMode();
+ mServiceNeedsRestart = true;
}
}
}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index b6042c769..a55ee5246 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -38,6 +38,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.inputmethodservice.InputMethodService;
import android.media.AudioManager;
+import android.net.ConnectivityManager;
import android.os.Debug;
import android.os.Handler;
import android.os.Message;
@@ -67,6 +68,8 @@ import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
+// @@@ import android.view.inputmethod.InputMethodSubtype;
+import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
@@ -83,22 +86,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
SharedPreferences.OnSharedPreferenceChangeListener {
private static final String TAG = "LatinIME";
private static final boolean PERF_DEBUG = false;
- private static final boolean DEBUG = false;
private static final boolean TRACE = false;
+ private static boolean DEBUG = LatinImeLogger.sDBG;
private static final int DELAY_UPDATE_SUGGESTIONS = 180;
private static final int DELAY_UPDATE_OLD_SUGGESTIONS = 300;
private static final int DELAY_UPDATE_SHIFT_STATE = 300;
+ private static final int EXTENDED_TOUCHABLE_REGION_HEIGHT = 100;
// How many continuous deletes at which to start deleting at a higher speed.
private static final int DELETE_ACCELERATE_AT = 20;
// Key events coming any faster than this are long-presses.
private static final int QUICK_PRESS = 200;
- // Contextual menu positions
- private static final int POS_METHOD = 0;
- private static final int POS_SETTINGS = 1;
-
private int mSuggestionVisibility;
private static final int SUGGESTION_VISIBILILTY_SHOW_VALUE
= R.string.prefs_suggestion_visibility_show_value;
@@ -121,6 +121,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private AlertDialog mOptionsDialog;
private InputMethodManager mImm;
+ private Resources mResources;
+ private SharedPreferences mPrefs;
+ private String mInputMethodId;
private KeyboardSwitcher mKeyboardSwitcher;
private SubtypeSwitcher mSubtypeSwitcher;
private VoiceIMEConnector mVoiceConnector;
@@ -130,9 +133,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private ContactsDictionary mContactsDictionary;
private AutoDictionary mAutoDictionary;
- private Resources mResources;
- private SharedPreferences mPrefs;
-
// These variables are initialized according to the {@link EditorInfo#inputType}.
private boolean mAutoSpace;
private boolean mInputTypeNoAutoCorrect;
@@ -154,6 +154,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private boolean mPopupOn;
private boolean mAutoCap;
private boolean mQuickFixes;
+ private boolean mConfigEnableShowSubtypeSettings;
private boolean mConfigSwipeDownDismissKeyboardEnabled;
private int mConfigDelayBeforeFadeoutLanguageOnSpacebar;
private int mConfigDurationOfFadeoutLanguageOnSpacebar;
@@ -348,6 +349,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
super.onCreate();
mImm = ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE));
+ mInputMethodId = Utils.getInputMethodId(mImm, getApplicationInfo().packageName);
mSubtypeSwitcher = SubtypeSwitcher.getInstance();
mKeyboardSwitcher = KeyboardSwitcher.getInstance();
@@ -358,11 +360,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// but always use the default setting defined in the resources.
if (res.getBoolean(R.bool.config_enable_show_recorrection_option)) {
mReCorrectionEnabled = prefs.getBoolean(Settings.PREF_RECORRECTION_ENABLED,
- res.getBoolean(R.bool.default_recorrection_enabled));
+ res.getBoolean(R.bool.config_default_recorrection_enabled));
} else {
- mReCorrectionEnabled = res.getBoolean(R.bool.default_recorrection_enabled);
+ mReCorrectionEnabled = res.getBoolean(R.bool.config_default_recorrection_enabled);
}
+ mConfigEnableShowSubtypeSettings = res.getBoolean(
+ R.bool.config_enable_show_subtype_settings);
mConfigSwipeDownDismissKeyboardEnabled = res.getBoolean(
R.bool.config_swipe_down_dismiss_keyboard_enabled);
mConfigDelayBeforeFadeoutLanguageOnSpacebar = res.getInteger(
@@ -386,8 +390,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mOrientation = res.getConfiguration().orientation;
initSuggestPuncList();
- // register to receive ringer mode changes for silent mode
- IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
+ // register to receive ringer mode change and network state change.
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
+ filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(mReceiver, filter);
mVoiceConnector = VoiceIMEConnector.init(this, prefs, mHandler);
prefs.registerOnSharedPreferenceChangeListener(this);
@@ -460,6 +466,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
commitTyped(ic);
if (ic != null) ic.finishComposingText(); // For voice input
mOrientation = conf.orientation;
+ if (isShowingOptionDialog())
+ mOptionsDialog.dismiss();
}
mConfigurationChanging = true;
@@ -506,6 +514,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final KeyboardSwitcher switcher = mKeyboardSwitcher;
LatinKeyboardView inputView = switcher.getInputView();
+ if(DEBUG) {
+ Log.d(TAG, "onStartInputView: " + inputView);
+ }
// In landscape mode, this method gets called without the input view being created.
if (inputView == null) {
return;
@@ -862,6 +873,34 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (!isFullscreenMode()) {
outInsets.contentTopInsets = outInsets.visibleTopInsets;
}
+ KeyboardView inputView = mKeyboardSwitcher.getInputView();
+ // Need to set touchable region only if input view is being shown
+ if (inputView != null && mKeyboardSwitcher.isInputViewShown()) {
+ final int x = 0;
+ int y = 0;
+ final int width = inputView.getWidth();
+ int height = inputView.getHeight() + EXTENDED_TOUCHABLE_REGION_HEIGHT;
+ if (mCandidateViewContainer != null) {
+ ViewParent candidateParent = mCandidateViewContainer.getParent();
+ if (candidateParent instanceof FrameLayout) {
+ FrameLayout fl = (FrameLayout) candidateParent;
+ if (fl != null) {
+ // Check frame layout's visibility
+ if (fl.getVisibility() == View.INVISIBLE) {
+ y = fl.getHeight();
+ height += y;
+ } else if (fl.getVisibility() == View.VISIBLE) {
+ height += fl.getHeight();
+ }
+ }
+ }
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Touchable region " + x + ", " + y + ", " + width + ", " + height);
+ }
+ outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
+ outInsets.touchableRegion.set(x, y, width, height);
+ }
}
@Override
@@ -1034,7 +1073,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void onSettingsKeyPressed() {
if (!isShowingOptionDialog()) {
- if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm)) {
+ if (!mConfigEnableShowSubtypeSettings) {
+ showSubtypeSelectorAndSettings();
+ } else if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm)) {
showOptionsMenu();
} else {
launchSettings();
@@ -1420,6 +1461,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
private boolean isCandidateStripVisible() {
+ if (mCandidateView == null)
+ return false;
if (mCandidateView.isShowingAddToDictionaryHint() || TextEntryState.isCorrecting())
return true;
if (!isShowingSuggestionsStrip())
@@ -1508,7 +1551,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mKeyboardSwitcher.setPreferredLetters(nextLettersFrequencies);
boolean correctionAvailable = !mInputTypeNoAutoCorrect && !mJustReverted
- && mSuggest.hasMinimalCorrection();
+ && mSuggest.hasAutoCorrection();
final CharSequence typedWord = word.getTypedWord();
// If we're in basic correct
final boolean typedWordValid = mSuggest.isValidWord(typedWord) ||
@@ -1524,10 +1567,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Basically, we update the suggestion strip only when suggestion count > 1. However,
// there is an exception: We update the suggestion strip whenever typed word's length
- // is 1, regardless of suggestion count. Actually, in most cases, suggestion count is 1
- // when typed word's length is 1, but we do always need to clear the previous state when
- // the user starts typing a word (i.e. typed word's length == 1).
- if (typedWord.length() == 1 || builder.size() > 1
+ // is 1 or typed word is found in dictionary, regardless of suggestion count. Actually,
+ // in most cases, suggestion count is 1 when typed word's length is 1, but we do always
+ // need to clear the previous state when the user starts typing a word (i.e. typed word's
+ // length == 1).
+ if (builder.size() > 1 || typedWord.length() == 1 || typedWordValid
|| mCandidateView.isShowingAddToDictionaryHint()) {
builder.setTypedWordValid(typedWordValid).setHasMinimalSuggestion(correctionAvailable);
} else {
@@ -1542,7 +1586,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void showSuggestions(SuggestedWords suggestedWords, CharSequence typedWord) {
setSuggestions(suggestedWords);
if (suggestedWords.size() > 0) {
- if (Utils.shouldBlockedBySafetyNetForAutoCorrection(suggestedWords)) {
+ if (Utils.shouldBlockedBySafetyNetForAutoCorrection(suggestedWords, mSuggest)) {
mBestWord = typedWord;
} else if (suggestedWords.hasAutoCorrectionWord()) {
mBestWord = suggestedWords.getWord(1);
@@ -1912,7 +1956,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} else if (Settings.PREF_RECORRECTION_ENABLED.equals(key)) {
mReCorrectionEnabled = sharedPreferences.getBoolean(
Settings.PREF_RECORRECTION_ENABLED,
- mResources.getBoolean(R.bool.default_recorrection_enabled));
+ mResources.getBoolean(R.bool.config_default_recorrection_enabled));
}
}
@@ -1953,11 +1997,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
- // receive ringer mode changes to detect silent mode
+ // receive ringer mode change and network state change.
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- updateRingerMode();
+ final String action = intent.getAction();
+ if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
+ updateRingerMode();
+ } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ mSubtypeSwitcher.onNetworkStateChanged(intent);
+ }
}
};
@@ -2039,7 +2088,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void updateAutoTextEnabled() {
if (mSuggest == null) return;
- mSuggest.setAutoTextEnabled(mQuickFixes
+ mSuggest.setQuickFixesEnabled(mQuickFixes
&& SubtypeSwitcher.getInstance().isSystemLanguageSameAsInputLanguage());
}
@@ -2079,9 +2128,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// @@@ mVibrateOn = vibrator != null && vibrator.hasVibrator()
mVibrateOn = vibrator != null
&& prefs.getBoolean(Settings.PREF_VIBRATE_ON, false);
- mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON, false);
- mPopupOn = prefs.getBoolean(Settings.PREF_POPUP_ON,
- mResources.getBoolean(R.bool.config_default_popup_preview));
+ mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON,
+ mResources.getBoolean(R.bool.config_default_sound_enabled));
+
+ mPopupOn = isPopupEnabled(prefs);
mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true);
mQuickFixes = isQuickFixesEnabled(prefs);
@@ -2132,6 +2182,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mSuggest.setAutoCorrectionThreshold(autoCorrectionThreshold);
}
+ private boolean isPopupEnabled(SharedPreferences sp) {
+ final boolean showPopupOption = getResources().getBoolean(
+ R.bool.config_enable_show_popup_on_keypress_option);
+ if (!showPopupOption) return mResources.getBoolean(R.bool.config_default_popup_preview);
+ return sp.getBoolean(Settings.PREF_POPUP_ON,
+ mResources.getBoolean(R.bool.config_default_popup_preview));
+ }
+
private boolean isQuickFixesEnabled(SharedPreferences sp) {
final boolean showQuickFixesOption = mResources.getBoolean(
R.bool.config_enable_quick_fixes_option);
@@ -2179,32 +2237,70 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return mSuggestPuncs.contains(String.valueOf((char)code));
}
- private void showOptionsMenu() {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setCancelable(true);
- builder.setIcon(R.drawable.ic_dialog_keyboard);
- builder.setNegativeButton(android.R.string.cancel, null);
- CharSequence itemSettings = getString(R.string.english_ime_settings);
- CharSequence itemInputMethod = getString(R.string.selectInputMethod);
- builder.setItems(new CharSequence[] {
- itemInputMethod, itemSettings},
- new DialogInterface.OnClickListener() {
+ private void showSubtypeSelectorAndSettings() {
+ final CharSequence title = getString(R.string.english_ime_input_options);
+ final CharSequence[] items = new CharSequence[] {
+ // TODO: Should use new string "Select active input modes".
+ getString(R.string.language_selection_title),
+ getString(R.string.english_ime_settings),
+ };
+ final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface di, int position) {
+ di.dismiss();
+ switch (position) {
+ case 0:
+ Intent intent = new Intent(
+ android.provider.Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.putExtra(android.provider.Settings.EXTRA_INPUT_METHOD_ID,
+ mInputMethodId);
+ startActivity(intent);
+ break;
+ case 1:
+ launchSettings();
+ break;
+ }
+ }
+ };
+ showOptionsMenuInternal(title, items, listener);
+ }
+ private void showOptionsMenu() {
+ final CharSequence title = getString(R.string.english_ime_input_options);
+ final CharSequence[] items = new CharSequence[] {
+ getString(R.string.selectInputMethod),
+ getString(R.string.english_ime_settings),
+ };
+ final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface di, int position) {
di.dismiss();
switch (position) {
- case POS_SETTINGS:
- launchSettings();
- break;
- case POS_METHOD:
- mImm.showInputMethodPicker();
- break;
+ case 0:
+ mImm.showInputMethodPicker();
+ break;
+ case 1:
+ launchSettings();
+ break;
}
}
- });
- builder.setTitle(mResources.getString(R.string.english_ime_input_options));
+ };
+ showOptionsMenuInternal(title, items, listener);
+ }
+
+ private void showOptionsMenuInternal(CharSequence title, CharSequence[] items,
+ DialogInterface.OnClickListener listener) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setCancelable(true);
+ builder.setIcon(R.drawable.ic_dialog_keyboard);
+ builder.setNegativeButton(android.R.string.cancel, null);
+ builder.setItems(items, listener);
+ builder.setTitle(title);
mOptionsDialog = builder.create();
+ mOptionsDialog.setCanceledOnTouchOutside(true);
Window window = mOptionsDialog.getWindow();
WindowManager.LayoutParams lp = window.getAttributes();
lp.token = mKeyboardSwitcher.getInputView().getWindowToken();
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index c78e6dd07..59c68f208 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -75,6 +75,7 @@ public class Settings extends PreferenceActivity
private CheckBoxPreference mQuickFixes;
private ListPreference mVoicePreference;
private ListPreference mSettingsKeyPreference;
+ private ListPreference mShowCorrectionSuggestionsPreference;
private ListPreference mAutoCorrectionThreshold;
private CheckBoxPreference mBigramSuggestion;
private boolean mVoiceOn;
@@ -102,6 +103,8 @@ public class Settings extends PreferenceActivity
mQuickFixes = (CheckBoxPreference) findPreference(PREF_QUICK_FIXES);
mVoicePreference = (ListPreference) findPreference(PREF_VOICE_SETTINGS_KEY);
mSettingsKeyPreference = (ListPreference) findPreference(PREF_SETTINGS_KEY);
+ mShowCorrectionSuggestionsPreference =
+ (ListPreference) findPreference(PREF_SHOW_SUGGESTIONS_SETTING);
SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
prefs.registerOnSharedPreferenceChangeListener(this);
@@ -190,6 +193,7 @@ public class Settings extends PreferenceActivity
updateVoiceModeSummary();
}
updateSettingsKeySummary();
+ updateShowCorrectionSuggestionsSummary();
}
@Override
@@ -214,6 +218,7 @@ public class Settings extends PreferenceActivity
.equals(mVoiceModeOff));
updateVoiceModeSummary();
updateSettingsKeySummary();
+ updateShowCorrectionSuggestionsSummary();
}
@Override
@@ -222,7 +227,9 @@ public class Settings extends PreferenceActivity
final String action;
if (android.os.Build.VERSION.SDK_INT
>= /* android.os.Build.VERSION_CODES.HONEYCOMB */ 11) {
- action = "android.settings.INPUT_METHOD_AND_SUBTYPE_ENABLER";
+ // Refer to android.provider.Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS
+ // TODO: Can this be a constant instead of literal String constant?
+ action = "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS";
} else {
action = "com.android.inputmethod.latin.INPUT_LANGUAGE_SELECTION";
}
@@ -232,6 +239,13 @@ public class Settings extends PreferenceActivity
return false;
}
+ private void updateShowCorrectionSuggestionsSummary() {
+ mShowCorrectionSuggestionsPreference.setSummary(
+ getResources().getStringArray(R.array.prefs_suggestion_visibilities)
+ [mShowCorrectionSuggestionsPreference.findIndexOfValue(
+ mShowCorrectionSuggestionsPreference.getValue())]);
+ }
+
private void updateSettingsKeySummary() {
mSettingsKeyPreference.setSummary(
getResources().getStringArray(R.array.settings_key_modes)
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 6666c8e15..f45c2b7f8 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -18,16 +18,21 @@ package com.android.inputmethod.latin;
import com.android.inputmethod.compat.InputMethodSubtype;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
+import com.android.inputmethod.keyboard.LatinKeyboard;
+import com.android.inputmethod.keyboard.LatinKeyboardView;
import com.android.inputmethod.voice.SettingsUtil;
import com.android.inputmethod.voice.VoiceIMEConnector;
import com.android.inputmethod.voice.VoiceInput;
import android.content.Context;
+import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.os.IBinder;
import android.text.TextUtils;
import android.util.Log;
@@ -41,12 +46,14 @@ import java.util.Locale;
import java.util.Map;
public class SubtypeSwitcher {
- private static final boolean DBG = false;
+ private static boolean DBG = LatinImeLogger.sDBG;
private static final String TAG = "SubtypeSwitcher";
private static final char LOCALE_SEPARATER = '_';
private static final String KEYBOARD_MODE = "keyboard";
private static final String VOICE_MODE = "voice";
+ private static final String SUBTYPE_EXTRAVALUE_REQUIRE_NETWORK_CONNECTIVITY =
+ "requireNetworkConnectivity";
private final TextUtils.SimpleStringSplitter mLocaleSplitter =
new TextUtils.SimpleStringSplitter(LOCALE_SEPARATER);
@@ -55,17 +62,17 @@ public class SubtypeSwitcher {
private /* final */ SharedPreferences mPrefs;
private /* final */ InputMethodManager mImm;
private /* final */ Resources mResources;
+ private /* final */ ConnectivityManager mConnectivityManager;
+ private /* final */ boolean mConfigUseSpacebarLanguageSwitcher;
private final ArrayList<InputMethodSubtype> mEnabledKeyboardSubtypesOfCurrentInputMethod =
new ArrayList<InputMethodSubtype>();
private final ArrayList<String> mEnabledLanguagesOfCurrentInputMethod = new ArrayList<String>();
- private boolean mConfigUseSpacebarLanguageSwitcher;
-
/*-----------------------------------------------------------*/
// Variants which should be changed only by reload functions.
private boolean mNeedsToDisplayLanguage;
private boolean mIsSystemLanguageSameAsInputLanguage;
- private InputMethodInfo mShortcutInfo;
+ private InputMethodInfo mShortcutInputMethodInfo;
private InputMethodSubtype mShortcutSubtype;
private List<InputMethodSubtype> mAllEnabledSubtypesOfCurrentInputMethod;
private Locale mSystemLocale;
@@ -75,13 +82,14 @@ public class SubtypeSwitcher {
private VoiceInput mVoiceInput;
/*-----------------------------------------------------------*/
+ private boolean mIsNetworkConnected;
+
public static SubtypeSwitcher getInstance() {
return sInstance;
}
public static void init(LatinIME service, SharedPreferences prefs) {
- sInstance.mPrefs = prefs;
- sInstance.resetParams(service);
+ sInstance.initialize(service, prefs);
sInstance.updateAllParameters();
SubtypeLocale.init(service);
@@ -91,10 +99,13 @@ public class SubtypeSwitcher {
// Intentional empty constructor for singleton.
}
- private void resetParams(LatinIME service) {
+ private void initialize(LatinIME service, SharedPreferences prefs) {
mService = service;
+ mPrefs = prefs;
mResources = service.getResources();
mImm = (InputMethodManager) service.getSystemService(Context.INPUT_METHOD_SERVICE);
+ mConnectivityManager = (ConnectivityManager) service.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
mEnabledKeyboardSubtypesOfCurrentInputMethod.clear();
mEnabledLanguagesOfCurrentInputMethod.clear();
mSystemLocale = null;
@@ -109,6 +120,9 @@ public class SubtypeSwitcher {
R.bool.config_use_spacebar_language_switcher);
if (mConfigUseSpacebarLanguageSwitcher)
initLanguageSwitcher(service);
+
+ final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
+ mIsNetworkConnected = (info != null && info.isConnected());
}
// Update all parameters stored in SubtypeSwitcher.
@@ -163,6 +177,13 @@ public class SubtypeSwitcher {
}
private void updateShortcutIME() {
+ if (DBG) {
+ Log.d(TAG, "Update shortcut IME from : "
+ + (mShortcutInputMethodInfo == null
+ ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
+ + (mShortcutSubtype == null ? "<null>" : (mShortcutSubtype.getLocale()
+ + ", " + mShortcutSubtype.getMode())));
+ }
// TODO: Update an icon for shortcut IME
/*
Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts =
@@ -171,7 +192,7 @@ public class SubtypeSwitcher {
List<InputMethodSubtype> subtypes = shortcuts.get(imi);
// TODO: Returns the first found IMI for now. Should handle all shortcuts as
// appropriate.
- mShortcutInfo = imi;
+ mShortcutInputMethodInfo = imi;
// TODO: Pick up the first found subtype for now. Should handle all subtypes
// as appropriate.
mShortcutSubtype = subtypes.size() > 0 ? subtypes.get(0) : null;
@@ -213,6 +234,9 @@ public class SubtypeSwitcher {
}
mMode = newMode;
}
+
+ // If the old mode is voice input, we need to reset or cancel its status.
+ // We cancel its status when we change mode, while we reset otherwise.
if (isKeyboardMode()) {
if (modeChanged) {
if (VOICE_MODE.equals(oldMode) && mVoiceInput != null) {
@@ -220,19 +244,26 @@ public class SubtypeSwitcher {
}
}
if (modeChanged || languageChanged) {
+ updateShortcutIME();
mService.onRefreshKeyboard();
}
- } else if (isVoiceMode()) {
+ } else if (isVoiceMode() && mVoiceInput != null) {
+ if (VOICE_MODE.equals(oldMode)) {
+ mVoiceInput.reset();
+ }
// If needsToShowWarningDialog is true, voice input need to show warning before
// show recognition view.
if (languageChanged || modeChanged
|| VoiceIMEConnector.getInstance().needsToShowWarningDialog()) {
- if (mVoiceInput != null) {
- triggerVoiceIME();
- }
+ triggerVoiceIME();
}
} else {
Log.w(TAG, "Unknown subtype mode: " + mMode);
+ if (VOICE_MODE.equals(oldMode) && mVoiceInput != null) {
+ // We need to reset the voice input to release the resources and to reset its status
+ // as it is not the current input mode.
+ mVoiceInput.reset();
+ }
}
}
@@ -268,15 +299,15 @@ public class SubtypeSwitcher {
////////////////////////////
public void switchToShortcutIME() {
- IBinder token = mService.getWindow().getWindow().getAttributes().token;
- if (token == null || mShortcutInfo == null) {
+ final IBinder token = mService.getWindow().getWindow().getAttributes().token;
+ if (token == null || mShortcutInputMethodInfo == null) {
return;
}
// @@@ mImm.setInputMethodAndSubtype(token, mShortcutInfo.getId(), mShortcutSubtype);
}
public Drawable getShortcutIcon() {
- return getSubtypeIcon(mShortcutInfo, mShortcutSubtype);
+ return getSubtypeIcon(mShortcutInputMethodInfo, mShortcutSubtype);
}
private Drawable getSubtypeIcon(InputMethodInfo imi, InputMethodSubtype subtype) {
@@ -291,9 +322,9 @@ public class SubtypeSwitcher {
if (subtype != null) {
return pm.getDrawable(imiPackageName, subtype.getIconResId(),
imi.getServiceInfo().applicationInfo);
- } else if (imi.getSubtypes().size() > 0 && imi.getSubtypes().get(0) != null) {
+ } else if (imi.getSubtypeCount() > 0 && imi.getSubtypeAt(0) != null) {
return pm.getDrawable(imiPackageName,
- imi.getSubtypes().get(0).getIconResId(),
+ imi.getSubtypeAt(0).getIconResId(),
imi.getServiceInfo().applicationInfo);
} else {
try {
@@ -307,6 +338,38 @@ public class SubtypeSwitcher {
return null;
}
+ private static boolean contains(String[] hay, String needle) {
+ for (String element : hay) {
+ if (element.equals(needle))
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isShortcutAvailable() {
+ if (mShortcutInputMethodInfo == null)
+ return false;
+ if (mShortcutSubtype != null && contains(mShortcutSubtype.getExtraValue().split(","),
+ SUBTYPE_EXTRAVALUE_REQUIRE_NETWORK_CONNECTIVITY)) {
+ return mIsNetworkConnected;
+ }
+ return true;
+ }
+
+ public void onNetworkStateChanged(Intent intent) {
+ final boolean noConnection = intent.getBooleanExtra(
+ ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+ mIsNetworkConnected = !noConnection;
+
+ final LatinKeyboardView inputView = KeyboardSwitcher.getInstance().getInputView();
+ if (inputView != null) {
+ final LatinKeyboard keyboard = inputView.getLatinKeyboard();
+ if (keyboard != null) {
+ keyboard.updateShortcutKey(isShortcutAvailable(), inputView);
+ }
+ }
+ }
+
//////////////////////////////////
// Language Switching functions //
//////////////////////////////////
@@ -353,8 +416,15 @@ public class SubtypeSwitcher {
if (mConfigUseSpacebarLanguageSwitcher) {
return mLanguageSwitcher.getEnabledLanguages();
} else {
+ int enabledLanguageCount = mEnabledLanguagesOfCurrentInputMethod.size();
+ // Workaround for explicitly specifying the voice language
+ if (enabledLanguageCount == 1) {
+ mEnabledLanguagesOfCurrentInputMethod.add(
+ mEnabledLanguagesOfCurrentInputMethod.get(0));
+ ++enabledLanguageCount;
+ }
return mEnabledLanguagesOfCurrentInputMethod.toArray(
- new String[mEnabledLanguagesOfCurrentInputMethod.size()]);
+ new String[enabledLanguageCount]);
}
}
@@ -427,7 +497,7 @@ public class SubtypeSwitcher {
mVoiceInput = vi;
if (isVoiceMode()) {
if (DBG) {
- Log.d(TAG, "Set and call voice input.");
+ Log.d(TAG, "Set and call voice input.: " + getInputLocaleStr());
}
triggerVoiceIME();
return true;
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 24c73e8ea..c9e57d0a5 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -22,6 +22,7 @@ import android.text.TextUtils;
import android.util.Log;
import android.view.View;
+import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
@@ -64,7 +65,7 @@ public class Suggest implements Dictionary.WordCallback {
static final int LARGE_DICTIONARY_THRESHOLD = 200 * 1000;
- private static boolean DBG = LatinImeLogger.sDBG;
+ private static final boolean DBG = LatinImeLogger.sDBG;
private BinaryDictionary mMainDict;
@@ -80,7 +81,7 @@ public class Suggest implements Dictionary.WordCallback {
private static final int PREF_MAX_BIGRAMS = 60;
- private boolean mAutoTextEnabled;
+ private boolean mQuickFixesEnabled;
private double mAutoCorrectionThreshold;
private int[] mPriorities = new int[mPrefMaxSuggestions];
@@ -95,7 +96,7 @@ public class Suggest implements Dictionary.WordCallback {
private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>();
ArrayList<CharSequence> mBigramSuggestions = new ArrayList<CharSequence>();
private ArrayList<CharSequence> mStringPool = new ArrayList<CharSequence>();
- private boolean mHaveAutoCorrection;
+ private boolean mHasAutoCorrection;
private String mLowerOriginalWord;
// TODO: Remove these member variables by passing more context to addWord() callback method
@@ -109,6 +110,12 @@ public class Suggest implements Dictionary.WordCallback {
initPool();
}
+ // For unit test
+ /* package */ Suggest(File dictionary, long startOffset, long length) {
+ mMainDict = BinaryDictionary.initDictionary(dictionary, startOffset, length, DIC_MAIN);
+ initPool();
+ }
+
private void initPool() {
for (int i = 0; i < mPrefMaxSuggestions; i++) {
StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
@@ -116,8 +123,8 @@ public class Suggest implements Dictionary.WordCallback {
}
}
- public void setAutoTextEnabled(boolean enabled) {
- mAutoTextEnabled = enabled;
+ public void setQuickFixesEnabled(boolean enabled) {
+ mQuickFixesEnabled = enabled;
}
public int getCorrectionMode() {
@@ -163,6 +170,10 @@ public class Suggest implements Dictionary.WordCallback {
mAutoCorrectionThreshold = threshold;
}
+ public boolean isAggressiveAutoCorrectionMode() {
+ return (mAutoCorrectionThreshold == 0);
+ }
+
/**
* Number of suggestions to generate from the input key sequence. This has
* to be a number between 1 and 100 (inclusive).
@@ -200,7 +211,7 @@ public class Suggest implements Dictionary.WordCallback {
public SuggestedWords.Builder getSuggestedWordBuilder(View view, WordComposer wordComposer,
CharSequence prevWordForBigram) {
LatinImeLogger.onStartSuggestion(prevWordForBigram);
- mHaveAutoCorrection = false;
+ mHasAutoCorrection = false;
mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
mIsAllUpperCase = wordComposer.isAllUpperCase();
collectGarbage(mSuggestions, mPrefMaxSuggestions);
@@ -220,6 +231,7 @@ public class Suggest implements Dictionary.WordCallback {
mLowerOriginalWord = "";
}
+ double normalizedScore = Integer.MIN_VALUE;
if (wordComposer.size() == 1 && (mCorrectionMode == CORRECTION_FULL_BIGRAM
|| mCorrectionMode == CORRECTION_BASIC)) {
// At first character typed, search only the bigrams
@@ -278,7 +290,7 @@ public class Suggest implements Dictionary.WordCallback {
if (DBG) {
Log.d(TAG, "Auto corrected by CORRECTION_FULL.");
}
- mHaveAutoCorrection = true;
+ mHasAutoCorrection = true;
}
}
if (mMainDict != null) mMainDict.getWords(wordComposer, this, mNextLettersFrequencies);
@@ -286,25 +298,25 @@ public class Suggest implements Dictionary.WordCallback {
&& 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 = Utils.calcNormalizedScore(
+ normalizedScore = Utils.calcNormalizedScore(
typedWord, mSuggestions.get(0), mPriorities[0]);
- if (LatinImeLogger.sDBG) {
+ if (DBG) {
Log.d(TAG, "Normalized " + typedWord + "," + mSuggestions.get(0) + ","
- + mPriorities[0] + normalizedScore
+ + mPriorities[0] + ", " + normalizedScore
+ "(" + mAutoCorrectionThreshold + ")");
}
if (normalizedScore >= mAutoCorrectionThreshold) {
if (DBG) {
Log.d(TAG, "Auto corrected by S-threthhold.");
}
- mHaveAutoCorrection = true;
+ mHasAutoCorrection = true;
}
}
}
if (typedWord != null) {
mSuggestions.add(0, typedWord.toString());
}
- if (mAutoTextEnabled) {
+ if (mQuickFixesEnabled) {
int i = 0;
int max = 6;
// Don't autotext the suggestions from the dictionaries
@@ -342,7 +354,7 @@ public class Suggest implements Dictionary.WordCallback {
if (DBG) {
Log.d(TAG, "Auto corrected by AUTOTEXT.");
}
- mHaveAutoCorrection = true;
+ mHasAutoCorrection = true;
mSuggestions.add(i + 1, autoText);
i++;
}
@@ -350,7 +362,30 @@ public class Suggest implements Dictionary.WordCallback {
}
}
removeDupes();
- return new SuggestedWords.Builder().addWords(mSuggestions);
+ if (DBG) {
+ ArrayList<SuggestedWords.SuggestedWordInfo> frequencyInfoList =
+ new ArrayList<SuggestedWords.SuggestedWordInfo>();
+ frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("+", false));
+ final int priorityLength = mPriorities.length;
+ for (int i = 0; i < priorityLength; ++i) {
+ if (normalizedScore > 0) {
+ final String priorityThreshold = Integer.toString(mPriorities[i]) + " (" +
+ normalizedScore + ")";
+ frequencyInfoList.add(
+ new SuggestedWords.SuggestedWordInfo(priorityThreshold, false));
+ normalizedScore = 0.0;
+ } else {
+ final String priority = Integer.toString(mPriorities[i]);
+ frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo(priority, false));
+ }
+ }
+ for (int i = priorityLength; i < mSuggestions.size(); ++i) {
+ frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("--", false));
+ }
+ return new SuggestedWords.Builder().addWords(mSuggestions, frequencyInfoList);
+ } else {
+ return new SuggestedWords.Builder().addWords(mSuggestions, null);
+ }
}
public int[] getNextLettersFrequencies() {
@@ -384,16 +419,16 @@ public class Suggest implements Dictionary.WordCallback {
}
}
- public boolean hasMinimalCorrection() {
- return mHaveAutoCorrection;
+ public boolean hasAutoCorrection() {
+ return mHasAutoCorrection;
}
- private boolean compareCaseInsensitive(final String mLowerOriginalWord,
+ private static boolean compareCaseInsensitive(final String lowerOriginalWord,
final char[] word, final int offset, final int length) {
- final int originalLength = mLowerOriginalWord.length();
+ final int originalLength = lowerOriginalWord.length();
if (originalLength == length && Character.isUpperCase(word[offset])) {
for (int i = 0; i < originalLength; i++) {
- if (mLowerOriginalWord.charAt(i) != Character.toLowerCase(word[offset+i])) {
+ if (lowerOriginalWord.charAt(i) != Character.toLowerCase(word[offset+i])) {
return false;
}
}
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 0fbbcdd91..f774ce3a5 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -20,6 +20,7 @@ import android.view.inputmethod.CompletionInfo;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
public class SuggestedWords {
@@ -29,10 +30,11 @@ public class SuggestedWords {
public final boolean mIsApplicationSpecifiedCompletions;
public final boolean mTypedWordValid;
public final boolean mHasMinimalSuggestion;
- public final Object[] mDebugInfo;
+ public final List<SuggestedWordInfo> mSuggestedWordInfoList;
private SuggestedWords(List<CharSequence> words, boolean isApplicationSpecifiedCompletions,
- boolean typedWordValid, boolean hasMinamlSuggestion, Object[] debugInfo) {
+ boolean typedWordValid, boolean hasMinamlSuggestion,
+ List<SuggestedWordInfo> suggestedWordInfoList) {
if (words != null) {
mWords = words;
} else {
@@ -41,7 +43,7 @@ public class SuggestedWords {
mIsApplicationSpecifiedCompletions = isApplicationSpecifiedCompletions;
mTypedWordValid = typedWordValid;
mHasMinimalSuggestion = hasMinamlSuggestion;
- mDebugInfo = debugInfo;
+ mSuggestedWordInfoList = suggestedWordInfoList;
}
public int size() {
@@ -61,38 +63,46 @@ public class SuggestedWords {
}
public static class Builder {
- private List<CharSequence> mWords;
+ private List<CharSequence> mWords = new ArrayList<CharSequence>();
private boolean mIsCompletions;
private boolean mTypedWordValid;
private boolean mHasMinimalSuggestion;
- private Object[] mDebugInfo;
+ private List<SuggestedWordInfo> mSuggestedWordInfoList =
+ new ArrayList<SuggestedWordInfo>();
public Builder() {
// Nothing to do here.
}
- public Builder addWords(List<CharSequence> words) {
- for (final CharSequence word : words)
- addWord(word);
+ public Builder addWords(List<CharSequence> words,
+ List<SuggestedWordInfo> suggestedWordInfoList) {
+ final int N = words.size();
+ for (int i = 0; i < N; ++i) {
+ SuggestedWordInfo suggestedWordInfo = null;
+ if (suggestedWordInfoList != null) {
+ suggestedWordInfo = suggestedWordInfoList.get(i);
+ }
+ if (suggestedWordInfo == null) {
+ suggestedWordInfo = new SuggestedWordInfo();
+ }
+ addWord(words.get(i), suggestedWordInfo);
+ }
return this;
}
- public Builder setDebugInfo(Object[] debuginfo) {
- mDebugInfo = debuginfo;
- return this;
+ public Builder addWord(CharSequence word) {
+ return addWord(word, null, false);
}
- public Builder addWord(int pos, CharSequence word) {
- if (mWords == null)
- mWords = new ArrayList<CharSequence>();
- mWords.add(pos, word);
- return this;
+ public Builder addWord(CharSequence word, CharSequence debugString,
+ boolean isPreviousSuggestedWord) {
+ SuggestedWordInfo info = new SuggestedWordInfo(debugString, isPreviousSuggestedWord);
+ return addWord(word, info);
}
- public Builder addWord(CharSequence word) {
- if (mWords == null)
- mWords = new ArrayList<CharSequence>();
+ private Builder addWord(CharSequence word, SuggestedWordInfo suggestedWordInfo) {
mWords.add(word);
+ mSuggestedWordInfoList.add(suggestedWordInfo);
return this;
}
@@ -117,11 +127,20 @@ public class SuggestedWords {
// and replace it with what the user currently typed.
public Builder addTypedWordAndPreviousSuggestions(CharSequence typedWord,
SuggestedWords previousSuggestions) {
- if (mWords != null) mWords.clear();
- addWord(typedWord);
+ mWords.clear();
+ mSuggestedWordInfoList.clear();
+ final HashSet<String> alreadySeen = new HashSet<String>();
+ addWord(typedWord, null, false);
+ alreadySeen.add(typedWord.toString());
final int previousSize = previousSuggestions.size();
- for (int pos = 1; pos < previousSize; pos++)
- addWord(previousSuggestions.getWord(pos));
+ for (int pos = 1; pos < previousSize; pos++) {
+ final String prevWord = previousSuggestions.getWord(pos).toString();
+ // Filter out duplicate suggestion.
+ if (!alreadySeen.contains(prevWord)) {
+ addWord(prevWord, null, true);
+ alreadySeen.add(prevWord);
+ }
+ }
mIsCompletions = false;
mTypedWordValid = false;
mHasMinimalSuggestion = false;
@@ -130,15 +149,42 @@ public class SuggestedWords {
public SuggestedWords build() {
return new SuggestedWords(mWords, mIsCompletions, mTypedWordValid,
- mHasMinimalSuggestion, mDebugInfo);
+ mHasMinimalSuggestion, mSuggestedWordInfoList);
}
public int size() {
- return mWords == null ? 0 : mWords.size();
+ return mWords.size();
}
public CharSequence getWord(int pos) {
return mWords.get(pos);
}
}
+
+ public static class SuggestedWordInfo {
+ private final CharSequence mDebugString;
+ private final boolean mPreviousSuggestedWord;
+
+ public SuggestedWordInfo() {
+ mDebugString = "";
+ mPreviousSuggestedWord = false;
+ }
+
+ public SuggestedWordInfo(CharSequence debugString, boolean previousSuggestedWord) {
+ mDebugString = debugString;
+ mPreviousSuggestedWord = previousSuggestedWord;
+ }
+
+ public String getDebugString() {
+ if (mDebugString == null) {
+ return "";
+ } else {
+ return mDebugString.toString();
+ }
+ }
+
+ public boolean isPreviousSuggestedWord () {
+ return mPreviousSuggestedWord;
+ }
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index 160948507..d33d962c0 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -23,6 +23,7 @@ import android.os.HandlerThread;
import android.os.Process;
import android.text.format.DateUtils;
import android.util.Log;
+import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import java.io.BufferedReader;
@@ -37,6 +38,7 @@ import java.util.Date;
public class Utils {
private static final String TAG = Utils.class.getSimpleName();
+ private static final int MINIMUM_SAFETY_NET_CHAR_LENGTH = 4;
private static boolean DBG = LatinImeLogger.sDBG;
/**
@@ -98,12 +100,26 @@ public class Utils {
// || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
}
+ public static String getInputMethodId(InputMethodManager imm, String packageName) {
+ for (final InputMethodInfo imi : imm.getEnabledInputMethodList()) {
+ if (imi.getPackageName().equals(packageName))
+ return imi.getId();
+ }
+ throw new RuntimeException("Can not find input method id for " + packageName);
+ }
- public static boolean shouldBlockedBySafetyNetForAutoCorrection(SuggestedWords suggestions) {
+ public static boolean shouldBlockedBySafetyNetForAutoCorrection(SuggestedWords suggestions,
+ Suggest suggest) {
// Safety net for auto correction.
// Actually if we hit this safety net, it's actually a bug.
if (suggestions.size() <= 1 || suggestions.mTypedWordValid) return false;
+ // If user selected aggressive auto correction mode, there is no need to use the safety
+ // net.
+ if (suggest.isAggressiveAutoCorrectionMode()) return false;
CharSequence typedWord = suggestions.getWord(0);
+ // If the length of typed word is less than MINIMUM_SAFETY_NET_CHAR_LENGTH,
+ // we should not use net because relatively edit distance can be big.
+ if (typedWord.length() < MINIMUM_SAFETY_NET_CHAR_LENGTH) return false;
CharSequence candidateWord = suggestions.getWord(1);
final int typedWordLength = typedWord.length();
final int maxEditDistanceOfNativeDictionary = typedWordLength < 5 ? 2 : typedWordLength / 2;
@@ -113,8 +129,11 @@ public class Utils {
+ ", " + maxEditDistanceOfNativeDictionary);
}
if (distance > maxEditDistanceOfNativeDictionary) {
- Log.w(TAG, "(Error) The edit distance of this correction exceeds limit. "
- + "Turning off auto-correction.");
+ if (DBG) {
+ Log.d(TAG, "Safety net: before = " + typedWord + ", after = " + candidateWord);
+ Log.w(TAG, "(Error) The edit distance of this correction exceeds limit. "
+ + "Turning off auto-correction.");
+ }
return true;
} else {
return false;
@@ -260,9 +279,12 @@ public class Utils {
public static double calcNormalizedScore(CharSequence before, CharSequence after, int score) {
final int beforeLength = before.length();
final int afterLength = after.length();
+ if (beforeLength == 0 || afterLength == 0) return 0;
final int distance = editDistance(before, after);
+ // If afterLength < beforeLength, the algorithm is suggesting a word by excessive character
+ // correction.
final double maximumScore = MAX_INITIAL_SCORE
- * Math.pow(TYPED_LETTER_MULTIPLIER, beforeLength)
+ * Math.pow(TYPED_LETTER_MULTIPLIER, Math.min(beforeLength, afterLength))
* FULL_WORD_MULTIPLYER;
// add a weight based on edit distance.
// distance <= max(afterLength, beforeLength) == afterLength,