aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/latin/LatinIME.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod/latin/LatinIME.java')
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java2942
1 files changed, 1228 insertions, 1714 deletions
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 4f3d3ba9f..9c6465dd2 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -25,18 +25,17 @@ import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
import android.inputmethodservice.InputMethodService;
-import android.inputmethodservice.Keyboard;
import android.media.AudioManager;
+import android.net.ConnectivityManager;
import android.os.Debug;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
import android.os.SystemClock;
import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
-import android.speech.SpeechRecognizer;
-import android.text.ClipboardManager;
+import android.text.InputType;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -44,7 +43,6 @@ import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
@@ -53,441 +51,422 @@ import android.view.WindowManager;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
-import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.LinearLayout;
-import com.android.inputmethod.latin.LatinIMEUtil.RingCharBuffer;
-import com.android.inputmethod.voice.FieldContext;
-import com.android.inputmethod.voice.SettingsUtil;
-import com.android.inputmethod.voice.VoiceInput;
-
-import org.xmlpull.v1.XmlPullParserException;
+import com.android.inputmethod.accessibility.AccessibilityUtils;
+import com.android.inputmethod.compat.CompatUtils;
+import com.android.inputmethod.compat.EditorInfoCompatUtils;
+import com.android.inputmethod.compat.InputConnectionCompatUtils;
+import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.compat.InputMethodServiceCompatWrapper;
+import com.android.inputmethod.compat.InputTypeCompatUtils;
+import com.android.inputmethod.compat.SuggestionSpanUtils;
+import com.android.inputmethod.deprecated.LanguageSwitcherProxy;
+import com.android.inputmethod.deprecated.VoiceProxy;
+import com.android.inputmethod.deprecated.recorrection.Recorrection;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardActionListener;
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
+import com.android.inputmethod.keyboard.KeyboardView;
+import com.android.inputmethod.keyboard.LatinKeyboard;
+import com.android.inputmethod.keyboard.LatinKeyboardView;
import java.io.FileDescriptor;
-import java.io.IOException;
import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
import java.util.Locale;
-import java.util.Map;
/**
* Input method implementation for Qwerty'ish keyboard.
*/
-public class LatinIME extends InputMethodService
- implements LatinKeyboardBaseView.OnKeyboardActionListener,
- VoiceInput.UiListener,
- SharedPreferences.OnSharedPreferenceChangeListener {
- private static final String TAG = "LatinIME";
+public class LatinIME extends InputMethodServiceCompatWrapper implements KeyboardActionListener,
+ CandidateView.Listener {
+ private static final String TAG = LatinIME.class.getSimpleName();
private static final boolean PERF_DEBUG = false;
- static final boolean DEBUG = false;
- static final boolean TRACE = false;
- static final boolean VOICE_INSTALLED = true;
- static final boolean ENABLE_VOICE_BUTTON = true;
-
- private static final String PREF_VIBRATE_ON = "vibrate_on";
- private static final String PREF_SOUND_ON = "sound_on";
- private static final String PREF_POPUP_ON = "popup_on";
- 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_BIGRAM_SUGGESTIONS = "bigram_suggestion";
- private static final String PREF_VOICE_MODE = "voice_mode";
-
- // Whether or not the user has used voice input before (and thus, whether to show the
- // first-run warning dialog or not).
- private static final String PREF_HAS_USED_VOICE_INPUT = "has_used_voice_input";
-
- // Whether or not the user has used voice input from an unsupported locale UI before.
- // For example, the user has a Chinese UI but activates voice input.
- private static final String PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE =
- "has_used_voice_input_unsupported_locale";
-
- // A list of locales which are supported by default for voice input, unless we get a
- // different list from Gservices.
- public static final String DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES =
- "en " +
- "en_US " +
- "en_GB " +
- "en_AU " +
- "en_CA " +
- "en_IE " +
- "en_IN " +
- "en_NZ " +
- "en_SG " +
- "en_ZA ";
-
- // The private IME option used to indicate that no microphone should be shown for a
- // given text field. For instance this is specified by the search dialog when the
- // dialog is already showing a voice search button.
- private static final String IME_OPTION_NO_MICROPHONE = "nm";
-
- public static final String PREF_SELECTED_LANGUAGES = "selected_languages";
- public static final String PREF_INPUT_LANGUAGE = "input_language";
- private static final String PREF_RECORRECTION_ENABLED = "recorrection_enabled";
-
- private static final int MSG_UPDATE_SUGGESTIONS = 0;
- private static final int MSG_START_TUTORIAL = 1;
- private static final int MSG_UPDATE_SHIFT_STATE = 2;
- private static final int MSG_VOICE_RESULTS = 3;
- private static final int MSG_UPDATE_OLD_SUGGESTIONS = 4;
+ private static final boolean TRACE = false;
+ private static boolean DEBUG;
+
+ /**
+ * The private IME option used to indicate that no microphone should be
+ * shown for a given text field. For instance, this is specified by the
+ * search dialog when the dialog is already showing a voice search button.
+ *
+ * @deprecated Use {@link LatinIME#IME_OPTION_NO_MICROPHONE} with package name prefixed.
+ */
+ @SuppressWarnings("dep-ann")
+ public static final String IME_OPTION_NO_MICROPHONE_COMPAT = "nm";
+
+ /**
+ * The private IME option used to indicate that no microphone should be
+ * shown for a given text field. For instance, this is specified by the
+ * search dialog when the dialog is already showing a voice search button.
+ */
+ public static final String IME_OPTION_NO_MICROPHONE = "noMicrophoneKey";
+
+ /**
+ * The private IME option used to indicate that no settings key should be
+ * shown for a given text field.
+ */
+ public static final String IME_OPTION_NO_SETTINGS_KEY = "noSettingsKey";
+
+ 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;
- static final int KEYCODE_ENTER = '\n';
- static final int KEYCODE_SPACE = ' ';
- static final int KEYCODE_PERIOD = '.';
+ /**
+ * The name of the scheme used by the Package Manager to warn of a new package installation,
+ * replacement or removal.
+ */
+ private static final String SCHEME_PACKAGE = "package";
+
+ private int mSuggestionVisibility;
+ private static final int SUGGESTION_VISIBILILTY_SHOW_VALUE
+ = R.string.prefs_suggestion_visibility_show_value;
+ private static final int SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE
+ = R.string.prefs_suggestion_visibility_show_only_portrait_value;
+ private static final int SUGGESTION_VISIBILILTY_HIDE_VALUE
+ = R.string.prefs_suggestion_visibility_hide_value;
+
+ private static final int[] SUGGESTION_VISIBILITY_VALUE_ARRAY = new int[] {
+ SUGGESTION_VISIBILILTY_SHOW_VALUE,
+ SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE,
+ SUGGESTION_VISIBILILTY_HIDE_VALUE
+ };
- // Contextual menu positions
- private static final int POS_METHOD = 0;
- private static final int POS_SETTINGS = 1;
+ private Settings.Values mSettingsValues;
- //private LatinKeyboardView mInputView;
- private LinearLayout mCandidateViewContainer;
+ private View mCandidateViewContainer;
+ private int mCandidateStripHeight;
private CandidateView mCandidateView;
private Suggest mSuggest;
- private CompletionInfo[] mCompletions;
+ private CompletionInfo[] mApplicationSpecifiedCompletions;
private AlertDialog mOptionsDialog;
- private AlertDialog mVoiceWarningDialog;
- /* package */ KeyboardSwitcher mKeyboardSwitcher;
+ private InputMethodManagerCompatWrapper mImm;
+ private Resources mResources;
+ private SharedPreferences mPrefs;
+ private String mInputMethodId;
+ private KeyboardSwitcher mKeyboardSwitcher;
+ private SubtypeSwitcher mSubtypeSwitcher;
+ private VoiceProxy mVoiceProxy;
+ private Recorrection mRecorrection;
private UserDictionary mUserDictionary;
private UserBigramDictionary mUserBigramDictionary;
- private ContactsDictionary mContactsDictionary;
private AutoDictionary mAutoDictionary;
- private Hints mHints;
-
- private Resources mResources;
-
- private String mInputLocale;
- private String mSystemLocale;
- private LanguageSwitcher mLanguageSwitcher;
+ // TODO: Create an inner class to group options and pseudo-options to improve readability.
+ // These variables are initialized according to the {@link EditorInfo#inputType}.
+ private boolean mShouldInsertMagicSpace;
+ private boolean mInputTypeNoAutoCorrect;
+ private boolean mIsSettingsSuggestionStripOn;
+ private boolean mApplicationSpecifiedCompletionOn;
- private StringBuilder mComposing = new StringBuilder();
+ private final StringBuilder mComposing = new StringBuilder();
private WordComposer mWord = new WordComposer();
- private int mCommittedLength;
- private boolean mPredicting;
- private boolean mRecognizing;
- private boolean mAfterVoiceInput;
- private boolean mImmediatelyAfterVoiceInput;
- private boolean mShowingVoiceSuggestions;
- private boolean mVoiceInputHighlighted;
- private boolean mEnableVoiceButton;
private CharSequence mBestWord;
- private boolean mPredictionOn;
- private boolean mCompletionOn;
+ private boolean mHasUncommittedTypedChars;
private boolean mHasDictionary;
- private boolean mAutoSpace;
- private boolean mJustAddedAutoSpace;
- private boolean mAutoCorrectEnabled;
- private boolean mReCorrectionEnabled;
- // Bigram Suggestion is disabled in this version.
- private final boolean mBigramSuggestionEnabled = false;
- private boolean mAutoCorrectOn;
- // TODO move this state variable outside LatinIME
- private boolean mCapsLock;
- private boolean mPasswordText;
- private boolean mVibrateOn;
- private boolean mSoundOn;
- private boolean mPopupOn;
- private boolean mAutoCap;
- private boolean mQuickFixes;
- private boolean mHasUsedVoiceInput;
- private boolean mHasUsedVoiceInputUnsupportedLocale;
- private boolean mLocaleSupportedForVoiceInput;
- private boolean mShowSuggestions;
- private boolean mIsShowingHint;
- private int mCorrectionMode;
- private boolean mEnableVoice = true;
- private boolean mVoiceOnPrimary;
- private int mOrientation;
- private List<CharSequence> mSuggestPuncList;
+ // Magic space: a space that should disappear on space/apostrophe insertion, move after the
+ // punctuation on punctuation insertion, and become a real space on alpha char insertion.
+ private boolean mJustAddedMagicSpace; // This indicates whether the last char is a magic space.
+ // This indicates whether the last keypress resulted in processing of double space replacement
+ // with period-space.
+ private boolean mJustReplacedDoubleSpace;
+
+ private int mCorrectionMode;
+ private int mCommittedLength;
+ private int mOrientation;
// Keep track of the last selection range to decide if we need to show word alternatives
- private int mLastSelectionStart;
- private int mLastSelectionEnd;
-
- // Input type is such that we should not auto-correct
- private boolean mInputTypeNoAutoCorrect;
+ private int mLastSelectionStart;
+ private int mLastSelectionEnd;
- // Indicates whether the suggestion strip is to be on in landscape
- private boolean mJustAccepted;
- private CharSequence mJustRevertedSeparator;
+ // Whether we are expecting an onUpdateSelection event to fire. If it does when we don't
+ // "expect" it, it means the user actually moved the cursor.
+ private boolean mExpectingUpdateSelection;
private int mDeleteCount;
private long mLastKeyTime;
- // Modifier keys state
- private ModifierKeyState mShiftKeyState = new ModifierKeyState();
- private ModifierKeyState mSymbolKeyState = new ModifierKeyState();
-
- private Tutorial mTutorial;
-
private AudioManager mAudioManager;
// Align sound effect volume on music volume
- private final float FX_VOLUME = -1.0f;
- private boolean mSilentMode;
-
- /* package */ String mWordSeparators;
- private String mSentenceSeparators;
- private String mSuggestPuncs;
- private VoiceInput mVoiceInput;
- private VoiceResults mVoiceResults = new VoiceResults();
+ private static final float FX_VOLUME = -1.0f;
+ private boolean mSilentModeOn; // System-wide current configuration
+
+ // TODO: Move this flag to VoiceProxy
private boolean mConfigurationChanging;
+ // Object for reacting to adding/removing a dictionary pack.
+ private BroadcastReceiver mDictionaryPackInstallReceiver =
+ new DictionaryPackInstallBroadcastReceiver(this);
+
// Keeps track of most recently inserted text (multi-character key) for reverting
private CharSequence mEnteredText;
- private boolean mRefreshKeyboardRequired;
- // For each word, a list of potential replacements, usually from voice.
- private Map<String, List<CharSequence>> mWordToSuggestions =
- new HashMap<String, List<CharSequence>>();
- private ArrayList<WordAlternatives> mWordHistory = new ArrayList<WordAlternatives>();
+ public final UIHandler mHandler = new UIHandler();
- private class VoiceResults {
- List<String> candidates;
- Map<String, List<CharSequence>> alternatives;
- }
-
- public abstract static class WordAlternatives {
- protected CharSequence mChosenWord;
+ public class UIHandler extends Handler {
+ private static final int MSG_UPDATE_SUGGESTIONS = 0;
+ private static final int MSG_UPDATE_OLD_SUGGESTIONS = 1;
+ private static final int MSG_UPDATE_SHIFT_STATE = 2;
+ private static final int MSG_VOICE_RESULTS = 3;
+ private static final int MSG_FADEOUT_LANGUAGE_ON_SPACEBAR = 4;
+ private static final int MSG_DISMISS_LANGUAGE_ON_SPACEBAR = 5;
+ private static final int MSG_SPACE_TYPED = 6;
+ private static final int MSG_SET_BIGRAM_PREDICTIONS = 7;
- public WordAlternatives() {
- // Nothing
+ @Override
+ public void handleMessage(Message msg) {
+ final KeyboardSwitcher switcher = mKeyboardSwitcher;
+ final LatinKeyboardView inputView = switcher.getKeyboardView();
+ switch (msg.what) {
+ case MSG_UPDATE_SUGGESTIONS:
+ updateSuggestions();
+ break;
+ case MSG_UPDATE_OLD_SUGGESTIONS:
+ mRecorrection.fetchAndDisplayRecorrectionSuggestions(mVoiceProxy, mCandidateView,
+ mSuggest, mKeyboardSwitcher, mWord, mHasUncommittedTypedChars,
+ mLastSelectionStart, mLastSelectionEnd, mSettingsValues.mWordSeparators);
+ break;
+ case MSG_UPDATE_SHIFT_STATE:
+ switcher.updateShiftState();
+ break;
+ case MSG_SET_BIGRAM_PREDICTIONS:
+ updateBigramPredictions();
+ break;
+ case MSG_VOICE_RESULTS:
+ mVoiceProxy.handleVoiceResults(preferCapitalization()
+ || (switcher.isAlphabetMode() && switcher.isShiftedOrShiftLocked()));
+ break;
+ case MSG_FADEOUT_LANGUAGE_ON_SPACEBAR:
+ if (inputView != null) {
+ inputView.setSpacebarTextFadeFactor(
+ (1.0f + mSettingsValues.mFinalFadeoutFactorOfLanguageOnSpacebar) / 2,
+ (LatinKeyboard)msg.obj);
+ }
+ sendMessageDelayed(obtainMessage(MSG_DISMISS_LANGUAGE_ON_SPACEBAR, msg.obj),
+ mSettingsValues.mDurationOfFadeoutLanguageOnSpacebar);
+ break;
+ case MSG_DISMISS_LANGUAGE_ON_SPACEBAR:
+ if (inputView != null) {
+ inputView.setSpacebarTextFadeFactor(
+ mSettingsValues.mFinalFadeoutFactorOfLanguageOnSpacebar,
+ (LatinKeyboard)msg.obj);
+ }
+ break;
+ }
}
- public WordAlternatives(CharSequence chosenWord) {
- mChosenWord = chosenWord;
+ public void postUpdateSuggestions() {
+ removeMessages(MSG_UPDATE_SUGGESTIONS);
+ sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTIONS),
+ mSettingsValues.mDelayUpdateSuggestions);
}
- @Override
- public int hashCode() {
- return mChosenWord.hashCode();
+ public void cancelUpdateSuggestions() {
+ removeMessages(MSG_UPDATE_SUGGESTIONS);
}
- public abstract CharSequence getOriginalWord();
+ public boolean hasPendingUpdateSuggestions() {
+ return hasMessages(MSG_UPDATE_SUGGESTIONS);
+ }
- public CharSequence getChosenWord() {
- return mChosenWord;
+ public void postUpdateOldSuggestions() {
+ removeMessages(MSG_UPDATE_OLD_SUGGESTIONS);
+ sendMessageDelayed(obtainMessage(MSG_UPDATE_OLD_SUGGESTIONS),
+ mSettingsValues.mDelayUpdateOldSuggestions);
}
- public abstract List<CharSequence> getAlternatives();
- }
+ public void cancelUpdateOldSuggestions() {
+ removeMessages(MSG_UPDATE_OLD_SUGGESTIONS);
+ }
- public class TypedWordAlternatives extends WordAlternatives {
- private WordComposer word;
+ public void postUpdateShiftKeyState() {
+ removeMessages(MSG_UPDATE_SHIFT_STATE);
+ sendMessageDelayed(obtainMessage(MSG_UPDATE_SHIFT_STATE),
+ mSettingsValues.mDelayUpdateShiftState);
+ }
- public TypedWordAlternatives() {
- // Nothing
+ public void cancelUpdateShiftState() {
+ removeMessages(MSG_UPDATE_SHIFT_STATE);
}
- public TypedWordAlternatives(CharSequence chosenWord, WordComposer wordComposer) {
- super(chosenWord);
- word = wordComposer;
+ public void postUpdateBigramPredictions() {
+ removeMessages(MSG_SET_BIGRAM_PREDICTIONS);
+ sendMessageDelayed(obtainMessage(MSG_SET_BIGRAM_PREDICTIONS),
+ mSettingsValues.mDelayUpdateSuggestions);
}
- @Override
- public CharSequence getOriginalWord() {
- return word.getTypedWord();
+ public void cancelUpdateBigramPredictions() {
+ removeMessages(MSG_SET_BIGRAM_PREDICTIONS);
}
- @Override
- public List<CharSequence> getAlternatives() {
- return getTypedSuggestions(word);
+ public void updateVoiceResults() {
+ sendMessage(obtainMessage(MSG_VOICE_RESULTS));
}
- }
- /* package */ Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UPDATE_SUGGESTIONS:
- updateSuggestions();
- break;
- case MSG_UPDATE_OLD_SUGGESTIONS:
- setOldSuggestions();
- break;
- case MSG_START_TUTORIAL:
- if (mTutorial == null) {
- if (mKeyboardSwitcher.getInputView().isShown()) {
- mTutorial = new Tutorial(
- LatinIME.this, mKeyboardSwitcher.getInputView());
- mTutorial.start();
- } else {
- // Try again soon if the view is not yet showing
- sendMessageDelayed(obtainMessage(MSG_START_TUTORIAL), 100);
- }
- }
- break;
- case MSG_UPDATE_SHIFT_STATE:
- updateShiftKeyState(getCurrentInputEditorInfo());
- break;
- case MSG_VOICE_RESULTS:
- handleVoiceResults();
- break;
+ public void startDisplayLanguageOnSpacebar(boolean localeChanged) {
+ removeMessages(MSG_FADEOUT_LANGUAGE_ON_SPACEBAR);
+ removeMessages(MSG_DISMISS_LANGUAGE_ON_SPACEBAR);
+ final LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
+ if (inputView != null) {
+ final LatinKeyboard keyboard = mKeyboardSwitcher.getLatinKeyboard();
+ // The language is always displayed when the delay is negative.
+ final boolean needsToDisplayLanguage = localeChanged
+ || mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar < 0;
+ // The language is never displayed when the delay is zero.
+ if (mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar != 0) {
+ inputView.setSpacebarTextFadeFactor(needsToDisplayLanguage ? 1.0f
+ : mSettingsValues.mFinalFadeoutFactorOfLanguageOnSpacebar, keyboard);
+ }
+ // The fadeout animation will start when the delay is positive.
+ if (localeChanged && mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar > 0) {
+ sendMessageDelayed(obtainMessage(MSG_FADEOUT_LANGUAGE_ON_SPACEBAR, keyboard),
+ mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar);
+ }
}
}
- };
+
+ public void startDoubleSpacesTimer() {
+ removeMessages(MSG_SPACE_TYPED);
+ sendMessageDelayed(obtainMessage(MSG_SPACE_TYPED),
+ mSettingsValues.mDoubleSpacesTurnIntoPeriodTimeout);
+ }
+
+ public void cancelDoubleSpacesTimer() {
+ removeMessages(MSG_SPACE_TYPED);
+ }
+
+ public boolean isAcceptingDoubleSpaces() {
+ return hasMessages(MSG_SPACE_TYPED);
+ }
+ }
@Override
public void onCreate() {
- LatinImeLogger.init(this);
- KeyboardSwitcher.init(this);
- super.onCreate();
- //setStatusIcon(R.drawable.ime_qwerty);
- mResources = getResources();
- final Configuration conf = mResources.getConfiguration();
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
- mLanguageSwitcher = new LanguageSwitcher(this);
- mLanguageSwitcher.loadLocales(prefs);
+ mPrefs = prefs;
+ LatinImeLogger.init(this, prefs);
+ LanguageSwitcherProxy.init(this, prefs);
+ SubtypeSwitcher.init(this, prefs);
+ KeyboardSwitcher.init(this, prefs);
+ Recorrection.init(this, prefs);
+ AccessibilityUtils.init(this, prefs);
+
+ super.onCreate();
+
+ mImm = InputMethodManagerCompatWrapper.getInstance(this);
+ mInputMethodId = Utils.getInputMethodId(mImm, getPackageName());
+ mSubtypeSwitcher = SubtypeSwitcher.getInstance();
mKeyboardSwitcher = KeyboardSwitcher.getInstance();
- mKeyboardSwitcher.setLanguageSwitcher(mLanguageSwitcher);
- mSystemLocale = conf.locale.toString();
- mLanguageSwitcher.setSystemLocale(conf.locale);
- String inputLanguage = mLanguageSwitcher.getInputLanguage();
- if (inputLanguage == null) {
- inputLanguage = conf.locale.toString();
- }
- mReCorrectionEnabled = prefs.getBoolean(PREF_RECORRECTION_ENABLED,
- getResources().getBoolean(R.bool.default_recorrection_enabled));
+ mRecorrection = Recorrection.getInstance();
+ DEBUG = LatinImeLogger.sDBG;
- LatinIMEUtil.GCUtils.getInstance().reset();
+ loadSettings();
+
+ final Resources res = getResources();
+ mResources = res;
+
+ Utils.GCUtils.getInstance().reset();
boolean tryGC = true;
- for (int i = 0; i < LatinIMEUtil.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
+ for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
try {
- initSuggest(inputLanguage);
+ initSuggest();
tryGC = false;
} catch (OutOfMemoryError e) {
- tryGC = LatinIMEUtil.GCUtils.getInstance().tryGCOrWait(inputLanguage, e);
+ tryGC = Utils.GCUtils.getInstance().tryGCOrWait("InitSuggest", e);
}
}
- mOrientation = conf.orientation;
- initSuggestPuncList();
+ mOrientation = res.getConfiguration().orientation;
- // 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.
+ // Also receive installation and removal of a dictionary pack.
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
+ filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(mReceiver, filter);
- if (VOICE_INSTALLED) {
- mVoiceInput = new VoiceInput(this, this);
- mHints = new Hints(this, new Hints.Display() {
- public void showHint(int viewResource) {
- LayoutInflater inflater = (LayoutInflater) getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- View view = inflater.inflate(viewResource, null);
- setCandidatesView(view);
- setCandidatesViewShown(true);
- mIsShowingHint = true;
- }
- });
- }
- prefs.registerOnSharedPreferenceChangeListener(this);
- }
+ mVoiceProxy = VoiceProxy.init(this, prefs, mHandler);
- /**
- * Loads a dictionary or multiple separated dictionary
- * @return returns array of dictionary resource ids
- */
- /* package */ static int[] getDictionary(Resources res) {
- String packageName = LatinIME.class.getPackage().getName();
- XmlResourceParser xrp = res.getXml(R.xml.dictionary);
- ArrayList<Integer> dictionaries = new ArrayList<Integer>();
-
- try {
- int current = xrp.getEventType();
- while (current != XmlResourceParser.END_DOCUMENT) {
- if (current == XmlResourceParser.START_TAG) {
- String tag = xrp.getName();
- if (tag != null) {
- if (tag.equals("part")) {
- String dictFileName = xrp.getAttributeValue(null, "name");
- dictionaries.add(res.getIdentifier(dictFileName, "raw", packageName));
- }
- }
- }
- xrp.next();
- current = xrp.getEventType();
- }
- } catch (XmlPullParserException e) {
- Log.e(TAG, "Dictionary XML parsing failure");
- } catch (IOException e) {
- Log.e(TAG, "Dictionary XML IOException");
- }
+ final IntentFilter packageFilter = new IntentFilter();
+ packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ packageFilter.addDataScheme(SCHEME_PACKAGE);
+ registerReceiver(mDictionaryPackInstallReceiver, packageFilter);
- int count = dictionaries.size();
- int[] dict = new int[count];
- for (int i = 0; i < count; i++) {
- dict[i] = dictionaries.get(i);
- }
+ final IntentFilter newDictFilter = new IntentFilter();
+ newDictFilter.addAction(
+ DictionaryPackInstallBroadcastReceiver.NEW_DICTIONARY_INTENT_ACTION);
+ registerReceiver(mDictionaryPackInstallReceiver, newDictFilter);
+ }
- return dict;
+ // Has to be package-visible for unit tests
+ /* package */ void loadSettings() {
+ if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ if (null == mSubtypeSwitcher) mSubtypeSwitcher = SubtypeSwitcher.getInstance();
+ mSettingsValues = new Settings.Values(mPrefs, this, mSubtypeSwitcher.getInputLocaleStr());
+ resetContactsDictionary();
}
- private void initSuggest(String locale) {
- mInputLocale = locale;
+ private void initSuggest() {
+ final String localeStr = mSubtypeSwitcher.getInputLocaleStr();
+ final Locale keyboardLocale = Utils.constructLocaleFromString(localeStr);
- Resources orig = getResources();
- Configuration conf = orig.getConfiguration();
- Locale saveLocale = conf.locale;
- conf.locale = new Locale(locale);
- orig.updateConfiguration(conf, orig.getDisplayMetrics());
+ final Resources res = mResources;
+ final Locale savedLocale = Utils.setSystemLocale(res, keyboardLocale);
if (mSuggest != null) {
mSuggest.close();
}
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
- mQuickFixes = sp.getBoolean(PREF_QUICK_FIXES, true);
- int[] dictionaries = getDictionary(orig);
- mSuggest = new Suggest(this, dictionaries);
- updateAutoTextEnabled(saveLocale);
- if (mUserDictionary != null) mUserDictionary.close();
- mUserDictionary = new UserDictionary(this, mInputLocale);
- if (mContactsDictionary == null) {
- mContactsDictionary = new ContactsDictionary(this, Suggest.DIC_CONTACTS);
- }
- if (mAutoDictionary != null) {
- mAutoDictionary.close();
+ int mainDicResId = Utils.getMainDictionaryResourceId(res);
+ mSuggest = new Suggest(this, mainDicResId, keyboardLocale);
+ if (mSettingsValues.mAutoCorrectEnabled) {
+ mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold);
}
- mAutoDictionary = new AutoDictionary(this, this, mInputLocale, Suggest.DIC_AUTO);
- if (mUserBigramDictionary != null) {
- mUserBigramDictionary.close();
- }
- mUserBigramDictionary = new UserBigramDictionary(this, this, mInputLocale,
- Suggest.DIC_USER);
- mSuggest.setUserBigramDictionary(mUserBigramDictionary);
+ updateAutoTextEnabled();
+
+ mUserDictionary = new UserDictionary(this, localeStr);
mSuggest.setUserDictionary(mUserDictionary);
- mSuggest.setContactsDictionary(mContactsDictionary);
+
+ resetContactsDictionary();
+
+ mAutoDictionary = new AutoDictionary(this, this, localeStr, Suggest.DIC_AUTO);
mSuggest.setAutoDictionary(mAutoDictionary);
+
+ mUserBigramDictionary = new UserBigramDictionary(this, this, localeStr, Suggest.DIC_USER);
+ mSuggest.setUserBigramDictionary(mUserBigramDictionary);
+
updateCorrectionMode();
- mWordSeparators = mResources.getString(R.string.word_separators);
- mSentenceSeparators = mResources.getString(R.string.sentence_separators);
- conf.locale = saveLocale;
- orig.updateConfiguration(conf, orig.getDisplayMetrics());
+ Utils.setSystemLocale(res, savedLocale);
+ }
+
+ private void resetContactsDictionary() {
+ if (null == mSuggest) return;
+ ContactsDictionary contactsDictionary = mSettingsValues.mUseContactsDict
+ ? new ContactsDictionary(this, Suggest.DIC_CONTACTS) : null;
+ mSuggest.setContactsDictionary(contactsDictionary);
+ }
+
+ /* package private */ void resetSuggestMainDict() {
+ final String localeStr = mSubtypeSwitcher.getInputLocaleStr();
+ final Locale keyboardLocale = Utils.constructLocaleFromString(localeStr);
+ int mainDicResId = Utils.getMainDictionaryResourceId(mResources);
+ mSuggest.resetMainDict(this, mainDicResId, keyboardLocale);
}
@Override
public void onDestroy() {
- if (mUserDictionary != null) {
- mUserDictionary.close();
- }
- if (mContactsDictionary != null) {
- mContactsDictionary.close();
+ if (mSuggest != null) {
+ mSuggest.close();
+ mSuggest = null;
}
unregisterReceiver(mReceiver);
- if (VOICE_INSTALLED && mVoiceInput != null) {
- mVoiceInput.destroy();
- }
+ unregisterReceiver(mDictionaryPackInstallReceiver);
+ mVoiceProxy.destroy();
LatinImeLogger.commit();
LatinImeLogger.onDestroy();
super.onDestroy();
@@ -495,236 +474,192 @@ public class LatinIME extends InputMethodService
@Override
public void onConfigurationChanged(Configuration conf) {
- // If the system locale changes and is different from the saved
- // locale (mSystemLocale), then reload the input locale list from the
- // latin ime settings (shared prefs) and reset the input locale
- // to the first one.
- final String systemLocale = conf.locale.toString();
- if (!TextUtils.equals(systemLocale, mSystemLocale)) {
- mSystemLocale = systemLocale;
- if (mLanguageSwitcher != null) {
- mLanguageSwitcher.loadLocales(
- PreferenceManager.getDefaultSharedPreferences(this));
- mLanguageSwitcher.setSystemLocale(conf.locale);
- toggleLanguage(true, true);
- } else {
- reloadKeyboards();
- }
- }
+ mSubtypeSwitcher.onConfigurationChanged(conf);
// If orientation changed while predicting, commit the change
if (conf.orientation != mOrientation) {
InputConnection ic = getCurrentInputConnection();
commitTyped(ic);
if (ic != null) ic.finishComposingText(); // For voice input
mOrientation = conf.orientation;
- reloadKeyboards();
+ if (isShowingOptionDialog())
+ mOptionsDialog.dismiss();
}
+
mConfigurationChanging = true;
super.onConfigurationChanged(conf);
- if (mRecognizing) {
- switchToRecognitionStatusView();
- }
+ mVoiceProxy.onConfigurationChanged(conf);
mConfigurationChanging = false;
+
+ // This will work only when the subtype is not supported.
+ LanguageSwitcherProxy.onConfigurationChanged(conf);
}
@Override
public View onCreateInputView() {
- mKeyboardSwitcher.recreateInputView();
- mKeyboardSwitcher.makeKeyboards(true);
- mKeyboardSwitcher.setKeyboardMode(
- KeyboardSwitcher.MODE_TEXT, 0,
- shouldShowVoiceButton(makeFieldContext(), getCurrentInputEditorInfo()));
- return mKeyboardSwitcher.getInputView();
+ return mKeyboardSwitcher.onCreateInputView();
}
@Override
- public View onCreateCandidatesView() {
- mKeyboardSwitcher.makeKeyboards(true);
- mCandidateViewContainer = (LinearLayout) getLayoutInflater().inflate(
- R.layout.candidates, null);
- mCandidateView = (CandidateView) mCandidateViewContainer.findViewById(R.id.candidates);
- mCandidateView.setService(this);
- setCandidatesViewShown(true);
- return mCandidateViewContainer;
+ public void setInputView(View view) {
+ super.setInputView(view);
+ mCandidateViewContainer = view.findViewById(R.id.candidates_container);
+ mCandidateView = (CandidateView) view.findViewById(R.id.candidates);
+ mCandidateView.setListener(this, view);
+ mCandidateStripHeight = (int)mResources.getDimension(R.dimen.candidate_strip_height);
+ }
+
+ @Override
+ public void setCandidatesView(View view) {
+ // To ensure that CandidatesView will never be set.
+ return;
}
@Override
public void onStartInputView(EditorInfo attribute, boolean restarting) {
- LatinKeyboardView inputView = mKeyboardSwitcher.getInputView();
+ final KeyboardSwitcher switcher = mKeyboardSwitcher;
+ LatinKeyboardView inputView = switcher.getKeyboardView();
+
+ if (DEBUG) {
+ Log.d(TAG, "onStartInputView: attribute:" + ((attribute == null) ? "none"
+ : String.format("inputType=0x%08x imeOptions=0x%08x",
+ attribute.inputType, attribute.imeOptions)));
+ }
// In landscape mode, this method gets called without the input view being created.
if (inputView == null) {
return;
}
- if (mRefreshKeyboardRequired) {
- mRefreshKeyboardRequired = false;
- toggleLanguage(true, true);
- }
-
- mKeyboardSwitcher.makeKeyboards(false);
-
- TextEntryState.newSession(this);
+ mSubtypeSwitcher.updateParametersOnStartInputView();
- // Most such things we decide below in the switch statement, but we need to know
- // now whether this is a password text field, because we need to know now (before
- // the switch statement) whether we want to enable the voice button.
- mPasswordText = false;
- int variation = attribute.inputType & EditorInfo.TYPE_MASK_VARIATION;
- if (variation == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD ||
- variation == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) {
- mPasswordText = true;
- }
+ TextEntryState.reset();
- mEnableVoiceButton = shouldShowVoiceButton(makeFieldContext(), attribute);
- final boolean enableVoiceButton = mEnableVoiceButton && mEnableVoice;
+ // Most such things we decide below in initializeInputAttributesAndGetMode, but we need to
+ // know now whether this is a password text field, because we need to know now whether we
+ // want to enable the voice button.
+ final VoiceProxy voiceIme = mVoiceProxy;
+ voiceIme.resetVoiceStates(InputTypeCompatUtils.isPasswordInputType(attribute.inputType)
+ || InputTypeCompatUtils.isVisiblePasswordInputType(attribute.inputType));
- mAfterVoiceInput = false;
- mImmediatelyAfterVoiceInput = false;
- mShowingVoiceSuggestions = false;
- mVoiceInputHighlighted = false;
- mInputTypeNoAutoCorrect = false;
- mPredictionOn = false;
- mCompletionOn = false;
- mCompletions = null;
- mCapsLock = false;
- mEnteredText = null;
-
- switch (attribute.inputType & EditorInfo.TYPE_MASK_CLASS) {
- case EditorInfo.TYPE_CLASS_NUMBER:
- case EditorInfo.TYPE_CLASS_DATETIME:
- // fall through
- // NOTE: For now, we use the phone keyboard for NUMBER and DATETIME until we get
- // a dedicated number entry keypad.
- // TODO: Use a dedicated number entry keypad here when we get one.
- case EditorInfo.TYPE_CLASS_PHONE:
- mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_PHONE,
- attribute.imeOptions, enableVoiceButton);
- break;
- case EditorInfo.TYPE_CLASS_TEXT:
- mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_TEXT,
- attribute.imeOptions, enableVoiceButton);
- //startPrediction();
- mPredictionOn = true;
- // Make sure that passwords are not displayed in candidate view
- if (variation == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD ||
- variation == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD ) {
- mPredictionOn = false;
- }
- if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
- || variation == EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME) {
- mAutoSpace = false;
- } else {
- mAutoSpace = true;
- }
- if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS) {
- mPredictionOn = false;
- mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_EMAIL,
- attribute.imeOptions, enableVoiceButton);
- } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_URI) {
- mPredictionOn = false;
- mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_URL,
- attribute.imeOptions, enableVoiceButton);
- } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE) {
- mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_IM,
- attribute.imeOptions, enableVoiceButton);
- } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_FILTER) {
- mPredictionOn = false;
- } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) {
- mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_WEB,
- attribute.imeOptions, enableVoiceButton);
- // If it's a browser edit field and auto correct is not ON explicitly, then
- // disable auto correction, but keep suggestions on.
- if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0) {
- mInputTypeNoAutoCorrect = true;
- }
- }
+ initializeInputAttributes(attribute);
- // If NO_SUGGESTIONS is set, don't do prediction.
- if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS) != 0) {
- mPredictionOn = false;
- mInputTypeNoAutoCorrect = true;
- }
- // If it's not multiline and the autoCorrect flag is not set, then don't correct
- if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0 &&
- (attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) == 0) {
- mInputTypeNoAutoCorrect = true;
- }
- if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
- mPredictionOn = false;
- mCompletionOn = isFullscreenMode();
- }
- break;
- default:
- mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_TEXT,
- attribute.imeOptions, enableVoiceButton);
- }
inputView.closing();
+ mEnteredText = null;
mComposing.setLength(0);
- mPredicting = false;
+ mHasUncommittedTypedChars = false;
mDeleteCount = 0;
- mJustAddedAutoSpace = false;
+ mJustAddedMagicSpace = false;
+ mJustReplacedDoubleSpace = false;
+
loadSettings();
- updateShiftKeyState(attribute);
+ updateCorrectionMode();
+ updateAutoTextEnabled();
+ updateSuggestionVisibility(mPrefs, mResources);
- setCandidatesViewShownInternal(isCandidateStripVisible() || mCompletionOn,
- false /* needsInputViewShown */ );
- updateSuggestions();
+ if (mSuggest != null && mSettingsValues.mAutoCorrectEnabled) {
+ mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold);
+ }
+ mVoiceProxy.loadSettings(attribute, mPrefs);
+ // This will work only when the subtype is not supported.
+ LanguageSwitcherProxy.loadSettings();
- // If the dictionary is not big enough, don't auto correct
- mHasDictionary = mSuggest.hasMainDictionary();
+ if (mSubtypeSwitcher.isKeyboardMode()) {
+ switcher.loadKeyboard(attribute,
+ mSubtypeSwitcher.isShortcutImeEnabled() && voiceIme.isVoiceButtonEnabled(),
+ voiceIme.isVoiceButtonOnPrimary());
+ switcher.updateShiftState();
+ }
+
+ setSuggestionStripShownInternal(isCandidateStripVisible(), /* needsInputViewShown */ false);
+ // Delay updating suggestions because keyboard input view may not be shown at this point.
+ mHandler.postUpdateSuggestions();
updateCorrectionMode();
- inputView.setPreviewEnabled(mPopupOn);
+ inputView.setKeyPreviewPopupEnabled(mSettingsValues.mKeyPreviewPopupOn,
+ mSettingsValues.mKeyPreviewPopupDismissDelay);
inputView.setProximityCorrectionEnabled(true);
- mPredictionOn = mPredictionOn && (mCorrectionMode > 0 || mShowSuggestions);
// If we just entered a text field, maybe it has some old text that requires correction
- checkReCorrectionOnStart();
- checkTutorial(attribute.privateImeOptions);
+ mRecorrection.checkRecorrectionOnStart();
+ inputView.setForeground(true);
+
+ voiceIme.onStartInputView(inputView.getWindowToken());
+
if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
}
- private void checkReCorrectionOnStart() {
- if (mReCorrectionEnabled && isPredictionOn()) {
- // First get the cursor position. This is required by setOldSuggestions(), so that
- // it can pass the correct range to setComposingRegion(). At this point, we don't
- // have valid values for mLastSelectionStart/Stop because onUpdateSelection() has
- // not been called yet.
- InputConnection ic = getCurrentInputConnection();
- if (ic == null) return;
- ExtractedTextRequest etr = new ExtractedTextRequest();
- etr.token = 0; // anything is fine here
- ExtractedText et = ic.getExtractedText(etr, 0);
- if (et == null) return;
-
- mLastSelectionStart = et.startOffset + et.selectionStart;
- mLastSelectionEnd = et.startOffset + et.selectionEnd;
-
- // Then look for possible corrections in a delayed fashion
- if (!TextUtils.isEmpty(et.text) && isCursorTouchingWord()) {
- postUpdateOldSuggestions();
+ private void initializeInputAttributes(EditorInfo attribute) {
+ if (attribute == null)
+ return;
+ final int inputType = attribute.inputType;
+ final int variation = inputType & InputType.TYPE_MASK_VARIATION;
+ mShouldInsertMagicSpace = false;
+ mInputTypeNoAutoCorrect = false;
+ mIsSettingsSuggestionStripOn = false;
+ mApplicationSpecifiedCompletionOn = false;
+ mApplicationSpecifiedCompletions = null;
+
+ if ((inputType & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT) {
+ mIsSettingsSuggestionStripOn = true;
+ // Make sure that passwords are not displayed in candidate view
+ if (InputTypeCompatUtils.isPasswordInputType(inputType)
+ || InputTypeCompatUtils.isVisiblePasswordInputType(inputType)) {
+ mIsSettingsSuggestionStripOn = false;
+ }
+ if (InputTypeCompatUtils.isEmailVariation(variation)
+ || variation == InputType.TYPE_TEXT_VARIATION_PERSON_NAME) {
+ mShouldInsertMagicSpace = false;
+ } else {
+ mShouldInsertMagicSpace = true;
+ }
+ if (InputTypeCompatUtils.isEmailVariation(variation)) {
+ mIsSettingsSuggestionStripOn = false;
+ } else if (variation == InputType.TYPE_TEXT_VARIATION_URI) {
+ mIsSettingsSuggestionStripOn = false;
+ } else if (variation == InputType.TYPE_TEXT_VARIATION_FILTER) {
+ mIsSettingsSuggestionStripOn = false;
+ } else if (variation == InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) {
+ // If it's a browser edit field and auto correct is not ON explicitly, then
+ // disable auto correction, but keep suggestions on.
+ if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0) {
+ mInputTypeNoAutoCorrect = true;
+ }
+ }
+
+ // If NO_SUGGESTIONS is set, don't do prediction.
+ if ((inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) != 0) {
+ mIsSettingsSuggestionStripOn = false;
+ mInputTypeNoAutoCorrect = true;
+ }
+ // If it's not multiline and the autoCorrect flag is not set, then don't correct
+ if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0
+ && (inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) == 0) {
+ mInputTypeNoAutoCorrect = true;
+ }
+ if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
+ mIsSettingsSuggestionStripOn = false;
+ mApplicationSpecifiedCompletionOn = isFullscreenMode();
}
}
}
@Override
+ public void onWindowHidden() {
+ super.onWindowHidden();
+ KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
+ if (inputView != null) inputView.closing();
+ }
+
+ @Override
public void onFinishInput() {
super.onFinishInput();
LatinImeLogger.commit();
- onAutoCompletionStateChanged(false);
+ mKeyboardSwitcher.onAutoCorrectionStateChanged(false);
- if (VOICE_INSTALLED && !mConfigurationChanging) {
- if (mAfterVoiceInput) {
- mVoiceInput.flushAllTextModificationCounters();
- mVoiceInput.logInputEnded();
- }
- mVoiceInput.flushLogs();
- mVoiceInput.cancel();
- }
- if (mKeyboardSwitcher.getInputView() != null) {
- mKeyboardSwitcher.getInputView().closing();
- }
+ mVoiceProxy.flushVoiceInputLogs(mConfigurationChanging);
+
+ KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
+ if (inputView != null) inputView.closing();
if (mAutoDictionary != null) mAutoDictionary.flushPendingWrites();
if (mUserBigramDictionary != null) mUserBigramDictionary.flushPendingWrites();
}
@@ -732,21 +667,17 @@ public class LatinIME extends InputMethodService
@Override
public void onFinishInputView(boolean finishingInput) {
super.onFinishInputView(finishingInput);
- // Remove penging messages related to update suggestions
- mHandler.removeMessages(MSG_UPDATE_SUGGESTIONS);
- mHandler.removeMessages(MSG_UPDATE_OLD_SUGGESTIONS);
+ KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
+ if (inputView != null) inputView.setForeground(false);
+ // Remove pending messages related to update suggestions
+ mHandler.cancelUpdateSuggestions();
+ mHandler.cancelUpdateOldSuggestions();
}
@Override
public void onUpdateExtractedText(int token, ExtractedText text) {
super.onUpdateExtractedText(token, text);
- InputConnection ic = getCurrentInputConnection();
- if (!mImmediatelyAfterVoiceInput && mAfterVoiceInput && ic != null) {
- if (mHints.showPunctuationHintIfNecessary(ic)) {
- mVoiceInput.logPunctuationHintDisplayed();
- }
- }
- mImmediatelyAfterVoiceInput = false;
+ mVoiceProxy.showPunctuationHintIfNecessary();
}
@Override
@@ -759,75 +690,70 @@ public class LatinIME extends InputMethodService
if (DEBUG) {
Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart
+ ", ose=" + oldSelEnd
+ + ", lss=" + mLastSelectionStart
+ + ", lse=" + mLastSelectionEnd
+ ", nss=" + newSelStart
+ ", nse=" + newSelEnd
+ ", cs=" + candidatesStart
+ ", ce=" + candidatesEnd);
}
- if (mAfterVoiceInput) {
- mVoiceInput.setCursorPos(newSelEnd);
- mVoiceInput.setSelectionSpan(newSelEnd - newSelStart);
- }
+ mVoiceProxy.setCursorAndSelection(newSelEnd, newSelStart);
// If the current selection in the text view changes, we should
// clear whatever candidate text we have.
- if ((((mComposing.length() > 0 && mPredicting) || mVoiceInputHighlighted)
- && (newSelStart != candidatesEnd
- || newSelEnd != candidatesEnd)
- && mLastSelectionStart != newSelStart)) {
+ final boolean selectionChanged = (newSelStart != candidatesEnd
+ || newSelEnd != candidatesEnd) && mLastSelectionStart != newSelStart;
+ final boolean candidatesCleared = candidatesStart == -1 && candidatesEnd == -1;
+ if (((mComposing.length() > 0 && mHasUncommittedTypedChars)
+ || mVoiceProxy.isVoiceInputHighlighted())
+ && (selectionChanged || candidatesCleared)) {
+ if (candidatesCleared) {
+ // If the composing span has been cleared, save the typed word in the history for
+ // recorrection before we reset the candidate strip. Then, we'll be able to show
+ // suggestions for recorrection right away.
+ mRecorrection.saveRecorrectionSuggestion(mWord, mComposing);
+ }
mComposing.setLength(0);
- mPredicting = false;
- postUpdateSuggestions();
+ mHasUncommittedTypedChars = false;
+ if (isCursorTouchingWord()) {
+ mHandler.cancelUpdateBigramPredictions();
+ mHandler.postUpdateSuggestions();
+ } else {
+ setPunctuationSuggestions();
+ }
TextEntryState.reset();
InputConnection ic = getCurrentInputConnection();
if (ic != null) {
ic.finishComposingText();
}
- mVoiceInputHighlighted = false;
- } else if (!mPredicting && !mJustAccepted) {
- switch (TextEntryState.getState()) {
- case ACCEPTED_DEFAULT:
+ mVoiceProxy.setVoiceInputHighlighted(false);
+ } else if (!mHasUncommittedTypedChars && !mExpectingUpdateSelection) {
+ if (TextEntryState.isAcceptedDefault() || TextEntryState.isSpaceAfterPicked()) {
+ if (TextEntryState.isAcceptedDefault())
TextEntryState.reset();
- // fall through
- case SPACE_AFTER_PICKED:
- mJustAddedAutoSpace = false; // The user moved the cursor.
- break;
}
}
- mJustAccepted = false;
- postUpdateShiftKeyState();
+ if (!mExpectingUpdateSelection) {
+ mJustAddedMagicSpace = false; // The user moved the cursor.
+ mJustReplacedDoubleSpace = false;
+ }
+ mExpectingUpdateSelection = false;
+ mHandler.postUpdateShiftKeyState();
// Make a note of the cursor position
mLastSelectionStart = newSelStart;
mLastSelectionEnd = newSelEnd;
- if (mReCorrectionEnabled) {
- // Don't look for corrections if the keyboard is not visible
- if (mKeyboardSwitcher != null && mKeyboardSwitcher.getInputView() != null
- && mKeyboardSwitcher.getInputView().isShown()) {
- // Check if we should go in or out of correction mode.
- if (isPredictionOn()
- && mJustRevertedSeparator == null
- && (candidatesStart == candidatesEnd || newSelStart != oldSelStart
- || TextEntryState.isCorrecting())
- && (newSelStart < newSelEnd - 1 || (!mPredicting))
- && !mVoiceInputHighlighted) {
- if (isCursorTouchingWord() || mLastSelectionStart < mLastSelectionEnd) {
- postUpdateOldSuggestions();
- } else {
- abortCorrection(false);
- // Show the punctuation suggestions list if the current one is not
- // and if not showing "Touch again to save".
- if (mCandidateView != null
- && !mSuggestPuncList.equals(mCandidateView.getSuggestions())
- && !mCandidateView.isShowingAddToDictionaryHint()) {
- setNextSuggestions();
- }
- }
- }
- }
- }
+ mRecorrection.updateRecorrectionSelection(mKeyboardSwitcher,
+ mCandidateView, candidatesStart, candidatesEnd, newSelStart,
+ newSelEnd, oldSelStart, mLastSelectionStart,
+ mLastSelectionEnd, mHasUncommittedTypedChars);
+ }
+
+ public void setLastSelection(int start, int end) {
+ mLastSelectionStart = start;
+ mLastSelectionEnd = end;
}
/**
@@ -840,7 +766,7 @@ public class LatinIME extends InputMethodService
*/
@Override
public void onExtractedTextClicked() {
- if (mReCorrectionEnabled && isPredictionOn()) return;
+ if (mRecorrection.isRecorrectionEnabled() && isSuggestionsRequested()) return;
super.onExtractedTextClicked();
}
@@ -856,7 +782,7 @@ public class LatinIME extends InputMethodService
*/
@Override
public void onExtractedCursorMovement(int dx, int dy) {
- if (mReCorrectionEnabled && isPredictionOn()) return;
+ if (mRecorrection.isRecorrectionEnabled() && isSuggestionsRequested()) return;
super.onExtractedCursorMovement(dx, dy);
}
@@ -864,84 +790,103 @@ public class LatinIME extends InputMethodService
@Override
public void hideWindow() {
LatinImeLogger.commit();
- onAutoCompletionStateChanged(false);
+ mKeyboardSwitcher.onAutoCorrectionStateChanged(false);
if (TRACE) Debug.stopMethodTracing();
if (mOptionsDialog != null && mOptionsDialog.isShowing()) {
mOptionsDialog.dismiss();
mOptionsDialog = null;
}
- if (!mConfigurationChanging) {
- if (mAfterVoiceInput) mVoiceInput.logInputEnded();
- if (mVoiceWarningDialog != null && mVoiceWarningDialog.isShowing()) {
- mVoiceInput.logKeyboardWarningDialogDismissed();
- mVoiceWarningDialog.dismiss();
- mVoiceWarningDialog = null;
- }
- if (VOICE_INSTALLED & mRecognizing) {
- mVoiceInput.cancel();
- }
- }
- mWordToSuggestions.clear();
- mWordHistory.clear();
+ mVoiceProxy.hideVoiceWindow(mConfigurationChanging);
+ mRecorrection.clearWordsInHistory();
super.hideWindow();
- TextEntryState.endSession();
}
@Override
- public void onDisplayCompletions(CompletionInfo[] completions) {
+ public void onDisplayCompletions(CompletionInfo[] applicationSpecifiedCompletions) {
if (DEBUG) {
- Log.i("foo", "Received completions:");
- for (int i=0; i<(completions != null ? completions.length : 0); i++) {
- Log.i("foo", " #" + i + ": " + completions[i]);
+ Log.i(TAG, "Received completions:");
+ if (applicationSpecifiedCompletions != null) {
+ for (int i = 0; i < applicationSpecifiedCompletions.length; i++) {
+ Log.i(TAG, " #" + i + ": " + applicationSpecifiedCompletions[i]);
+ }
}
}
- if (mCompletionOn) {
- mCompletions = completions;
- if (completions == null) {
+ if (mApplicationSpecifiedCompletionOn) {
+ mApplicationSpecifiedCompletions = applicationSpecifiedCompletions;
+ if (applicationSpecifiedCompletions == null) {
clearSuggestions();
return;
}
- List<CharSequence> stringList = new ArrayList<CharSequence>();
- for (int i=0; i<(completions != null ? completions.length : 0); i++) {
- CompletionInfo ci = completions[i];
- if (ci != null) stringList.add(ci.getText());
- }
+ SuggestedWords.Builder builder = new SuggestedWords.Builder()
+ .setApplicationSpecifiedCompletions(applicationSpecifiedCompletions)
+ .setTypedWordValid(true)
+ .setHasMinimalSuggestion(true);
// When in fullscreen mode, show completions generated by the application
- setSuggestions(stringList, true, true, true);
+ setSuggestions(builder.build());
mBestWord = null;
- setCandidatesViewShown(true);
+ setSuggestionStripShown(true);
}
}
- private void setCandidatesViewShownInternal(boolean shown, boolean needsInputViewShown) {
- // TODO: Remove this if we support candidates with hard keyboard
+ private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) {
+ // TODO: Modify this if we support candidates with hard keyboard
if (onEvaluateInputViewShown()) {
- super.setCandidatesViewShown(shown && mKeyboardSwitcher.getInputView() != null
- && (needsInputViewShown ? mKeyboardSwitcher.getInputView().isShown() : true));
+ final boolean shouldShowCandidates = shown
+ && (needsInputViewShown ? mKeyboardSwitcher.isInputViewShown() : true);
+ if (isExtractViewShown()) {
+ // No need to have extra space to show the key preview.
+ mCandidateViewContainer.setMinimumHeight(0);
+ mCandidateViewContainer.setVisibility(
+ shouldShowCandidates ? View.VISIBLE : View.GONE);
+ } else {
+ // We must control the visibility of the suggestion strip in order to avoid clipped
+ // key previews, even when we don't show the suggestion strip.
+ mCandidateViewContainer.setVisibility(
+ shouldShowCandidates ? View.VISIBLE : View.INVISIBLE);
+ }
}
}
- @Override
- public void setCandidatesViewShown(boolean shown) {
- setCandidatesViewShownInternal(shown, true /* needsInputViewShown */ );
+ private void setSuggestionStripShown(boolean shown) {
+ setSuggestionStripShownInternal(shown, /* needsInputViewShown */true);
}
@Override
public void onComputeInsets(InputMethodService.Insets outInsets) {
super.onComputeInsets(outInsets);
- if (!isFullscreenMode()) {
- outInsets.contentTopInsets = outInsets.visibleTopInsets;
+ final KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
+ if (inputView == null || mCandidateViewContainer == null)
+ return;
+ final int containerHeight = mCandidateViewContainer.getHeight();
+ int touchY = containerHeight;
+ // Need to set touchable region only if input view is being shown
+ if (mKeyboardSwitcher.isInputViewShown()) {
+ if (mCandidateViewContainer.getVisibility() == View.VISIBLE) {
+ touchY -= mCandidateStripHeight;
+ }
+ final int touchWidth = inputView.getWidth();
+ final int touchHeight = inputView.getHeight() + containerHeight
+ // Extend touchable region below the keyboard.
+ + EXTENDED_TOUCHABLE_REGION_HEIGHT;
+ if (DEBUG) {
+ Log.d(TAG, "Touchable region: y=" + touchY + " width=" + touchWidth
+ + " height=" + touchHeight);
+ }
+ setTouchableRegionCompat(outInsets, 0, touchY, touchWidth, touchHeight);
}
+ outInsets.contentTopInsets = touchY;
+ outInsets.visibleTopInsets = touchY;
}
@Override
public boolean onEvaluateFullscreenMode() {
- DisplayMetrics dm = getResources().getDisplayMetrics();
+ final Resources res = mResources;
+ DisplayMetrics dm = res.getDisplayMetrics();
float displayHeight = dm.heightPixels;
// If the display is more than X inches high, don't go to fullscreen mode
- float dimen = getResources().getDimension(R.dimen.max_height_for_fullscreen);
+ float dimen = res.getDimension(R.dimen.max_height_for_fullscreen);
if (displayHeight > dimen) {
return false;
} else {
@@ -952,25 +897,13 @@ public class LatinIME extends InputMethodService
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
- case KeyEvent.KEYCODE_BACK:
- if (event.getRepeatCount() == 0 && mKeyboardSwitcher.getInputView() != null) {
- if (mKeyboardSwitcher.getInputView().handleBack()) {
- return true;
- } else if (mTutorial != null) {
- mTutorial.close();
- mTutorial = null;
- }
- }
- break;
- case KeyEvent.KEYCODE_DPAD_DOWN:
- case KeyEvent.KEYCODE_DPAD_UP:
- case KeyEvent.KEYCODE_DPAD_LEFT:
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- // If tutorial is visible, don't allow dpad to work
- if (mTutorial != null) {
+ case KeyEvent.KEYCODE_BACK:
+ if (event.getRepeatCount() == 0 && mKeyboardSwitcher.getKeyboardView() != null) {
+ if (mKeyboardSwitcher.getKeyboardView().handleBack()) {
return true;
}
- break;
+ }
+ break;
}
return super.onKeyDown(keyCode, event);
}
@@ -978,138 +911,86 @@ public class LatinIME extends InputMethodService
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch (keyCode) {
- case KeyEvent.KEYCODE_DPAD_DOWN:
- case KeyEvent.KEYCODE_DPAD_UP:
- case KeyEvent.KEYCODE_DPAD_LEFT:
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- // If tutorial is visible, don't allow dpad to work
- if (mTutorial != null) {
- return true;
- }
- LatinKeyboardView inputView = mKeyboardSwitcher.getInputView();
- // Enable shift key and DPAD to do selections
- if (inputView != null && inputView.isShown()
- && inputView.isShifted()) {
- event = new KeyEvent(event.getDownTime(), event.getEventTime(),
- event.getAction(), event.getKeyCode(), event.getRepeatCount(),
- event.getDeviceId(), event.getScanCode(),
- KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_ON);
- InputConnection ic = getCurrentInputConnection();
- if (ic != null) ic.sendKeyEvent(event);
- return true;
- }
- break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ case KeyEvent.KEYCODE_DPAD_UP:
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ // Enable shift key and DPAD to do selections
+ if (mKeyboardSwitcher.isInputViewShown()
+ && mKeyboardSwitcher.isShiftedOrShiftLocked()) {
+ KeyEvent newEvent = new KeyEvent(event.getDownTime(), event.getEventTime(),
+ event.getAction(), event.getKeyCode(), event.getRepeatCount(),
+ event.getDeviceId(), event.getScanCode(),
+ KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_ON);
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null)
+ ic.sendKeyEvent(newEvent);
+ return true;
+ }
+ break;
}
return super.onKeyUp(keyCode, event);
}
- private void revertVoiceInput() {
- InputConnection ic = getCurrentInputConnection();
- if (ic != null) ic.commitText("", 1);
- updateSuggestions();
- mVoiceInputHighlighted = false;
- }
-
- private void commitVoiceInput() {
- InputConnection ic = getCurrentInputConnection();
- if (ic != null) ic.finishComposingText();
- updateSuggestions();
- mVoiceInputHighlighted = false;
- }
-
- private void reloadKeyboards() {
- mKeyboardSwitcher.setLanguageSwitcher(mLanguageSwitcher);
- if (mKeyboardSwitcher.getInputView() != null
- && mKeyboardSwitcher.getKeyboardMode() != KeyboardSwitcher.MODE_NONE) {
- mKeyboardSwitcher.setVoiceMode(mEnableVoice && mEnableVoiceButton, mVoiceOnPrimary);
- }
- mKeyboardSwitcher.makeKeyboards(true);
- }
-
- private void commitTyped(InputConnection inputConnection) {
- if (mPredicting) {
- mPredicting = false;
+ public void commitTyped(InputConnection inputConnection) {
+ if (mHasUncommittedTypedChars) {
+ mHasUncommittedTypedChars = false;
if (mComposing.length() > 0) {
if (inputConnection != null) {
inputConnection.commitText(mComposing, 1);
}
mCommittedLength = mComposing.length();
TextEntryState.acceptedTyped(mComposing);
- addToDictionaries(mComposing, AutoDictionary.FREQUENCY_FOR_TYPED);
+ addToAutoAndUserBigramDictionaries(mComposing, AutoDictionary.FREQUENCY_FOR_TYPED);
}
updateSuggestions();
}
}
- private void postUpdateShiftKeyState() {
- mHandler.removeMessages(MSG_UPDATE_SHIFT_STATE);
- // TODO: Should remove this 300ms delay?
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_UPDATE_SHIFT_STATE), 300);
- }
-
- public void updateShiftKeyState(EditorInfo attr) {
+ public boolean getCurrentAutoCapsState() {
InputConnection ic = getCurrentInputConnection();
- if (ic != null && attr != null && mKeyboardSwitcher.isAlphabetMode()) {
- mKeyboardSwitcher.setShifted(mShiftKeyState.isMomentary() || mCapsLock
- || getCursorCapsMode(ic, attr) != 0);
- }
- }
-
- private int getCursorCapsMode(InputConnection ic, EditorInfo attr) {
- int caps = 0;
EditorInfo ei = getCurrentInputEditorInfo();
- if (mAutoCap && ei != null && ei.inputType != EditorInfo.TYPE_NULL) {
- caps = ic.getCursorCapsMode(attr.inputType);
+ if (mSettingsValues.mAutoCap && ic != null && ei != null
+ && ei.inputType != InputType.TYPE_NULL) {
+ return ic.getCursorCapsMode(ei.inputType) != 0;
}
- return caps;
+ return false;
}
- private void swapPunctuationAndSpace() {
+ private void swapSwapperAndSpace() {
final InputConnection ic = getCurrentInputConnection();
if (ic == null) return;
CharSequence lastTwo = ic.getTextBeforeCursor(2, 0);
+ // It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called.
if (lastTwo != null && lastTwo.length() == 2
- && lastTwo.charAt(0) == KEYCODE_SPACE && isSentenceSeparator(lastTwo.charAt(1))) {
+ && lastTwo.charAt(0) == Keyboard.CODE_SPACE) {
ic.beginBatchEdit();
ic.deleteSurroundingText(2, 0);
ic.commitText(lastTwo.charAt(1) + " ", 1);
ic.endBatchEdit();
- updateShiftKeyState(getCurrentInputEditorInfo());
- mJustAddedAutoSpace = true;
+ mKeyboardSwitcher.updateShiftState();
}
}
- private void reswapPeriodAndSpace() {
- final InputConnection ic = getCurrentInputConnection();
- if (ic == null) return;
- CharSequence lastThree = ic.getTextBeforeCursor(3, 0);
- if (lastThree != null && lastThree.length() == 3
- && lastThree.charAt(0) == KEYCODE_PERIOD
- && lastThree.charAt(1) == KEYCODE_SPACE
- && lastThree.charAt(2) == KEYCODE_PERIOD) {
- ic.beginBatchEdit();
- ic.deleteSurroundingText(3, 0);
- ic.commitText(" ..", 1);
- ic.endBatchEdit();
- updateShiftKeyState(getCurrentInputEditorInfo());
- }
- }
-
- private void doubleSpace() {
- //if (!mAutoPunctuate) return;
+ private void maybeDoubleSpace() {
if (mCorrectionMode == Suggest.CORRECTION_NONE) return;
final InputConnection ic = getCurrentInputConnection();
if (ic == null) return;
CharSequence lastThree = ic.getTextBeforeCursor(3, 0);
if (lastThree != null && lastThree.length() == 3
&& Character.isLetterOrDigit(lastThree.charAt(0))
- && lastThree.charAt(1) == KEYCODE_SPACE && lastThree.charAt(2) == KEYCODE_SPACE) {
+ && lastThree.charAt(1) == Keyboard.CODE_SPACE
+ && lastThree.charAt(2) == Keyboard.CODE_SPACE
+ && mHandler.isAcceptingDoubleSpaces()) {
+ mHandler.cancelDoubleSpacesTimer();
ic.beginBatchEdit();
ic.deleteSurroundingText(2, 0);
ic.commitText(". ", 1);
ic.endBatchEdit();
- updateShiftKeyState(getCurrentInputEditorInfo());
- mJustAddedAutoSpace = true;
+ mKeyboardSwitcher.updateShiftState();
+ mJustReplacedDoubleSpace = true;
+ } else {
+ mHandler.startDoubleSpacesTimer();
}
}
@@ -1121,8 +1002,8 @@ public class LatinIME extends InputMethodService
// if there is one.
CharSequence lastOne = ic.getTextBeforeCursor(1, 0);
if (lastOne != null && lastOne.length() == 1
- && lastOne.charAt(0) == KEYCODE_PERIOD
- && text.charAt(0) == KEYCODE_PERIOD) {
+ && lastOne.charAt(0) == Keyboard.CODE_PERIOD
+ && text.charAt(0) == Keyboard.CODE_PERIOD) {
ic.deleteSurroundingText(1, 0);
}
}
@@ -1133,16 +1014,17 @@ public class LatinIME extends InputMethodService
CharSequence lastOne = ic.getTextBeforeCursor(1, 0);
if (lastOne != null && lastOne.length() == 1
- && lastOne.charAt(0) == KEYCODE_SPACE) {
+ && lastOne.charAt(0) == Keyboard.CODE_SPACE) {
ic.deleteSurroundingText(1, 0);
}
}
+ @Override
public boolean addWordToDictionary(String word) {
mUserDictionary.addWord(word, 128);
// Suggestion strip should be updated after the operation of adding word to the
// user dictionary
- postUpdateSuggestions();
+ mHandler.postUpdateSuggestions();
return true;
}
@@ -1154,25 +1036,22 @@ public class LatinIME extends InputMethodService
}
}
- private void showInputMethodPicker() {
- ((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE))
- .showInputMethodPicker();
- }
-
- private void onOptionKeyPressed() {
- if (!isShowingOptionDialog()) {
- if (LatinIMEUtil.hasMultipleEnabledIMEs(this)) {
- showOptionsMenu();
- } else {
- launchSettings();
- }
+ private void onSettingsKeyPressed() {
+ if (isShowingOptionDialog())
+ return;
+ if (InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) {
+ showSubtypeSelectorAndSettings();
+ } else if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm)) {
+ showOptionsMenu();
+ } else {
+ launchSettings();
}
}
- private void onOptionKeyLongPressed() {
+ private void onSettingsKeyLongPressed() {
if (!isShowingOptionDialog()) {
- if (LatinIMEUtil.hasMultipleEnabledIMEs(this)) {
- showInputMethodPicker();
+ if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm)) {
+ mImm.showInputMethodPicker();
} else {
launchSettings();
}
@@ -1183,150 +1062,155 @@ public class LatinIME extends InputMethodService
return mOptionsDialog != null && mOptionsDialog.isShowing();
}
- // Implementation of KeyboardViewListener
-
- public void onKey(int primaryCode, int[] keyCodes, int x, int y) {
+ // Implementation of {@link KeyboardActionListener}.
+ @Override
+ public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) {
long when = SystemClock.uptimeMillis();
- if (primaryCode != Keyboard.KEYCODE_DELETE ||
- when > mLastKeyTime + QUICK_PRESS) {
+ if (primaryCode != Keyboard.CODE_DELETE || when > mLastKeyTime + QUICK_PRESS) {
mDeleteCount = 0;
}
mLastKeyTime = when;
- final boolean distinctMultiTouch = mKeyboardSwitcher.hasDistinctMultitouch();
+ KeyboardSwitcher switcher = mKeyboardSwitcher;
+ final boolean distinctMultiTouch = switcher.hasDistinctMultitouch();
+ final boolean lastStateOfJustReplacedDoubleSpace = mJustReplacedDoubleSpace;
+ mJustReplacedDoubleSpace = false;
switch (primaryCode) {
- case Keyboard.KEYCODE_DELETE:
- handleBackspace();
- mDeleteCount++;
- LatinImeLogger.logOnDelete();
- break;
- case Keyboard.KEYCODE_SHIFT:
- // Shift key is handled in onPress() when device has distinct multi-touch panel.
- if (!distinctMultiTouch)
- handleShift();
- break;
- case Keyboard.KEYCODE_MODE_CHANGE:
- // Symbol key is handled in onPress() when device has distinct multi-touch panel.
- if (!distinctMultiTouch)
- changeKeyboardMode();
- break;
- case Keyboard.KEYCODE_CANCEL:
- if (!isShowingOptionDialog()) {
- handleClose();
- }
- break;
- case LatinKeyboardView.KEYCODE_OPTIONS:
- onOptionKeyPressed();
- break;
- case LatinKeyboardView.KEYCODE_OPTIONS_LONGPRESS:
- onOptionKeyLongPressed();
- break;
- case LatinKeyboardView.KEYCODE_NEXT_LANGUAGE:
- toggleLanguage(false, true);
- break;
- case LatinKeyboardView.KEYCODE_PREV_LANGUAGE:
- toggleLanguage(false, false);
- break;
- case LatinKeyboardView.KEYCODE_VOICE:
- if (VOICE_INSTALLED) {
- startListening(false /* was a button press, was not a swipe */);
- }
- break;
- case 9 /*Tab*/:
- sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB);
- break;
- default:
- if (primaryCode != KEYCODE_ENTER) {
- mJustAddedAutoSpace = false;
- }
- RingCharBuffer.getInstance().push((char)primaryCode, x, y);
- LatinImeLogger.logOnInputChar();
- if (isWordSeparator(primaryCode)) {
- handleSeparator(primaryCode);
- } else {
- handleCharacter(primaryCode, keyCodes);
- }
- // Cancel the just reverted state
- mJustRevertedSeparator = null;
+ case Keyboard.CODE_DELETE:
+ handleBackspace(lastStateOfJustReplacedDoubleSpace);
+ mDeleteCount++;
+ mExpectingUpdateSelection = true;
+ LatinImeLogger.logOnDelete();
+ break;
+ case Keyboard.CODE_SHIFT:
+ // Shift key is handled in onPress() when device has distinct multi-touch panel.
+ if (!distinctMultiTouch)
+ switcher.toggleShift();
+ break;
+ case Keyboard.CODE_SWITCH_ALPHA_SYMBOL:
+ // Symbol key is handled in onPress() when device has distinct multi-touch panel.
+ if (!distinctMultiTouch)
+ switcher.changeKeyboardMode();
+ break;
+ case Keyboard.CODE_CANCEL:
+ if (!isShowingOptionDialog()) {
+ handleClose();
+ }
+ break;
+ case Keyboard.CODE_SETTINGS:
+ onSettingsKeyPressed();
+ break;
+ case Keyboard.CODE_SETTINGS_LONGPRESS:
+ onSettingsKeyLongPressed();
+ break;
+ case LatinKeyboard.CODE_NEXT_LANGUAGE:
+ toggleLanguage(true);
+ break;
+ case LatinKeyboard.CODE_PREV_LANGUAGE:
+ toggleLanguage(false);
+ break;
+ case Keyboard.CODE_CAPSLOCK:
+ switcher.toggleCapsLock();
+ break;
+ case Keyboard.CODE_SHORTCUT:
+ mSubtypeSwitcher.switchToShortcutIME();
+ break;
+ case Keyboard.CODE_TAB:
+ handleTab();
+ // There are two cases for tab. Either we send a "next" event, that may change the
+ // focus but will never move the cursor. Or, we send a real tab keycode, which some
+ // applications may accept or ignore, and we don't know whether this will move the
+ // cursor or not. So actually, we don't really know.
+ // So to go with the safer option, we'd rather behave as if the user moved the
+ // cursor when they didn't than the opposite. We also expect that most applications
+ // will actually use tab only for focus movement.
+ // To sum it up: do not update mExpectingUpdateSelection here.
+ break;
+ default:
+ if (mSettingsValues.isWordSeparator(primaryCode)) {
+ handleSeparator(primaryCode, x, y);
+ } else {
+ handleCharacter(primaryCode, keyCodes, x, y);
+ }
+ mExpectingUpdateSelection = true;
+ break;
}
- mKeyboardSwitcher.onKey(primaryCode);
+ switcher.onKey(primaryCode);
// Reset after any single keystroke
mEnteredText = null;
}
- public void onText(CharSequence text) {
- if (VOICE_INSTALLED && mVoiceInputHighlighted) {
- commitVoiceInput();
- }
+ @Override
+ public void onTextInput(CharSequence text) {
+ mVoiceProxy.commitVoiceInput();
InputConnection ic = getCurrentInputConnection();
if (ic == null) return;
- abortCorrection(false);
+ mRecorrection.abortRecorrection(false);
ic.beginBatchEdit();
- if (mPredicting) {
- commitTyped(ic);
- }
+ commitTyped(ic);
maybeRemovePreviousPeriod(text);
ic.commitText(text, 1);
ic.endBatchEdit();
- updateShiftKeyState(getCurrentInputEditorInfo());
- mKeyboardSwitcher.onKey(0); // dummy key code.
- mJustRevertedSeparator = null;
- mJustAddedAutoSpace = false;
+ mKeyboardSwitcher.updateShiftState();
+ mKeyboardSwitcher.onKey(Keyboard.CODE_DUMMY);
+ mJustAddedMagicSpace = false;
mEnteredText = text;
}
- public void onCancel() {
+ @Override
+ public void onCancelInput() {
// User released a finger outside any key
mKeyboardSwitcher.onCancelInput();
}
- private void handleBackspace() {
- if (VOICE_INSTALLED && mVoiceInputHighlighted) {
- mVoiceInput.incrementTextModificationDeleteCount(
- mVoiceResults.candidates.get(0).toString().length());
- revertVoiceInput();
- return;
- }
- boolean deleteChar = false;
- InputConnection ic = getCurrentInputConnection();
- if (ic == null) return;
+ private void handleBackspace(boolean justReplacedDoubleSpace) {
+ if (mVoiceProxy.logAndRevertVoiceInput()) return;
+ final InputConnection ic = getCurrentInputConnection();
+ if (ic == null) return;
ic.beginBatchEdit();
- if (mAfterVoiceInput) {
- // Don't log delete if the user is pressing delete at
- // the beginning of the text box (hence not deleting anything)
- if (mVoiceInput.getCursorPos() > 0) {
- // If anything was selected before the delete was pressed, increment the
- // delete count by the length of the selection
- int deleteLen = mVoiceInput.getSelectionSpan() > 0 ?
- mVoiceInput.getSelectionSpan() : 1;
- mVoiceInput.incrementTextModificationDeleteCount(deleteLen);
- }
- }
+ mVoiceProxy.handleBackspace();
- if (mPredicting) {
+ boolean deleteChar = false;
+ if (mHasUncommittedTypedChars) {
final int length = mComposing.length();
if (length > 0) {
mComposing.delete(length - 1, length);
mWord.deleteLast();
ic.setComposingText(mComposing, 1);
if (mComposing.length() == 0) {
- mPredicting = false;
+ mHasUncommittedTypedChars = false;
+ }
+ if (1 == length) {
+ // 1 == length means we are about to erase the last character of the word,
+ // so we can show bigrams.
+ mHandler.postUpdateBigramPredictions();
+ } else {
+ // length > 1, so we still have letters to deduce a suggestion from.
+ mHandler.postUpdateSuggestions();
}
- postUpdateSuggestions();
} else {
ic.deleteSurroundingText(1, 0);
}
} else {
deleteChar = true;
}
- postUpdateShiftKeyState();
+ mHandler.postUpdateShiftKeyState();
+
TextEntryState.backspace();
- if (TextEntryState.getState() == TextEntryState.State.UNDO_COMMIT) {
+ if (TextEntryState.isUndoCommit()) {
revertLastWord(deleteChar);
ic.endBatchEdit();
return;
- } else if (mEnteredText != null && sameAsTextBeforeCursor(ic, mEnteredText)) {
+ }
+ if (justReplacedDoubleSpace) {
+ if (revertDoubleSpace()) {
+ ic.endBatchEdit();
+ return;
+ }
+ }
+
+ if (mEnteredText != null && sameAsTextBeforeCursor(ic, mEnteredText)) {
ic.deleteSurroundingText(mEnteredText.length(), 0);
} else if (deleteChar) {
if (mCandidateView != null && mCandidateView.dismissAddToDictionaryHint()) {
@@ -1345,178 +1229,176 @@ public class LatinIME extends InputMethodService
}
}
}
- mJustRevertedSeparator = null;
ic.endBatchEdit();
}
- private void resetShift() {
- handleShiftInternal(true);
- }
+ private void handleTab() {
+ final int imeOptions = getCurrentInputEditorInfo().imeOptions;
+ if (!EditorInfoCompatUtils.hasFlagNavigateNext(imeOptions)
+ && !EditorInfoCompatUtils.hasFlagNavigatePrevious(imeOptions)) {
+ sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB);
+ return;
+ }
- private void handleShift() {
- handleShiftInternal(false);
- }
+ final InputConnection ic = getCurrentInputConnection();
+ if (ic == null)
+ return;
- private void handleShiftInternal(boolean forceNormal) {
- mHandler.removeMessages(MSG_UPDATE_SHIFT_STATE);
- KeyboardSwitcher switcher = mKeyboardSwitcher;
- LatinKeyboardView inputView = switcher.getInputView();
- if (switcher.isAlphabetMode()) {
- if (mCapsLock || forceNormal) {
- mCapsLock = false;
- switcher.setShifted(false);
- } else if (inputView != null) {
- if (inputView.isShifted()) {
- mCapsLock = true;
- switcher.setShiftLocked(true);
- } else {
- switcher.setShifted(true);
- }
- }
- } else {
- switcher.toggleShift();
+ // True if keyboard is in either chording shift or manual temporary upper case mode.
+ final boolean isManualTemporaryUpperCase = mKeyboardSwitcher.isManualTemporaryUpperCase();
+ if (EditorInfoCompatUtils.hasFlagNavigateNext(imeOptions)
+ && !isManualTemporaryUpperCase) {
+ EditorInfoCompatUtils.performEditorActionNext(ic);
+ } else if (EditorInfoCompatUtils.hasFlagNavigatePrevious(imeOptions)
+ && isManualTemporaryUpperCase) {
+ EditorInfoCompatUtils.performEditorActionPrevious(ic);
}
}
- private void abortCorrection(boolean force) {
- if (force || TextEntryState.isCorrecting()) {
- getCurrentInputConnection().finishComposingText();
- clearSuggestions();
- }
- }
+ private void handleCharacter(int primaryCode, int[] keyCodes, int x, int y) {
+ mVoiceProxy.handleCharacter();
- private void handleCharacter(int primaryCode, int[] keyCodes) {
- if (VOICE_INSTALLED && mVoiceInputHighlighted) {
- commitVoiceInput();
+ if (mJustAddedMagicSpace && mSettingsValues.isMagicSpaceStripper(primaryCode)) {
+ removeTrailingSpace();
}
- if (mAfterVoiceInput) {
- // Assume input length is 1. This assumption fails for smiley face insertions.
- mVoiceInput.incrementTextModificationInsertCount(1);
- }
- if (mLastSelectionStart == mLastSelectionEnd && TextEntryState.isCorrecting()) {
- abortCorrection(false);
+ if (mLastSelectionStart == mLastSelectionEnd) {
+ mRecorrection.abortRecorrection(false);
}
- if (isAlphabet(primaryCode) && isPredictionOn() && !isCursorTouchingWord()) {
- if (!mPredicting) {
- mPredicting = true;
+ int code = primaryCode;
+ if (isAlphabet(code) && isSuggestionsRequested() && !isCursorTouchingWord()) {
+ if (!mHasUncommittedTypedChars) {
+ mHasUncommittedTypedChars = true;
mComposing.setLength(0);
- saveWordInHistory(mBestWord);
+ mRecorrection.saveRecorrectionSuggestion(mWord, mBestWord);
mWord.reset();
+ clearSuggestions();
}
}
- if (mKeyboardSwitcher.getInputView().isShifted()) {
+ final KeyboardSwitcher switcher = mKeyboardSwitcher;
+ if (switcher.isShiftedOrShiftLocked()) {
if (keyCodes == null || keyCodes[0] < Character.MIN_CODE_POINT
|| keyCodes[0] > Character.MAX_CODE_POINT) {
return;
}
- primaryCode = keyCodes[0];
- if (mKeyboardSwitcher.isAlphabetMode() && Character.isLowerCase(primaryCode)) {
+ code = keyCodes[0];
+ if (switcher.isAlphabetMode() && Character.isLowerCase(code)) {
// In some locales, such as Turkish, Character.toUpperCase() may return a wrong
// character because it doesn't take care of locale.
- final String upperCaseString = new String(new int[] {primaryCode}, 0, 1)
- .toUpperCase(mLanguageSwitcher.getInputLocale());
+ final String upperCaseString = new String(new int[] {code}, 0, 1)
+ .toUpperCase(mSubtypeSwitcher.getInputLocale());
if (upperCaseString.codePointCount(0, upperCaseString.length()) == 1) {
- primaryCode = upperCaseString.codePointAt(0);
+ code = upperCaseString.codePointAt(0);
} else {
// Some keys, such as [eszett], have upper case as multi-characters.
- onText(upperCaseString);
+ onTextInput(upperCaseString);
return;
}
}
}
- if (mPredicting) {
- if (mKeyboardSwitcher.getInputView().isShifted()
- && mKeyboardSwitcher.isAlphabetMode()
- && mComposing.length() == 0) {
+ if (mHasUncommittedTypedChars) {
+ if (mComposing.length() == 0 && switcher.isAlphabetMode()
+ && switcher.isShiftedOrShiftLocked()) {
mWord.setFirstCharCapitalized(true);
}
- mComposing.append((char) primaryCode);
- mWord.add(primaryCode, keyCodes);
+ mComposing.append((char) code);
+ mWord.add(code, keyCodes, x, y);
InputConnection ic = getCurrentInputConnection();
if (ic != null) {
// If it's the first letter, make note of auto-caps state
if (mWord.size() == 1) {
- mWord.setAutoCapitalized(
- getCursorCapsMode(ic, getCurrentInputEditorInfo()) != 0);
+ mWord.setAutoCapitalized(getCurrentAutoCapsState());
}
ic.setComposingText(mComposing, 1);
}
- postUpdateSuggestions();
+ mHandler.postUpdateSuggestions();
} else {
- sendKeyChar((char)primaryCode);
+ sendKeyChar((char)code);
+ }
+ if (mJustAddedMagicSpace && mSettingsValues.isMagicSpaceSwapper(primaryCode)) {
+ swapSwapperAndSpace();
+ } else {
+ mJustAddedMagicSpace = false;
}
- updateShiftKeyState(getCurrentInputEditorInfo());
+
+ switcher.updateShiftState();
if (LatinIME.PERF_DEBUG) measureCps();
- TextEntryState.typedCharacter((char) primaryCode, isWordSeparator(primaryCode));
+ TextEntryState.typedCharacter((char) code, mSettingsValues.isWordSeparator(code), x, y);
}
- private void handleSeparator(int primaryCode) {
- if (VOICE_INSTALLED && mVoiceInputHighlighted) {
- commitVoiceInput();
- }
-
- if (mAfterVoiceInput){
- // Assume input length is 1. This assumption fails for smiley face insertions.
- mVoiceInput.incrementTextModificationInsertPunctuationCount(1);
- }
+ private void handleSeparator(int primaryCode, int x, int y) {
+ mVoiceProxy.handleSeparator();
// Should dismiss the "Touch again to save" message when handling separator
if (mCandidateView != null && mCandidateView.dismissAddToDictionaryHint()) {
- postUpdateSuggestions();
+ mHandler.cancelUpdateBigramPredictions();
+ mHandler.postUpdateSuggestions();
}
boolean pickedDefault = false;
// Handle separator
- InputConnection ic = getCurrentInputConnection();
+ final InputConnection ic = getCurrentInputConnection();
if (ic != null) {
ic.beginBatchEdit();
- abortCorrection(false);
+ mRecorrection.abortRecorrection(false);
}
- if (mPredicting) {
+ if (mHasUncommittedTypedChars) {
// In certain languages where single quote is a separator, it's better
// not to auto correct, but accept the typed word. For instance,
// in Italian dov' should not be expanded to dove' because the elision
// requires the last vowel to be removed.
- if (mAutoCorrectOn && primaryCode != '\'' &&
- (mJustRevertedSeparator == null
- || mJustRevertedSeparator.length() == 0
- || mJustRevertedSeparator.charAt(0) != primaryCode)) {
- pickedDefault = pickDefaultSuggestion();
- // Picked the suggestion by the space key. We consider this
- // as "added an auto space".
- if (primaryCode == KEYCODE_SPACE) {
- mJustAddedAutoSpace = true;
- }
+ final boolean shouldAutoCorrect =
+ (mSettingsValues.mAutoCorrectEnabled || mSettingsValues.mQuickFixes)
+ && !mInputTypeNoAutoCorrect && mHasDictionary;
+ if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) {
+ pickedDefault = pickDefaultSuggestion(primaryCode);
} else {
commitTyped(ic);
}
}
- if (mJustAddedAutoSpace && primaryCode == KEYCODE_ENTER) {
- removeTrailingSpace();
- mJustAddedAutoSpace = false;
- }
- sendKeyChar((char)primaryCode);
- // Handle the case of ". ." -> " .." with auto-space if necessary
- // before changing the TextEntryState.
- if (TextEntryState.getState() == TextEntryState.State.PUNCTUATION_AFTER_ACCEPTED
- && primaryCode == KEYCODE_PERIOD) {
- reswapPeriodAndSpace();
+ if (mJustAddedMagicSpace) {
+ if (mSettingsValues.isMagicSpaceSwapper(primaryCode)) {
+ sendKeyChar((char)primaryCode);
+ swapSwapperAndSpace();
+ } else {
+ if (mSettingsValues.isMagicSpaceStripper(primaryCode)) removeTrailingSpace();
+ sendKeyChar((char)primaryCode);
+ mJustAddedMagicSpace = false;
+ }
+ } else {
+ sendKeyChar((char)primaryCode);
}
- TextEntryState.typedCharacter((char) primaryCode, true);
- if (TextEntryState.getState() == TextEntryState.State.PUNCTUATION_AFTER_ACCEPTED
- && primaryCode != KEYCODE_ENTER) {
- swapPunctuationAndSpace();
- } else if (isPredictionOn() && primaryCode == KEYCODE_SPACE) {
- doubleSpace();
+ if (isSuggestionsRequested() && primaryCode == Keyboard.CODE_SPACE) {
+ maybeDoubleSpace();
}
+
+ TextEntryState.typedCharacter((char) primaryCode, true, x, y);
+
if (pickedDefault) {
- TextEntryState.backToAcceptedDefault(mWord.getTypedWord());
+ CharSequence typedWord = mWord.getTypedWord();
+ TextEntryState.backToAcceptedDefault(typedWord);
+ if (!TextUtils.isEmpty(typedWord) && !typedWord.equals(mBestWord)) {
+ InputConnectionCompatUtils.commitCorrection(
+ ic, mLastSelectionEnd - typedWord.length(), typedWord, mBestWord);
+ if (mCandidateView != null)
+ mCandidateView.onAutoCorrectionInverted(mBestWord);
+ }
+ }
+ if (Keyboard.CODE_SPACE == primaryCode) {
+ if (!isCursorTouchingWord()) {
+ mHandler.cancelUpdateSuggestions();
+ mHandler.cancelUpdateOldSuggestions();
+ mHandler.postUpdateBigramPredictions();
+ }
+ } else {
+ // Set punctuation right away. onUpdateSelection will fire but tests whether it is
+ // already displayed or not, so it's okay.
+ setPunctuationSuggestions();
}
- updateShiftKeyState(getCurrentInputEditorInfo());
+ mKeyboardSwitcher.updateShiftState();
if (ic != null) {
ic.endBatchEdit();
}
@@ -1524,367 +1406,172 @@ public class LatinIME extends InputMethodService
private void handleClose() {
commitTyped(getCurrentInputConnection());
- if (VOICE_INSTALLED & mRecognizing) {
- mVoiceInput.cancel();
- }
+ mVoiceProxy.handleClose();
requestHideSelf(0);
- if (mKeyboardSwitcher != null) {
- LatinKeyboardView inputView = mKeyboardSwitcher.getInputView();
- if (inputView != null) {
- inputView.closing();
- }
- }
- TextEntryState.endSession();
- }
-
- private void saveWordInHistory(CharSequence result) {
- if (mWord.size() <= 1) {
- mWord.reset();
- return;
- }
- // Skip if result is null. It happens in some edge case.
- if (TextUtils.isEmpty(result)) {
- return;
- }
-
- // Make a copy of the CharSequence, since it is/could be a mutable CharSequence
- final String resultCopy = result.toString();
- TypedWordAlternatives entry = new TypedWordAlternatives(resultCopy,
- new WordComposer(mWord));
- mWordHistory.add(entry);
+ LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
+ if (inputView != null)
+ inputView.closing();
}
- private void postUpdateSuggestions() {
- mHandler.removeMessages(MSG_UPDATE_SUGGESTIONS);
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_UPDATE_SUGGESTIONS), 100);
+ public boolean isSuggestionsRequested() {
+ return mIsSettingsSuggestionStripOn
+ && (mCorrectionMode > 0 || isShowingSuggestionsStrip());
}
- private void postUpdateOldSuggestions() {
- mHandler.removeMessages(MSG_UPDATE_OLD_SUGGESTIONS);
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_UPDATE_OLD_SUGGESTIONS), 300);
+ public boolean isShowingPunctuationList() {
+ return mSettingsValues.mSuggestPuncList == mCandidateView.getSuggestions();
}
- private boolean isPredictionOn() {
- return mPredictionOn;
+ public boolean isShowingSuggestionsStrip() {
+ return (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_VALUE)
+ || (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE
+ && mOrientation == Configuration.ORIENTATION_PORTRAIT);
}
- private boolean isCandidateStripVisible() {
- return isPredictionOn() && mShowSuggestions;
- }
-
- public void onCancelVoice() {
- if (mRecognizing) {
- switchToKeyboardView();
- }
- }
-
- private void switchToKeyboardView() {
- mHandler.post(new Runnable() {
- public void run() {
- mRecognizing = false;
- if (mKeyboardSwitcher.getInputView() != null) {
- setInputView(mKeyboardSwitcher.getInputView());
- }
- setCandidatesViewShown(true);
- updateInputViewShown();
- postUpdateSuggestions();
- }});
- }
-
- private void switchToRecognitionStatusView() {
- final boolean configChanged = mConfigurationChanging;
- mHandler.post(new Runnable() {
- public void run() {
- setCandidatesViewShown(false);
- mRecognizing = true;
- View v = mVoiceInput.getView();
- ViewParent p = v.getParent();
- if (p != null && p instanceof ViewGroup) {
- ((ViewGroup)v.getParent()).removeView(v);
- }
- setInputView(v);
- updateInputViewShown();
- if (configChanged) {
- mVoiceInput.onConfigurationChanged();
- }
- }});
- }
-
- private void startListening(boolean swipe) {
- if (!mHasUsedVoiceInput ||
- (!mLocaleSupportedForVoiceInput && !mHasUsedVoiceInputUnsupportedLocale)) {
- // Calls reallyStartListening if user clicks OK, does nothing if user clicks Cancel.
- showVoiceWarningDialog(swipe);
- } else {
- reallyStartListening(swipe);
- }
- }
-
- private void reallyStartListening(boolean swipe) {
- if (!mHasUsedVoiceInput) {
- // The user has started a voice input, so remember that in the
- // future (so we don't show the warning dialog after the first run).
- SharedPreferences.Editor editor =
- PreferenceManager.getDefaultSharedPreferences(this).edit();
- editor.putBoolean(PREF_HAS_USED_VOICE_INPUT, true);
- SharedPreferencesCompat.apply(editor);
- mHasUsedVoiceInput = true;
- }
-
- if (!mLocaleSupportedForVoiceInput && !mHasUsedVoiceInputUnsupportedLocale) {
- // The user has started a voice input from an unsupported locale, so remember that
- // in the future (so we don't show the warning dialog the next time they do this).
- SharedPreferences.Editor editor =
- PreferenceManager.getDefaultSharedPreferences(this).edit();
- editor.putBoolean(PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE, true);
- SharedPreferencesCompat.apply(editor);
- mHasUsedVoiceInputUnsupportedLocale = true;
- }
-
- // Clear N-best suggestions
- clearSuggestions();
-
- FieldContext context = new FieldContext(
- getCurrentInputConnection(),
- getCurrentInputEditorInfo(),
- mLanguageSwitcher.getInputLanguage(),
- mLanguageSwitcher.getEnabledLanguages());
- mVoiceInput.startListening(context, swipe);
- switchToRecognitionStatusView();
- }
-
- private void showVoiceWarningDialog(final boolean swipe) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setCancelable(true);
- builder.setIcon(R.drawable.ic_mic_dialog);
- builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- mVoiceInput.logKeyboardWarningDialogOk();
- reallyStartListening(swipe);
- }
- });
- builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- mVoiceInput.logKeyboardWarningDialogCancel();
- }
- });
-
- if (mLocaleSupportedForVoiceInput) {
- String message = getString(R.string.voice_warning_may_not_understand) + "\n\n" +
- getString(R.string.voice_warning_how_to_turn_off);
- builder.setMessage(message);
- } else {
- String message = getString(R.string.voice_warning_locale_not_supported) + "\n\n" +
- getString(R.string.voice_warning_may_not_understand) + "\n\n" +
- getString(R.string.voice_warning_how_to_turn_off);
- builder.setMessage(message);
- }
-
- builder.setTitle(R.string.voice_warning_title);
- mVoiceWarningDialog = builder.create();
-
- Window window = mVoiceWarningDialog.getWindow();
- WindowManager.LayoutParams lp = window.getAttributes();
- lp.token = mKeyboardSwitcher.getInputView().getWindowToken();
- lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
- window.setAttributes(lp);
- window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
- mVoiceInput.logKeyboardWarningDialogShown();
- mVoiceWarningDialog.show();
- }
-
- public void onVoiceResults(List<String> candidates,
- Map<String, List<CharSequence>> alternatives) {
- if (!mRecognizing) {
- return;
- }
- mVoiceResults.candidates = candidates;
- mVoiceResults.alternatives = alternatives;
- mHandler.sendMessage(mHandler.obtainMessage(MSG_VOICE_RESULTS));
+ public boolean isCandidateStripVisible() {
+ if (mCandidateView == null)
+ return false;
+ if (mCandidateView.isShowingAddToDictionaryHint() || TextEntryState.isRecorrecting())
+ return true;
+ if (!isShowingSuggestionsStrip())
+ return false;
+ if (mApplicationSpecifiedCompletionOn)
+ return true;
+ return isSuggestionsRequested();
}
- private void handleVoiceResults() {
- mAfterVoiceInput = true;
- mImmediatelyAfterVoiceInput = true;
-
- InputConnection ic = getCurrentInputConnection();
- if (!isFullscreenMode()) {
- // Start listening for updates to the text from typing, etc.
- if (ic != null) {
- ExtractedTextRequest req = new ExtractedTextRequest();
- ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
- }
- }
-
- vibrate();
- switchToKeyboardView();
-
- final List<CharSequence> nBest = new ArrayList<CharSequence>();
- boolean capitalizeFirstWord = preferCapitalization()
- || (mKeyboardSwitcher.isAlphabetMode()
- && mKeyboardSwitcher.getInputView().isShifted());
- for (String c : mVoiceResults.candidates) {
- if (capitalizeFirstWord) {
- c = Character.toUpperCase(c.charAt(0)) + c.substring(1, c.length());
+ public void switchToKeyboardView() {
+ if (DEBUG) {
+ Log.d(TAG, "Switch to keyboard view.");
+ }
+ View v = mKeyboardSwitcher.getKeyboardView();
+ if (v != null) {
+ // Confirms that the keyboard view doesn't have parent view.
+ ViewParent p = v.getParent();
+ if (p != null && p instanceof ViewGroup) {
+ ((ViewGroup) p).removeView(v);
}
- nBest.add(c);
+ setInputView(v);
}
-
- if (nBest.size() == 0) {
- return;
- }
-
- String bestResult = nBest.get(0).toString();
-
- mVoiceInput.logVoiceInputDelivered(bestResult.length());
-
- mHints.registerVoiceResult(bestResult);
-
- if (ic != null) ic.beginBatchEdit(); // To avoid extra updates on committing older text
-
- commitTyped(ic);
- EditingUtil.appendText(ic, bestResult);
-
- if (ic != null) ic.endBatchEdit();
-
- mVoiceInputHighlighted = true;
- mWordToSuggestions.putAll(mVoiceResults.alternatives);
+ setSuggestionStripShown(isCandidateStripVisible());
+ updateInputViewShown();
+ mHandler.postUpdateSuggestions();
}
- private void clearSuggestions() {
- setSuggestions(null, false, false, false);
+ public void clearSuggestions() {
+ setSuggestions(SuggestedWords.EMPTY);
}
- private void setSuggestions(
- List<CharSequence> suggestions,
- boolean completions,
- boolean typedWordValid,
- boolean haveMinimalSuggestion) {
-
- if (mIsShowingHint) {
- setCandidatesView(mCandidateViewContainer);
- mIsShowingHint = false;
- }
-
+ public void setSuggestions(SuggestedWords words) {
if (mCandidateView != null) {
- mCandidateView.setSuggestions(
- suggestions, completions, typedWordValid, haveMinimalSuggestion);
+ mCandidateView.setSuggestions(words);
+ mKeyboardSwitcher.onAutoCorrectionStateChanged(
+ words.hasWordAboveAutoCorrectionScoreThreshold());
}
}
- private void updateSuggestions() {
- LatinKeyboardView inputView = mKeyboardSwitcher.getInputView();
- ((LatinKeyboard) inputView.getKeyboard()).setPreferredLetters(null);
-
+ public void updateSuggestions() {
// Check if we have a suggestion engine attached.
- if ((mSuggest == null || !isPredictionOn()) && !mVoiceInputHighlighted) {
+ if ((mSuggest == null || !isSuggestionsRequested())
+ && !mVoiceProxy.isVoiceInputHighlighted()) {
return;
}
- if (!mPredicting) {
- setNextSuggestions();
+ if (!mHasUncommittedTypedChars) {
+ setPunctuationSuggestions();
return;
}
showSuggestions(mWord);
}
- private List<CharSequence> getTypedSuggestions(WordComposer word) {
- List<CharSequence> stringList = mSuggest.getSuggestions(
- mKeyboardSwitcher.getInputView(), word, false, null);
- return stringList;
- }
-
- private void showCorrections(WordAlternatives alternatives) {
- List<CharSequence> stringList = alternatives.getAlternatives();
- ((LatinKeyboard) mKeyboardSwitcher.getInputView().getKeyboard()).setPreferredLetters(null);
- showSuggestions(stringList, alternatives.getOriginalWord(), false, false);
- }
-
private void showSuggestions(WordComposer word) {
- // long startTime = System.currentTimeMillis(); // TIME MEASUREMENT!
- // TODO Maybe need better way of retrieving previous word
- CharSequence prevWord = EditingUtil.getPreviousWord(getCurrentInputConnection(),
- mWordSeparators);
- List<CharSequence> stringList = mSuggest.getSuggestions(
- mKeyboardSwitcher.getInputView(), word, false, prevWord);
- // long stopTime = System.currentTimeMillis(); // TIME MEASUREMENT!
- // Log.d("LatinIME","Suggest Total Time - " + (stopTime - startTime));
-
- int[] nextLettersFrequencies = mSuggest.getNextLettersFrequencies();
-
- ((LatinKeyboard) mKeyboardSwitcher.getInputView().getKeyboard()).setPreferredLetters(
- nextLettersFrequencies);
-
- boolean correctionAvailable = !mInputTypeNoAutoCorrect && mSuggest.hasMinimalCorrection();
- //|| mCorrectionMode == mSuggest.CORRECTION_FULL;
- CharSequence typedWord = word.getTypedWord();
- // If we're in basic correct
- boolean typedWordValid = mSuggest.isValidWord(typedWord) ||
- (preferCapitalization()
- && mSuggest.isValidWord(typedWord.toString().toLowerCase()));
+ // TODO: May need a better way of retrieving previous word
+ CharSequence prevWord = EditingUtils.getPreviousWord(getCurrentInputConnection(),
+ mSettingsValues.mWordSeparators);
+ SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(
+ mKeyboardSwitcher.getKeyboardView(), word, prevWord);
+
+ boolean correctionAvailable = !mInputTypeNoAutoCorrect && mSuggest.hasAutoCorrection();
+ final CharSequence typedWord = word.getTypedWord();
+ // Here, we want to promote a whitelisted word if exists.
+ final boolean typedWordValid = AutoCorrection.isValidWordForAutoCorrection(
+ mSuggest.getUnigramDictionaries(), typedWord, preferCapitalization());
if (mCorrectionMode == Suggest.CORRECTION_FULL
|| mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM) {
correctionAvailable |= typedWordValid;
}
// Don't auto-correct words with multiple capital letter
correctionAvailable &= !word.isMostlyCaps();
- correctionAvailable &= !TextEntryState.isCorrecting();
-
- showSuggestions(stringList, typedWord, typedWordValid, correctionAvailable);
+ correctionAvailable &= !TextEntryState.isRecorrecting();
+
+ // 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 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 (typedWord != null) {
+ if (builder.size() > 1 || typedWord.length() == 1 || typedWordValid
+ || mCandidateView.isShowingAddToDictionaryHint()) {
+ builder.setTypedWordValid(typedWordValid).setHasMinimalSuggestion(
+ correctionAvailable);
+ } else {
+ final SuggestedWords previousSuggestions = mCandidateView.getSuggestions();
+ if (previousSuggestions == mSettingsValues.mSuggestPuncList)
+ return;
+ builder.addTypedWordAndPreviousSuggestions(typedWord, previousSuggestions);
+ }
+ }
+ showSuggestions(builder.build(), typedWord);
}
- private void showSuggestions(List<CharSequence> stringList, CharSequence typedWord,
- boolean typedWordValid, boolean correctionAvailable) {
- setSuggestions(stringList, false, typedWordValid, correctionAvailable);
- if (stringList.size() > 0) {
- if (correctionAvailable && !typedWordValid && stringList.size() > 1) {
- mBestWord = stringList.get(1);
+ public void showSuggestions(SuggestedWords suggestedWords, CharSequence typedWord) {
+ setSuggestions(suggestedWords);
+ if (suggestedWords.size() > 0) {
+ if (Utils.shouldBlockedBySafetyNetForAutoCorrection(suggestedWords, mSuggest)) {
+ mBestWord = typedWord;
+ } else if (suggestedWords.hasAutoCorrectionWord()) {
+ mBestWord = suggestedWords.getWord(1);
} else {
mBestWord = typedWord;
}
} else {
mBestWord = null;
}
- setCandidatesViewShown(isCandidateStripVisible() || mCompletionOn);
+ setSuggestionStripShown(isCandidateStripVisible());
}
- private boolean pickDefaultSuggestion() {
+ private boolean pickDefaultSuggestion(int separatorCode) {
// Complete any pending candidate query first
- if (mHandler.hasMessages(MSG_UPDATE_SUGGESTIONS)) {
- mHandler.removeMessages(MSG_UPDATE_SUGGESTIONS);
+ if (mHandler.hasPendingUpdateSuggestions()) {
+ mHandler.cancelUpdateSuggestions();
updateSuggestions();
}
if (mBestWord != null && mBestWord.length() > 0) {
- TextEntryState.acceptedDefault(mWord.getTypedWord(), mBestWord);
- mJustAccepted = true;
- pickSuggestion(mBestWord, false);
+ TextEntryState.acceptedDefault(mWord.getTypedWord(), mBestWord, separatorCode);
+ mExpectingUpdateSelection = true;
+ commitBestWord(mBestWord);
// Add the word to the auto dictionary if it's not a known word
- addToDictionaries(mBestWord, AutoDictionary.FREQUENCY_FOR_TYPED);
+ addToAutoAndUserBigramDictionaries(mBestWord, AutoDictionary.FREQUENCY_FOR_TYPED);
return true;
-
}
return false;
}
+ @Override
public void pickSuggestionManually(int index, CharSequence suggestion) {
- List<CharSequence> suggestions = mCandidateView.getSuggestions();
-
- if (mAfterVoiceInput && mShowingVoiceSuggestions) {
- mVoiceInput.flushAllTextModificationCounters();
- // send this intent AFTER logging any prior aggregated edits.
- mVoiceInput.logTextModifiedByChooseSuggestion(suggestion.toString(), index,
- mWordSeparators,
- getCurrentInputConnection());
- }
+ SuggestedWords suggestions = mCandidateView.getSuggestions();
+ mVoiceProxy.flushAndLogAllTextModificationCounters(index, suggestion,
+ mSettingsValues.mWordSeparators);
- final boolean correcting = TextEntryState.isCorrecting();
+ final boolean recorrecting = TextEntryState.isRecorrecting();
InputConnection ic = getCurrentInputConnection();
if (ic != null) {
ic.beginBatchEdit();
}
- if (mCompletionOn && mCompletions != null && index >= 0
- && index < mCompletions.length) {
- CompletionInfo ci = mCompletions[index];
+ if (mApplicationSpecifiedCompletionOn && mApplicationSpecifiedCompletions != null
+ && index >= 0 && index < mApplicationSpecifiedCompletions.length) {
+ CompletionInfo ci = mApplicationSpecifiedCompletions[index];
if (ic != null) {
ic.commitCompletion(ci);
}
@@ -1892,7 +1579,7 @@ public class LatinIME extends InputMethodService
if (mCandidateView != null) {
mCandidateView.clear();
}
- updateShiftKeyState(getCurrentInputEditorInfo());
+ mKeyboardSwitcher.updateShiftState();
if (ic != null) {
ic.endBatchEdit();
}
@@ -1900,52 +1587,81 @@ public class LatinIME extends InputMethodService
}
// If this is a punctuation, apply it through the normal key press
- if (suggestion.length() == 1 && (isWordSeparator(suggestion.charAt(0))
- || isSuggestedPunctuation(suggestion.charAt(0)))) {
+ if (suggestion.length() == 1 && (mSettingsValues.isWordSeparator(suggestion.charAt(0))
+ || mSettingsValues.isSuggestedPunctuation(suggestion.charAt(0)))) {
// Word separators are suggested before the user inputs something.
// So, LatinImeLogger logs "" as a user's input.
LatinImeLogger.logOnManualSuggestion(
- "", suggestion.toString(), index, suggestions);
+ "", suggestion.toString(), index, suggestions.mWords);
+ // Find out whether the previous character is a space. If it is, as a special case
+ // for punctuation entered through the suggestion strip, it should be considered
+ // a magic space even if it was a normal space. This is meant to help in case the user
+ // pressed space on purpose of displaying the suggestion strip punctuation.
final char primaryCode = suggestion.charAt(0);
- onKey(primaryCode, new int[]{primaryCode}, LatinKeyboardBaseView.NOT_A_TOUCH_COORDINATE,
- LatinKeyboardBaseView.NOT_A_TOUCH_COORDINATE);
+ final CharSequence beforeText = ic != null ? ic.getTextBeforeCursor(1, 0) : "";
+ final int toLeft = (ic == null || TextUtils.isEmpty(beforeText))
+ ? 0 : beforeText.charAt(0);
+ final boolean oldMagicSpace = mJustAddedMagicSpace;
+ if (Keyboard.CODE_SPACE == toLeft) mJustAddedMagicSpace = true;
+ onCodeInput(primaryCode, new int[] { primaryCode },
+ KeyboardActionListener.NOT_A_TOUCH_COORDINATE,
+ KeyboardActionListener.NOT_A_TOUCH_COORDINATE);
+ mJustAddedMagicSpace = oldMagicSpace;
if (ic != null) {
ic.endBatchEdit();
}
return;
}
- mJustAccepted = true;
- pickSuggestion(suggestion, correcting);
+ if (!mHasUncommittedTypedChars) {
+ // If we are not composing a word, then it was a suggestion inferred from
+ // context - no user input. We should reset the word composer.
+ mWord.reset();
+ }
+ mExpectingUpdateSelection = true;
+ commitBestWord(suggestion);
// Add the word to the auto dictionary if it's not a known word
if (index == 0) {
- addToDictionaries(suggestion, AutoDictionary.FREQUENCY_FOR_PICKED);
+ addToAutoAndUserBigramDictionaries(suggestion, AutoDictionary.FREQUENCY_FOR_PICKED);
} else {
- addToBigramDictionary(suggestion, 1);
+ addToOnlyBigramDictionary(suggestion, 1);
}
LatinImeLogger.logOnManualSuggestion(mComposing.toString(), suggestion.toString(),
- index, suggestions);
+ index, suggestions.mWords);
TextEntryState.acceptedSuggestion(mComposing.toString(), suggestion);
// Follow it with a space
- if (mAutoSpace && !correcting) {
- sendSpace();
- mJustAddedAutoSpace = true;
- }
-
- final boolean showingAddToDictionaryHint = index == 0 && mCorrectionMode > 0
- && !mSuggest.isValidWord(suggestion)
- && !mSuggest.isValidWord(suggestion.toString().toLowerCase());
-
- if (!correcting) {
+ if (mShouldInsertMagicSpace && !recorrecting) {
+ sendMagicSpace();
+ }
+
+ // We should show the hint if the user pressed the first entry AND either:
+ // - There is no dictionary (we know that because we tried to load it => null != mSuggest
+ // AND mHasDictionary is false)
+ // - There is a dictionary and the word is not in it
+ // Please note that if mSuggest is null, it means that everything is off: suggestion
+ // and correction, so we shouldn't try to show the hint
+ // We used to look at mCorrectionMode here, but showing the hint should have nothing
+ // to do with the autocorrection setting.
+ final boolean showingAddToDictionaryHint = index == 0 && mSuggest != null
+ // If there is no dictionary the hint should be shown.
+ && (!mHasDictionary
+ // If "suggestion" is not in the dictionary, the hint should be shown.
+ || !AutoCorrection.isValidWord(
+ mSuggest.getUnigramDictionaries(), suggestion, true));
+
+ if (!recorrecting) {
// Fool the state watcher so that a subsequent backspace will not do a revert, unless
// we just did a correction, in which case we need to stay in
// TextEntryState.State.PICKED_SUGGESTION state.
- TextEntryState.typedCharacter((char) KEYCODE_SPACE, true);
- setNextSuggestions();
- } else if (!showingAddToDictionaryHint) {
+ TextEntryState.typedCharacter((char) Keyboard.CODE_SPACE, true,
+ WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE);
+ }
+ if (!showingAddToDictionaryHint) {
// If we're not showing the "Touch again to save", then show corrections again.
// In case the cursor position doesn't change, make sure we show the suggestions again.
- clearSuggestions();
- postUpdateOldSuggestions();
+ updateBigramPredictions();
+ // Updating the predictions right away may be slow and feel unresponsive on slower
+ // terminals. On the other hand if we just postUpdateBigramPredictions() it will
+ // take a noticeable delay to update them which may feel uneasy.
}
if (showingAddToDictionaryHint) {
mCandidateView.showAddToDictionaryHint(suggestion);
@@ -1955,193 +1671,71 @@ public class LatinIME extends InputMethodService
}
}
- private void rememberReplacedWord(CharSequence suggestion) {
- if (mShowingVoiceSuggestions) {
- // Retain the replaced word in the alternatives array.
- EditingUtil.Range range = new EditingUtil.Range();
- String wordToBeReplaced = EditingUtil.getWordAtCursor(getCurrentInputConnection(),
- mWordSeparators, range);
- if (!mWordToSuggestions.containsKey(wordToBeReplaced)) {
- wordToBeReplaced = wordToBeReplaced.toLowerCase();
- }
- if (mWordToSuggestions.containsKey(wordToBeReplaced)) {
- List<CharSequence> suggestions = mWordToSuggestions.get(wordToBeReplaced);
- if (suggestions.contains(suggestion)) {
- suggestions.remove(suggestion);
- }
- suggestions.add(wordToBeReplaced);
- mWordToSuggestions.remove(wordToBeReplaced);
- mWordToSuggestions.put(suggestion.toString(), suggestions);
- }
- }
- }
-
/**
* Commits the chosen word to the text field and saves it for later
* retrieval.
- * @param suggestion the suggestion picked by the user to be committed to
- * the text field
- * @param correcting whether this is due to a correction of an existing
- * word.
*/
- private void pickSuggestion(CharSequence suggestion, boolean correcting) {
- final LatinKeyboardView inputView = mKeyboardSwitcher.getInputView();
- final Locale inputLocale = mLanguageSwitcher.getInputLocale();
- if (mCapsLock) {
- suggestion = suggestion.toString().toUpperCase(inputLocale);
- } else if (preferCapitalization()
- || (mKeyboardSwitcher.isAlphabetMode()
- && inputView.isShifted())) {
- suggestion = suggestion.toString().toUpperCase(inputLocale).charAt(0)
- + suggestion.subSequence(1, suggestion.length()).toString();
- }
+ private void commitBestWord(CharSequence bestWord) {
+ KeyboardSwitcher switcher = mKeyboardSwitcher;
+ if (!switcher.isKeyboardAvailable())
+ return;
InputConnection ic = getCurrentInputConnection();
if (ic != null) {
- rememberReplacedWord(suggestion);
- ic.commitText(suggestion, 1);
- }
- saveWordInHistory(suggestion);
- mPredicting = false;
- mCommittedLength = suggestion.length();
- ((LatinKeyboard) inputView.getKeyboard()).setPreferredLetters(null);
- // If we just corrected a word, then don't show punctuations
- if (!correcting) {
- setNextSuggestions();
- }
- updateShiftKeyState(getCurrentInputEditorInfo());
- }
-
- /**
- * Tries to apply any voice alternatives for the word if this was a spoken word and
- * there are voice alternatives.
- * @param touching The word that the cursor is touching, with position information
- * @return true if an alternative was found, false otherwise.
- */
- private boolean applyVoiceAlternatives(EditingUtil.SelectedWord touching) {
- // Search for result in spoken word alternatives
- String selectedWord = touching.word.toString().trim();
- if (!mWordToSuggestions.containsKey(selectedWord)) {
- selectedWord = selectedWord.toLowerCase();
- }
- if (mWordToSuggestions.containsKey(selectedWord)) {
- mShowingVoiceSuggestions = true;
- List<CharSequence> suggestions = mWordToSuggestions.get(selectedWord);
- // If the first letter of touching is capitalized, make all the suggestions
- // start with a capital letter.
- if (Character.isUpperCase(touching.word.charAt(0))) {
- final Locale inputLocale = mLanguageSwitcher.getInputLocale();
- for (int i = 0; i < suggestions.size(); i++) {
- String origSugg = (String) suggestions.get(i);
- String capsSugg = origSugg.toUpperCase(inputLocale).charAt(0)
- + origSugg.subSequence(1, origSugg.length()).toString();
- suggestions.set(i, capsSugg);
- }
- }
- setSuggestions(suggestions, false, true, true);
- setCandidatesViewShown(true);
- return true;
+ mVoiceProxy.rememberReplacedWord(bestWord, mSettingsValues.mWordSeparators);
+ SuggestedWords suggestedWords = mCandidateView.getSuggestions();
+ ic.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
+ this, bestWord, suggestedWords), 1);
}
- return false;
+ mRecorrection.saveRecorrectionSuggestion(mWord, bestWord);
+ mHasUncommittedTypedChars = false;
+ mCommittedLength = bestWord.length();
}
- /**
- * Tries to apply any typed alternatives for the word if we have any cached alternatives,
- * otherwise tries to find new corrections and completions for the word.
- * @param touching The word that the cursor is touching, with position information
- * @return true if an alternative was found, false otherwise.
- */
- private boolean applyTypedAlternatives(EditingUtil.SelectedWord touching) {
- // If we didn't find a match, search for result in typed word history
- WordComposer foundWord = null;
- WordAlternatives alternatives = null;
- for (WordAlternatives entry : mWordHistory) {
- if (TextUtils.equals(entry.getChosenWord(), touching.word)) {
- if (entry instanceof TypedWordAlternatives) {
- foundWord = ((TypedWordAlternatives) entry).word;
- }
- alternatives = entry;
- break;
- }
- }
- // If we didn't find a match, at least suggest completions
- if (foundWord == null
- && (mSuggest.isValidWord(touching.word)
- || mSuggest.isValidWord(touching.word.toString().toLowerCase()))) {
- foundWord = new WordComposer();
- for (int i = 0; i < touching.word.length(); i++) {
- foundWord.add(touching.word.charAt(i), new int[] {
- touching.word.charAt(i)
- });
- }
- foundWord.setFirstCharCapitalized(Character.isUpperCase(touching.word.charAt(0)));
- }
- // Found a match, show suggestions
- if (foundWord != null || alternatives != null) {
- if (alternatives == null) {
- alternatives = new TypedWordAlternatives(touching.word, foundWord);
- }
- showCorrections(alternatives);
- if (foundWord != null) {
- mWord = new WordComposer(foundWord);
- } else {
- mWord.reset();
- }
- return true;
- }
- return false;
- }
+ private static final WordComposer sEmptyWordComposer = new WordComposer();
+ public void updateBigramPredictions() {
+ if (mSuggest == null || !isSuggestionsRequested())
+ return;
- private void setOldSuggestions() {
- mShowingVoiceSuggestions = false;
- if (mCandidateView != null && mCandidateView.isShowingAddToDictionaryHint()) {
+ if (!mSettingsValues.mBigramPredictionEnabled) {
+ setPunctuationSuggestions();
return;
}
- InputConnection ic = getCurrentInputConnection();
- if (ic == null) return;
- if (!mPredicting) {
- // Extract the selected or touching text
- EditingUtil.SelectedWord touching = EditingUtil.getWordAtCursorOrSelection(ic,
- mLastSelectionStart, mLastSelectionEnd, mWordSeparators);
- if (touching != null && touching.word.length() > 1) {
- ic.beginBatchEdit();
+ final CharSequence prevWord = EditingUtils.getThisWord(getCurrentInputConnection(),
+ mSettingsValues.mWordSeparators);
+ SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(
+ mKeyboardSwitcher.getKeyboardView(), sEmptyWordComposer, prevWord);
- if (!applyVoiceAlternatives(touching) && !applyTypedAlternatives(touching)) {
- abortCorrection(true);
- } else {
- TextEntryState.selectedForCorrection();
- EditingUtil.underlineWord(ic, touching);
- }
-
- ic.endBatchEdit();
- } else {
- abortCorrection(true);
- setNextSuggestions(); // Show the punctuation suggestions list
- }
+ if (builder.size() > 0) {
+ // Explicitly supply an empty typed word (the no-second-arg version of
+ // showSuggestions will retrieve the word near the cursor, we don't want that here)
+ showSuggestions(builder.build(), "");
} else {
- abortCorrection(true);
+ if (!isShowingPunctuationList()) setPunctuationSuggestions();
}
}
- private void setNextSuggestions() {
- setSuggestions(mSuggestPuncList, false, false, false);
+ public void setPunctuationSuggestions() {
+ setSuggestions(mSettingsValues.mSuggestPuncList);
+ setSuggestionStripShown(isCandidateStripVisible());
}
- private void addToDictionaries(CharSequence suggestion, int frequencyDelta) {
+ private void addToAutoAndUserBigramDictionaries(CharSequence suggestion, int frequencyDelta) {
checkAddToDictionary(suggestion, frequencyDelta, false);
}
- private void addToBigramDictionary(CharSequence suggestion, int frequencyDelta) {
+ private void addToOnlyBigramDictionary(CharSequence suggestion, int frequencyDelta) {
checkAddToDictionary(suggestion, frequencyDelta, true);
}
/**
* Adds to the UserBigramDictionary and/or AutoDictionary
- * @param addToBigramDictionary true if it should be added to bigram dictionary if possible
+ * @param selectedANotTypedWord true if it should be added to bigram dictionary if possible
*/
private void checkAddToDictionary(CharSequence suggestion, int frequencyDelta,
- boolean addToBigramDictionary) {
+ boolean selectedANotTypedWord) {
if (suggestion == null || suggestion.length() < 1) return;
+
// Only auto-add to dictionary if auto-correct is ON. Otherwise we'll be
// adding words in situations where the user or application really didn't
// want corrections enabled or learned.
@@ -2149,36 +1743,42 @@ public class LatinIME extends InputMethodService
|| mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM)) {
return;
}
- if (suggestion != null) {
- if (!addToBigramDictionary && mAutoDictionary.isValidWord(suggestion)
- || (!mSuggest.isValidWord(suggestion.toString())
- && !mSuggest.isValidWord(suggestion.toString().toLowerCase()))) {
- mAutoDictionary.addWord(suggestion.toString(), frequencyDelta);
- }
- if (mUserBigramDictionary != null) {
- CharSequence prevWord = EditingUtil.getPreviousWord(getCurrentInputConnection(),
- mSentenceSeparators);
- if (!TextUtils.isEmpty(prevWord)) {
- mUserBigramDictionary.addBigrams(prevWord.toString(), suggestion.toString());
- }
+ final boolean selectedATypedWordAndItsInAutoDic =
+ !selectedANotTypedWord && mAutoDictionary.isValidWord(suggestion);
+ final boolean isValidWord = AutoCorrection.isValidWord(
+ mSuggest.getUnigramDictionaries(), suggestion, true);
+ final boolean needsToAddToAutoDictionary = selectedATypedWordAndItsInAutoDic
+ || !isValidWord;
+ if (needsToAddToAutoDictionary) {
+ mAutoDictionary.addWord(suggestion.toString(), frequencyDelta);
+ }
+
+ if (mUserBigramDictionary != null) {
+ // We don't want to register as bigrams words separated by a separator.
+ // For example "I will, and you too" : we don't want the pair ("will" "and") to be
+ // a bigram.
+ CharSequence prevWord = EditingUtils.getPreviousWord(getCurrentInputConnection(),
+ mSettingsValues.mWordSeparators);
+ if (!TextUtils.isEmpty(prevWord)) {
+ mUserBigramDictionary.addBigrams(prevWord.toString(), suggestion.toString());
}
}
}
- private boolean isCursorTouchingWord() {
+ public boolean isCursorTouchingWord() {
InputConnection ic = getCurrentInputConnection();
if (ic == null) return false;
CharSequence toLeft = ic.getTextBeforeCursor(1, 0);
CharSequence toRight = ic.getTextAfterCursor(1, 0);
if (!TextUtils.isEmpty(toLeft)
- && !isWordSeparator(toLeft.charAt(0))
- && !isSuggestedPunctuation(toLeft.charAt(0))) {
+ && !mSettingsValues.isWordSeparator(toLeft.charAt(0))
+ && !mSettingsValues.isSuggestedPunctuation(toLeft.charAt(0))) {
return true;
}
if (!TextUtils.isEmpty(toRight)
- && !isWordSeparator(toRight.charAt(0))
- && !isSuggestedPunctuation(toRight.charAt(0))) {
+ && !mSettingsValues.isWordSeparator(toRight.charAt(0))
+ && !mSettingsValues.isSuggestedPunctuation(toRight.charAt(0))) {
return true;
}
return false;
@@ -2191,165 +1791,140 @@ public class LatinIME extends InputMethodService
public void revertLastWord(boolean deleteChar) {
final int length = mComposing.length();
- if (!mPredicting && length > 0) {
+ if (!mHasUncommittedTypedChars && length > 0) {
final InputConnection ic = getCurrentInputConnection();
- mPredicting = true;
- mJustRevertedSeparator = ic.getTextBeforeCursor(1, 0);
+ final CharSequence punctuation = ic.getTextBeforeCursor(1, 0);
if (deleteChar) ic.deleteSurroundingText(1, 0);
int toDelete = mCommittedLength;
- CharSequence toTheLeft = ic.getTextBeforeCursor(mCommittedLength, 0);
- if (toTheLeft != null && toTheLeft.length() > 0
- && isWordSeparator(toTheLeft.charAt(0))) {
+ final CharSequence toTheLeft = ic.getTextBeforeCursor(mCommittedLength, 0);
+ if (!TextUtils.isEmpty(toTheLeft)
+ && mSettingsValues.isWordSeparator(toTheLeft.charAt(0))) {
toDelete--;
}
ic.deleteSurroundingText(toDelete, 0);
- ic.setComposingText(mComposing, 1);
- TextEntryState.backspace();
- postUpdateSuggestions();
+ // Re-insert punctuation only when the deleted character was word separator and the
+ // composing text wasn't equal to the auto-corrected text.
+ if (deleteChar
+ && !TextUtils.isEmpty(punctuation)
+ && mSettingsValues.isWordSeparator(punctuation.charAt(0))
+ && !TextUtils.equals(mComposing, toTheLeft)) {
+ ic.commitText(mComposing, 1);
+ TextEntryState.acceptedTyped(mComposing);
+ ic.commitText(punctuation, 1);
+ TextEntryState.typedCharacter(punctuation.charAt(0), true,
+ WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE);
+ // Clear composing text
+ mComposing.setLength(0);
+ } else {
+ mHasUncommittedTypedChars = true;
+ ic.setComposingText(mComposing, 1);
+ TextEntryState.backspace();
+ }
+ mHandler.cancelUpdateBigramPredictions();
+ mHandler.postUpdateSuggestions();
} else {
sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
- mJustRevertedSeparator = null;
}
}
- protected String getWordSeparators() {
- return mWordSeparators;
+ public boolean revertDoubleSpace() {
+ mHandler.cancelDoubleSpacesTimer();
+ final InputConnection ic = getCurrentInputConnection();
+ // Here we test whether we indeed have a period and a space before us. This should not
+ // be needed, but it's there just in case something went wrong.
+ final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
+ if (!". ".equals(textBeforeCursor))
+ return false;
+ ic.beginBatchEdit();
+ ic.deleteSurroundingText(2, 0);
+ ic.commitText(" ", 1);
+ ic.endBatchEdit();
+ return true;
}
public boolean isWordSeparator(int code) {
- String separators = getWordSeparators();
- return separators.contains(String.valueOf((char)code));
- }
-
- private boolean isSentenceSeparator(int code) {
- return mSentenceSeparators.contains(String.valueOf((char)code));
+ return mSettingsValues.isWordSeparator(code);
}
- private void sendSpace() {
- sendKeyChar((char)KEYCODE_SPACE);
- updateShiftKeyState(getCurrentInputEditorInfo());
- //onKey(KEY_SPACE[0], KEY_SPACE);
+ private void sendMagicSpace() {
+ sendKeyChar((char)Keyboard.CODE_SPACE);
+ mJustAddedMagicSpace = true;
+ mKeyboardSwitcher.updateShiftState();
}
public boolean preferCapitalization() {
return mWord.isFirstCharCapitalized();
}
- private void toggleLanguage(boolean reset, boolean next) {
- if (reset) {
- mLanguageSwitcher.reset();
- } else {
- if (next) {
- mLanguageSwitcher.next();
- } else {
- mLanguageSwitcher.prev();
- }
- }
- int currentKeyboardMode = mKeyboardSwitcher.getKeyboardMode();
- reloadKeyboards();
- mKeyboardSwitcher.makeKeyboards(true);
- mKeyboardSwitcher.setKeyboardMode(currentKeyboardMode, 0,
- mEnableVoiceButton && mEnableVoice);
- initSuggest(mLanguageSwitcher.getInputLanguage());
- mLanguageSwitcher.persist();
- updateShiftKeyState(getCurrentInputEditorInfo());
- }
-
- public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
- String key) {
- if (PREF_SELECTED_LANGUAGES.equals(key)) {
- mLanguageSwitcher.loadLocales(sharedPreferences);
- mRefreshKeyboardRequired = true;
- } else if (PREF_RECORRECTION_ENABLED.equals(key)) {
- mReCorrectionEnabled = sharedPreferences.getBoolean(PREF_RECORRECTION_ENABLED,
- getResources().getBoolean(R.bool.default_recorrection_enabled));
- }
- }
-
- public void swipeRight() {
- if (LatinKeyboardView.DEBUG_AUTO_PLAY) {
- ClipboardManager cm = ((ClipboardManager)getSystemService(CLIPBOARD_SERVICE));
- CharSequence text = cm.getText();
- if (!TextUtils.isEmpty(text)) {
- mKeyboardSwitcher.getInputView().startPlaying(text.toString());
- }
- }
- }
-
- public void swipeLeft() {
+ // Notify that language or mode have been changed and toggleLanguage will update KeyboardID
+ // according to new language or mode.
+ public void onRefreshKeyboard() {
+ // Reload keyboard because the current language has been changed.
+ mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(),
+ mSubtypeSwitcher.isShortcutImeEnabled() && mVoiceProxy.isVoiceButtonEnabled(),
+ mVoiceProxy.isVoiceButtonOnPrimary());
+ initSuggest();
+ loadSettings();
+ mKeyboardSwitcher.updateShiftState();
}
- public void swipeDown() {
- handleClose();
- }
+ // "reset" and "next" are used only for USE_SPACEBAR_LANGUAGE_SWITCHER.
+ private void toggleLanguage(boolean next) {
+ if (mSubtypeSwitcher.useSpacebarLanguageSwitcher()) {
+ mSubtypeSwitcher.toggleLanguage(next);
+ }
+ // The following is necessary because on API levels < 10, we don't get notified when
+ // subtype changes.
+ if (!CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED)
+ onRefreshKeyboard();
+ }
- public void swipeUp() {
- //launchSettings();
+ @Override
+ public void onSwipeDown() {
+ if (mSettingsValues.mSwipeDownDismissKeyboardEnabled)
+ handleClose();
}
- public void onPress(int primaryCode) {
+ @Override
+ public void onPress(int primaryCode, boolean withSliding) {
if (mKeyboardSwitcher.isVibrateAndSoundFeedbackRequired()) {
vibrate();
playKeyClick(primaryCode);
}
- final boolean distinctMultiTouch = mKeyboardSwitcher.hasDistinctMultitouch();
- if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_SHIFT) {
- mShiftKeyState.onPress();
- handleShift();
- } else if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_MODE_CHANGE) {
- changeKeyboardMode();
- mSymbolKeyState.onPress();
- mKeyboardSwitcher.setAutoModeSwitchStateMomentary();
+ KeyboardSwitcher switcher = mKeyboardSwitcher;
+ final boolean distinctMultiTouch = switcher.hasDistinctMultitouch();
+ if (distinctMultiTouch && primaryCode == Keyboard.CODE_SHIFT) {
+ switcher.onPressShift(withSliding);
+ } else if (distinctMultiTouch && primaryCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
+ switcher.onPressSymbol();
} else {
- mShiftKeyState.onOtherKeyPressed();
- mSymbolKeyState.onOtherKeyPressed();
+ switcher.onOtherKeyPressed();
}
}
- public void onRelease(int primaryCode) {
+ @Override
+ public void onRelease(int primaryCode, boolean withSliding) {
+ KeyboardSwitcher switcher = mKeyboardSwitcher;
// Reset any drag flags in the keyboard
- ((LatinKeyboard) mKeyboardSwitcher.getInputView().getKeyboard()).keyReleased();
- //vibrate();
- final boolean distinctMultiTouch = mKeyboardSwitcher.hasDistinctMultitouch();
- if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_SHIFT) {
- if (mShiftKeyState.isMomentary())
- resetShift();
- mShiftKeyState.onRelease();
- } else if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_MODE_CHANGE) {
- // Snap back to the previous keyboard mode if the user chords the mode change key and
- // other key, then released the mode change key.
- if (mKeyboardSwitcher.isInChordingAutoModeSwitchState())
- changeKeyboardMode();
- mSymbolKeyState.onRelease();
+ final boolean distinctMultiTouch = switcher.hasDistinctMultitouch();
+ if (distinctMultiTouch && primaryCode == Keyboard.CODE_SHIFT) {
+ switcher.onReleaseShift(withSliding);
+ } else if (distinctMultiTouch && primaryCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
+ switcher.onReleaseSymbol();
}
}
- private FieldContext makeFieldContext() {
- return new FieldContext(
- getCurrentInputConnection(),
- getCurrentInputEditorInfo(),
- mLanguageSwitcher.getInputLanguage(),
- mLanguageSwitcher.getEnabledLanguages());
- }
-
- private boolean fieldCanDoVoice(FieldContext fieldContext) {
- return !mPasswordText
- && mVoiceInput != null
- && !mVoiceInput.isBlacklistedField(fieldContext);
- }
-
- private boolean shouldShowVoiceButton(FieldContext fieldContext, EditorInfo attribute) {
- return ENABLE_VOICE_BUTTON && fieldCanDoVoice(fieldContext)
- && !(attribute != null
- && IME_OPTION_NO_MICROPHONE.equals(attribute.privateImeOptions))
- && SpeechRecognizer.isRecognitionAvailable(this);
- }
- // 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);
+ }
}
};
@@ -2359,7 +1934,7 @@ public class LatinIME extends InputMethodService
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
}
if (mAudioManager != null) {
- mSilentMode = (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL);
+ mSilentModeOn = (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL);
}
}
@@ -2367,22 +1942,22 @@ public class LatinIME extends InputMethodService
// if mAudioManager is null, we don't have the ringer state yet
// mAudioManager will be set by updateRingerMode
if (mAudioManager == null) {
- if (mKeyboardSwitcher.getInputView() != null) {
+ if (mKeyboardSwitcher.getKeyboardView() != null) {
updateRingerMode();
}
}
- if (mSoundOn && !mSilentMode) {
+ if (isSoundOn()) {
// FIXME: Volume and enable should come from UI settings
// FIXME: These should be triggered after auto-repeat logic
int sound = AudioManager.FX_KEYPRESS_STANDARD;
switch (primaryCode) {
- case Keyboard.KEYCODE_DELETE:
+ case Keyboard.CODE_DELETE:
sound = AudioManager.FX_KEYPRESS_DELETE;
break;
- case KEYCODE_ENTER:
+ case Keyboard.CODE_ENTER:
sound = AudioManager.FX_KEYPRESS_RETURN;
break;
- case KEYCODE_SPACE:
+ case Keyboard.CODE_SPACE:
sound = AudioManager.FX_KEYPRESS_SPACEBAR;
break;
}
@@ -2390,81 +1965,69 @@ public class LatinIME extends InputMethodService
}
}
- private void vibrate() {
- if (!mVibrateOn) {
+ public void vibrate() {
+ if (!mSettingsValues.mVibrateOn) {
return;
}
- if (mKeyboardSwitcher.getInputView() != null) {
- mKeyboardSwitcher.getInputView().performHapticFeedback(
+ LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
+ if (inputView != null) {
+ inputView.performHapticFeedback(
HapticFeedbackConstants.KEYBOARD_TAP,
HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
}
}
- private void checkTutorial(String privateImeOptions) {
- if (privateImeOptions == null) return;
- if (privateImeOptions.equals("com.android.setupwizard:ShowTutorial")) {
- if (mTutorial == null) startTutorial();
- } else if (privateImeOptions.equals("com.android.setupwizard:HideTutorial")) {
- if (mTutorial != null) {
- if (mTutorial.close()) {
- mTutorial = null;
- }
- }
- }
- }
-
- private void startTutorial() {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_TUTORIAL), 500);
- }
-
- /* package */ void tutorialDone() {
- mTutorial = null;
- }
-
- /* package */ void promoteToUserDictionary(String word, int frequency) {
- if (mUserDictionary.isValidWord(word)) return;
- mUserDictionary.addWord(word, frequency);
- }
-
- /* package */ WordComposer getCurrentWord() {
+ public WordComposer getCurrentWord() {
return mWord;
}
- /* package */ boolean getPopupOn() {
- return mPopupOn;
+ boolean isSoundOn() {
+ return mSettingsValues.mSoundOn && !mSilentModeOn;
}
private void updateCorrectionMode() {
+ // TODO: cleanup messy flags
mHasDictionary = mSuggest != null ? mSuggest.hasMainDictionary() : false;
- mAutoCorrectOn = (mAutoCorrectEnabled || mQuickFixes)
- && !mInputTypeNoAutoCorrect && mHasDictionary;
- mCorrectionMode = (mAutoCorrectOn && mAutoCorrectEnabled)
+ final boolean shouldAutoCorrect = (mSettingsValues.mAutoCorrectEnabled
+ || mSettingsValues.mQuickFixes) && !mInputTypeNoAutoCorrect && mHasDictionary;
+ mCorrectionMode = (shouldAutoCorrect && mSettingsValues.mAutoCorrectEnabled)
? Suggest.CORRECTION_FULL
- : (mAutoCorrectOn ? Suggest.CORRECTION_BASIC : Suggest.CORRECTION_NONE);
- mCorrectionMode = (mBigramSuggestionEnabled && mAutoCorrectOn && mAutoCorrectEnabled)
+ : (shouldAutoCorrect ? Suggest.CORRECTION_BASIC : Suggest.CORRECTION_NONE);
+ mCorrectionMode = (mSettingsValues.mBigramSuggestionEnabled && shouldAutoCorrect
+ && mSettingsValues.mAutoCorrectEnabled)
? Suggest.CORRECTION_FULL_BIGRAM : mCorrectionMode;
if (mSuggest != null) {
mSuggest.setCorrectionMode(mCorrectionMode);
}
}
- private void updateAutoTextEnabled(Locale systemLocale) {
+ private void updateAutoTextEnabled() {
if (mSuggest == null) return;
- boolean different =
- !systemLocale.getLanguage().equalsIgnoreCase(mInputLocale.substring(0, 2));
- mSuggest.setAutoTextEnabled(!different && mQuickFixes);
+ mSuggest.setQuickFixesEnabled(mSettingsValues.mQuickFixes
+ && SubtypeSwitcher.getInstance().isSystemLanguageSameAsInputLanguage());
+ }
+
+ private void updateSuggestionVisibility(final SharedPreferences prefs, final Resources res) {
+ final String suggestionVisiblityStr = prefs.getString(
+ Settings.PREF_SHOW_SUGGESTIONS_SETTING,
+ res.getString(R.string.prefs_suggestion_visibility_default_value));
+ for (int visibility : SUGGESTION_VISIBILITY_VALUE_ARRAY) {
+ if (suggestionVisiblityStr.equals(res.getString(visibility))) {
+ mSuggestionVisibility = visibility;
+ break;
+ }
+ }
}
protected void launchSettings() {
- launchSettings(LatinIMESettings.class);
+ launchSettings(Settings.class);
}
public void launchDebugSettings() {
- launchSettings(LatinIMEDebugSettings.class);
+ launchSettings(DebugSettings.class);
}
- protected void launchSettings (Class<? extends PreferenceActivity> settingsClass) {
+ protected void launchSettings(Class<? extends PreferenceActivity> settingsClass) {
handleClose();
Intent intent = new Intent();
intent.setClass(LatinIME.this, settingsClass);
@@ -2472,122 +2035,78 @@ public class LatinIME extends InputMethodService
startActivity(intent);
}
- private void loadSettings() {
- // Get the settings preferences
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
- mVibrateOn = sp.getBoolean(PREF_VIBRATE_ON, false);
- mSoundOn = sp.getBoolean(PREF_SOUND_ON, false);
- mPopupOn = sp.getBoolean(PREF_POPUP_ON,
- mResources.getBoolean(R.bool.default_popup_preview));
- mAutoCap = sp.getBoolean(PREF_AUTO_CAP, true);
- mQuickFixes = sp.getBoolean(PREF_QUICK_FIXES, true);
- mHasUsedVoiceInput = sp.getBoolean(PREF_HAS_USED_VOICE_INPUT, false);
- mHasUsedVoiceInputUnsupportedLocale =
- sp.getBoolean(PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE, false);
-
- // Get the current list of supported locales and check the current locale against that
- // list. We cache this value so as not to check it every time the user starts a voice
- // input. Because this method is called by onStartInputView, this should mean that as
- // long as the locale doesn't change while the user is keeping the IME open, the
- // value should never be stale.
- String supportedLocalesString = SettingsUtil.getSettingsString(
- getContentResolver(),
- SettingsUtil.LATIN_IME_VOICE_INPUT_SUPPORTED_LOCALES,
- DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES);
- ArrayList<String> voiceInputSupportedLocales =
- newArrayList(supportedLocalesString.split("\\s+"));
-
- mLocaleSupportedForVoiceInput = voiceInputSupportedLocales.contains(mInputLocale);
-
- mShowSuggestions = sp.getBoolean(PREF_SHOW_SUGGESTIONS, true);
-
- if (VOICE_INSTALLED) {
- final String voiceMode = sp.getString(PREF_VOICE_MODE,
- getString(R.string.voice_mode_main));
- boolean enableVoice = !voiceMode.equals(getString(R.string.voice_mode_off))
- && mEnableVoiceButton;
- boolean voiceOnPrimary = voiceMode.equals(getString(R.string.voice_mode_main));
- if (mKeyboardSwitcher != null &&
- (enableVoice != mEnableVoice || voiceOnPrimary != mVoiceOnPrimary)) {
- mKeyboardSwitcher.setVoiceMode(enableVoice, voiceOnPrimary);
+ 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 = CompatUtils.getInputLanguageSelectionIntent(
+ mInputMethodId, Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ break;
+ case 1:
+ launchSettings();
+ break;
+ }
}
- 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);
+ };
+ showOptionsMenuInternal(title, items, listener);
}
- private void initSuggestPuncList() {
- mSuggestPuncList = new ArrayList<CharSequence>();
- mSuggestPuncs = mResources.getString(R.string.suggested_punctuations);
- if (mSuggestPuncs != null) {
- for (int i = 0; i < mSuggestPuncs.length(); i++) {
- mSuggestPuncList.add(mSuggestPuncs.subSequence(i, i + 1));
+ 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 0:
+ mImm.showInputMethodPicker();
+ break;
+ case 1:
+ launchSettings();
+ break;
+ }
}
- }
+ };
+ showOptionsMenuInternal(title, items, listener);
}
- private boolean isSuggestedPunctuation(int code) {
- return mSuggestPuncs.contains(String.valueOf((char)code));
- }
-
- private void showOptionsMenu() {
+ private void showOptionsMenuInternal(CharSequence title, CharSequence[] items,
+ DialogInterface.OnClickListener listener) {
+ final IBinder windowToken = mKeyboardSwitcher.getKeyboardView().getWindowToken();
+ if (windowToken == null) return;
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() {
-
- public void onClick(DialogInterface di, int position) {
- di.dismiss();
- switch (position) {
- case POS_SETTINGS:
- launchSettings();
- break;
- case POS_METHOD:
- ((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE))
- .showInputMethodPicker();
- break;
- }
- }
- });
- builder.setTitle(mResources.getString(R.string.english_ime_input_options));
+ 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();
+ lp.token = windowToken;
lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
window.setAttributes(lp);
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
mOptionsDialog.show();
}
- public void changeKeyboardMode() {
- mKeyboardSwitcher.toggleSymbols();
- if (mCapsLock && mKeyboardSwitcher.isAlphabetMode()) {
- mKeyboardSwitcher.setShiftLocked(mCapsLock);
- }
-
- updateShiftKeyState(getCurrentInputEditorInfo());
- }
-
- public static <E> ArrayList<E> newArrayList(E... elements) {
- int capacity = (elements.length * 110) / 100 + 5;
- ArrayList<E> list = new ArrayList<E>(capacity);
- Collections.addAll(list, elements);
- return list;
- }
-
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
super.dump(fd, fout, args);
@@ -2595,18 +2114,17 @@ public class LatinIME extends InputMethodService
final Printer p = new PrintWriterPrinter(fout);
p.println("LatinIME state :");
p.println(" Keyboard mode = " + mKeyboardSwitcher.getKeyboardMode());
- p.println(" mCapsLock=" + mCapsLock);
p.println(" mComposing=" + mComposing.toString());
- p.println(" mPredictionOn=" + mPredictionOn);
+ p.println(" mIsSuggestionsRequested=" + mIsSettingsSuggestionStripOn);
p.println(" mCorrectionMode=" + mCorrectionMode);
- p.println(" mPredicting=" + mPredicting);
- p.println(" mAutoCorrectOn=" + mAutoCorrectOn);
- p.println(" mAutoSpace=" + mAutoSpace);
- p.println(" mCompletionOn=" + mCompletionOn);
+ p.println(" mHasUncommittedTypedChars=" + mHasUncommittedTypedChars);
+ p.println(" mAutoCorrectEnabled=" + mSettingsValues.mAutoCorrectEnabled);
+ p.println(" mShouldInsertMagicSpace=" + mShouldInsertMagicSpace);
+ p.println(" mApplicationSpecifiedCompletionOn=" + mApplicationSpecifiedCompletionOn);
p.println(" TextEntryState.state=" + TextEntryState.getState());
- p.println(" mSoundOn=" + mSoundOn);
- p.println(" mVibrateOn=" + mVibrateOn);
- p.println(" mPopupOn=" + mPopupOn);
+ p.println(" mSoundOn=" + mSettingsValues.mSoundOn);
+ p.println(" mVibrateOn=" + mSettingsValues.mVibrateOn);
+ p.println(" mKeyPreviewPopupOn=" + mSettingsValues.mKeyPreviewPopupOn);
}
// Characters per second measurement
@@ -2626,8 +2144,4 @@ public class LatinIME extends InputMethodService
for (int i = 0; i < CPS_BUFFER_SIZE; i++) total += mCpsIntervals[i];
System.out.println("CPS = " + ((CPS_BUFFER_SIZE * 1000f) / total));
}
-
- public void onAutoCompletionStateChanged(boolean isAutoCompletion) {
- mKeyboardSwitcher.onAutoCompletionStateChanged(isAutoCompletion);
- }
}