diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/LatinIME.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/LatinIME.java | 616 |
1 files changed, 295 insertions, 321 deletions
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index d57db8e9a..330be377b 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -16,9 +16,9 @@ package com.android.inputmethod.latin; -import static com.android.inputmethod.latin.Constants.ImeOption.FORCE_ASCII; -import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE; -import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT; +import static com.android.inputmethod.latin.common.Constants.ImeOption.FORCE_ASCII; +import static com.android.inputmethod.latin.common.Constants.ImeOption.NO_MICROPHONE; +import static com.android.inputmethod.latin.common.Constants.ImeOption.NO_MICROPHONE_COMPAT; import android.app.AlertDialog; import android.content.BroadcastReceiver; @@ -31,13 +31,11 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.inputmethodservice.InputMethodService; import android.media.AudioManager; -import android.net.ConnectivityManager; import android.os.Debug; import android.os.IBinder; import android.os.Message; import android.preference.PreferenceManager; import android.text.InputType; -import android.text.TextUtils; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Printer; @@ -46,19 +44,17 @@ import android.view.Gravity; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup.LayoutParams; -import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; -import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodSubtype; -import android.widget.TextView; import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper; import com.android.inputmethod.compat.InputMethodServiceCompatUtils; +import com.android.inputmethod.compat.ViewOutlineProviderCompatUtils; +import com.android.inputmethod.compat.ViewOutlineProviderCompatUtils.InsetsUpdater; import com.android.inputmethod.dictionarypack.DictionaryPackConstants; import com.android.inputmethod.event.Event; import com.android.inputmethod.event.HardwareEventDecoder; @@ -69,32 +65,29 @@ import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.MainKeyboardView; -import com.android.inputmethod.keyboard.TextDecoratorUi; import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.CoordinateUtils; +import com.android.inputmethod.latin.common.InputPointers; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.define.ProductionFlags; import com.android.inputmethod.latin.inputlogic.InputLogic; -import com.android.inputmethod.latin.personalization.ContextualDictionaryUpdater; -import com.android.inputmethod.latin.personalization.DictionaryDecayBroadcastReciever; -import com.android.inputmethod.latin.personalization.PersonalizationDictionaryUpdater; import com.android.inputmethod.latin.personalization.PersonalizationHelper; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.settings.SettingsActivity; import com.android.inputmethod.latin.settings.SettingsValues; import com.android.inputmethod.latin.suggestions.SuggestionStripView; import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor; +import com.android.inputmethod.latin.touchinputconsumer.GestureConsumer; import com.android.inputmethod.latin.utils.ApplicationUtils; -import com.android.inputmethod.latin.utils.CapsModeUtils; -import com.android.inputmethod.latin.utils.CoordinateUtils; -import com.android.inputmethod.latin.utils.CursorAnchorInfoUtils; import com.android.inputmethod.latin.utils.DialogUtils; -import com.android.inputmethod.latin.utils.DistracterFilterCheckingExactMatchesAndSuggestions; import com.android.inputmethod.latin.utils.ImportantNoticeUtils; import com.android.inputmethod.latin.utils.IntentUtils; import com.android.inputmethod.latin.utils.JniUtils; import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; import com.android.inputmethod.latin.utils.StatsUtils; +import com.android.inputmethod.latin.utils.StatsUtilsManager; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import com.android.inputmethod.latin.utils.ViewLayoutUtils; @@ -105,6 +98,8 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.TimeUnit; +import javax.annotation.Nonnull; + /** * Input method implementation for Qwerty'ish keyboard. */ @@ -112,17 +107,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen SuggestionStripView.Listener, SuggestionStripViewAccessor, DictionaryFacilitator.DictionaryInitializationListener, ImportantNoticeDialog.ImportantNoticeDialogListener { - private static final String TAG = LatinIME.class.getSimpleName(); + static final String TAG = LatinIME.class.getSimpleName(); private static final boolean TRACE = false; - private static boolean DEBUG = false; private static final int EXTENDED_TOUCHABLE_REGION_HEIGHT = 100; - - private static final int PENDING_IMS_CALLBACK_DURATION = 800; - - private static final int DELAY_WAIT_FOR_DICTIONARY_LOAD = 2000; // 2s - private static final int PERIOD_FOR_AUDIO_AND_HAPTIC_FEEDBACK_IN_KEY_REPEAT = 2; + private static final int PENDING_IMS_CALLBACK_DURATION_MILLIS = 800; + static final long DELAY_WAIT_FOR_DICTIONARY_LOAD_MILLIS = TimeUnit.SECONDS.toMillis(2); + static final long DELAY_DEALLOCATE_MEMORY_MILLIS = TimeUnit.SECONDS.toMillis(10); /** * The name of the scheme used by the Package Manager to warn of a new package installation, @@ -130,22 +122,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen */ private static final String SCHEME_PACKAGE = "package"; - private final Settings mSettings; + final Settings mSettings; private final DictionaryFacilitator mDictionaryFacilitator = - new DictionaryFacilitator( - new DistracterFilterCheckingExactMatchesAndSuggestions(this /* context */)); - // TODO: Move from LatinIME. - private final PersonalizationDictionaryUpdater mPersonalizationDictionaryUpdater = - new PersonalizationDictionaryUpdater(this /* context */, mDictionaryFacilitator); - private final ContextualDictionaryUpdater mContextualDictionaryUpdater = - new ContextualDictionaryUpdater(this /* context */, mDictionaryFacilitator, - new Runnable() { - @Override - public void run() { - mHandler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_NONE); - } - }); - private final InputLogic mInputLogic = new InputLogic(this /* LatinIME */, + DictionaryFacilitatorProvider.getDictionaryFacilitator( + false /* isNeededForSpellChecking */); + final InputLogic mInputLogic = new InputLogic(this /* LatinIME */, this /* SuggestionStripViewAccessor */, mDictionaryFacilitator); // We expect to have only one decoder in almost all cases, hence the default capacity of 1. // If it turns out we need several, it will get grown seamlessly. @@ -153,14 +134,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // TODO: Move these {@link View}s to {@link KeyboardSwitcher}. private View mInputView; + private InsetsUpdater mInsetsUpdater; private SuggestionStripView mSuggestionStripView; - private TextView mExtractEditText; private RichInputMethodManager mRichImm; @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher; - private final SubtypeSwitcher mSubtypeSwitcher; private final SubtypeState mSubtypeState = new SubtypeState(); - private final SpecialKeyDetector mSpecialKeyDetector; + private final EmojiAltPhysicalKeyDetector mEmojiAltPhysicalKeyDetector = + new EmojiAltPhysicalKeyDetector(mInputLogic.mConnection); + private StatsUtilsManager mStatsUtilsManager; // Working variable for {@link #startShowingInputView()} and // {@link #onEvaluateInputViewShown()}. private boolean mIsExecutingStartShowingInputView; @@ -176,6 +158,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private final boolean mIsHardwareAcceleratedDrawingEnabled; + private GestureConsumer mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER; + public final UIHandler mHandler = new UIHandler(this); public static final class UIHandler extends LeakGuardHandlerWrapper<LatinIME> { @@ -188,20 +172,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final int MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED = 6; private static final int MSG_RESET_CACHES = 7; private static final int MSG_WAIT_FOR_DICTIONARY_LOAD = 8; + private static final int MSG_DEALLOCATE_MEMORY = 9; + private static final int MSG_RESUME_SUGGESTIONS_FOR_START_INPUT = 10; // Update this when adding new messages - private static final int MSG_LAST = MSG_WAIT_FOR_DICTIONARY_LOAD; + private static final int MSG_LAST = MSG_RESUME_SUGGESTIONS_FOR_START_INPUT; private static final int ARG1_NOT_GESTURE_INPUT = 0; private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1; private static final int ARG1_SHOW_GESTURE_FLOATING_PREVIEW_TEXT = 2; private static final int ARG2_UNUSED = 0; - private static final int ARG1_FALSE = 0; private static final int ARG1_TRUE = 1; private int mDelayInMillisecondsToUpdateSuggestions; private int mDelayInMillisecondsToUpdateShiftState; - public UIHandler(final LatinIME ownerInstance) { + public UIHandler(@Nonnull final LatinIME ownerInstance) { super(ownerInstance); } @@ -245,20 +230,26 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen break; case MSG_RESUME_SUGGESTIONS: latinIme.mInputLogic.restartSuggestionsOnWordTouchedByCursor( - latinIme.mSettings.getCurrent(), - msg.arg1 == ARG1_TRUE /* shouldIncludeResumedWordInSuggestions */, + latinIme.mSettings.getCurrent(), false /* forStartInput */, + latinIme.mKeyboardSwitcher.getCurrentKeyboardScriptId()); + break; + case MSG_RESUME_SUGGESTIONS_FOR_START_INPUT: + latinIme.mInputLogic.restartSuggestionsOnWordTouchedByCursor( + latinIme.mSettings.getCurrent(), true /* forStartInput */, latinIme.mKeyboardSwitcher.getCurrentKeyboardScriptId()); break; case MSG_REOPEN_DICTIONARIES: // We need to re-evaluate the currently composing word in case the script has // changed. postWaitForDictionaryLoad(); - latinIme.resetSuggest(); + latinIme.resetDictionaryFacilitatorIfNecessary(); break; case MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED: + final SuggestedWords suggestedWords = (SuggestedWords) msg.obj; latinIme.mInputLogic.onUpdateTailBatchInputCompleted( latinIme.mSettings.getCurrent(), - (SuggestedWords) msg.obj, latinIme.mKeyboardSwitcher); + suggestedWords, latinIme.mKeyboardSwitcher); + latinIme.onTailBatchInputResultShown(suggestedWords); break; case MSG_RESET_CACHES: final SettingsValues settingsValues = latinIme.mSettings.getCurrent(); @@ -275,6 +266,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen case MSG_WAIT_FOR_DICTIONARY_LOAD: Log.i(TAG, "Timeout waiting for dictionary load"); break; + case MSG_DEALLOCATE_MEMORY: + latinIme.deallocateMemory(); + break; } } @@ -287,8 +281,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen sendMessage(obtainMessage(MSG_REOPEN_DICTIONARIES)); } - public void postResumeSuggestions(final boolean shouldIncludeResumedWordInSuggestions, - final boolean shouldDelay) { + private void postResumeSuggestionsInternal(final boolean shouldDelay, + final boolean forStartInput) { final LatinIME latinIme = getOwnerInstance(); if (latinIme == null) { return; @@ -297,17 +291,25 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } removeMessages(MSG_RESUME_SUGGESTIONS); + removeMessages(MSG_RESUME_SUGGESTIONS_FOR_START_INPUT); + final int message = forStartInput ? MSG_RESUME_SUGGESTIONS_FOR_START_INPUT + : MSG_RESUME_SUGGESTIONS; if (shouldDelay) { - sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS, - shouldIncludeResumedWordInSuggestions ? ARG1_TRUE : ARG1_FALSE, - 0 /* ignored */), mDelayInMillisecondsToUpdateSuggestions); + sendMessageDelayed(obtainMessage(message), + mDelayInMillisecondsToUpdateSuggestions); } else { - sendMessage(obtainMessage(MSG_RESUME_SUGGESTIONS, - shouldIncludeResumedWordInSuggestions ? ARG1_TRUE : ARG1_FALSE, - 0 /* ignored */)); + sendMessage(obtainMessage(message)); } } + public void postResumeSuggestions(final boolean shouldDelay) { + postResumeSuggestionsInternal(shouldDelay, false /* forStartInput */); + } + + public void postResumeSuggestionsForStartInput(final boolean shouldDelay) { + postResumeSuggestionsInternal(shouldDelay, true /* forStartInput */); + } + public void postResetCaches(final boolean tryResumeSuggestions, final int remainingTries) { removeMessages(MSG_RESET_CACHES); sendMessage(obtainMessage(MSG_RESET_CACHES, tryResumeSuggestions ? 1 : 0, @@ -316,7 +318,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void postWaitForDictionaryLoad() { sendMessageDelayed(obtainMessage(MSG_WAIT_FOR_DICTIONARY_LOAD), - DELAY_WAIT_FOR_DICTIONARY_LOAD); + DELAY_WAIT_FOR_DICTIONARY_LOAD_MILLIS); } public void cancelWaitForDictionaryLoad() { @@ -345,6 +347,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mDelayInMillisecondsToUpdateShiftState); } + public void postDeallocateMemory() { + sendMessageDelayed(obtainMessage(MSG_DEALLOCATE_MEMORY), + DELAY_DEALLOCATE_MEMORY_MILLIS); + } + + public void cancelDeallocateMemory() { + removeMessages(MSG_DEALLOCATE_MEMORY); + } + + public boolean hasPendingDeallocateMemory() { + return hasMessages(MSG_DEALLOCATE_MEMORY); + } + @UsedForTesting public void removeAllMessages() { for (int i = 0; i <= MSG_LAST; ++i) { @@ -442,7 +457,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mPendingSuccessiveImsCallback = false; resetPendingImsCallback(); sendMessageDelayed(obtainMessage(MSG_PENDING_IMS_CALLBACK), - PENDING_IMS_CALLBACK_DURATION); + PENDING_IMS_CALLBACK_DURATION_MILLIS); } final LatinIME latinIme = getOwnerInstance(); if (latinIme != null) { @@ -450,6 +465,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen latinIme.onStartInputViewInternal(editorInfo, restarting); mAppliedEditorInfo = editorInfo; } + cancelDeallocateMemory(); } } @@ -463,6 +479,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen latinIme.onFinishInputViewInternal(finishingInput); mAppliedEditorInfo = null; } + if (!hasPendingDeallocateMemory()) { + postDeallocateMemory(); + } } } @@ -516,9 +535,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public LatinIME() { super(); mSettings = Settings.getInstance(); - mSubtypeSwitcher = SubtypeSwitcher.getInstance(); mKeyboardSwitcher = KeyboardSwitcher.getInstance(); - mSpecialKeyDetector = new SpecialKeyDetector(this); + mStatsUtilsManager = StatsUtilsManager.getInstance(); mIsHardwareAcceleratedDrawingEnabled = InputMethodServiceCompatUtils.enableHardwareAcceleration(this); Log.i(TAG, "Hardware accelerated drawing: " + mIsHardwareAcceleratedDrawingEnabled); @@ -530,28 +548,25 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen DebugFlags.init(PreferenceManager.getDefaultSharedPreferences(this)); RichInputMethodManager.init(this); mRichImm = RichInputMethodManager.getInstance(); - SubtypeSwitcher.init(this); KeyboardSwitcher.init(this); AudioAndHapticFeedbackManager.init(this); AccessibilityUtils.init(this); - StatsUtils.init(this); - + mStatsUtilsManager.onCreate(this /* context */, mDictionaryFacilitator); super.onCreate(); mHandler.onCreate(); - DEBUG = DebugFlags.DEBUG_ENABLED; - // TODO: Resolve mutual dependencies of {@link #loadSettings()} and {@link #initSuggest()}. + // TODO: Resolve mutual dependencies of {@link #loadSettings()} and + // {@link #resetDictionaryFacilitatorIfNecessary()}. loadSettings(); - resetSuggest(); + resetDictionaryFacilitatorIfNecessary(); - // Register to receive ringer mode change and network state change. - // Also receive installation and removal of a dictionary pack. + // Register to receive ringer mode change. final IntentFilter filter = new IntentFilter(); - filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); - registerReceiver(mConnectivityAndRingerModeChangeReceiver, filter); + registerReceiver(mRingerModeChangeReceiver, filter); + // Register to receive installation and removal of a dictionary pack. final IntentFilter packageFilter = new IntentFilter(); packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); @@ -566,15 +581,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen dictDumpFilter.addAction(DictionaryDumpBroadcastReceiver.DICTIONARY_DUMP_INTENT_ACTION); registerReceiver(mDictionaryDumpBroadcastReceiver, dictDumpFilter); - DictionaryDecayBroadcastReciever.setUpIntervalAlarmForDictionaryDecaying(this); - - StatsUtils.onCreate(mSettings.getCurrent()); + StatsUtils.onCreate(mSettings.getCurrent(), mRichImm); } // Has to be package-visible for unit tests @UsedForTesting void loadSettings() { - final Locale locale = mSubtypeSwitcher.getCurrentSubtypeLocale(); + final Locale locale = mRichImm.getCurrentSubtypeLocale(); final EditorInfo editorInfo = getCurrentInputEditorInfo(); final InputAttributes inputAttributes = new InputAttributes( editorInfo, isFullscreenMode(), getPackageName()); @@ -585,30 +598,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // been displayed. Opening dictionaries never affects responsivity as dictionaries are // asynchronously loaded. if (!mHandler.hasPendingReopenDictionaries()) { - resetSuggestForLocale(locale); + resetDictionaryFacilitator(locale); } - mDictionaryFacilitator.updateEnabledSubtypes(mRichImm.getMyEnabledInputMethodSubtypeList( - true /* allowsImplicitlySelectedSubtypes */)); refreshPersonalizationDictionarySession(currentSettingsValues); - StatsUtils.onLoadSettings(currentSettingsValues); + resetDictionaryFacilitatorIfNecessary(); + mStatsUtilsManager.onLoadSettings(this /* context */, currentSettingsValues); } private void refreshPersonalizationDictionarySession( final SettingsValues currentSettingsValues) { - mPersonalizationDictionaryUpdater.onLoadSettings( - currentSettingsValues.mUsePersonalizedDicts, - mSubtypeSwitcher.isSystemLocaleSameAsLocaleOfAllEnabledSubtypesOfEnabledImes()); - mContextualDictionaryUpdater.onLoadSettings(currentSettingsValues.mUsePersonalizedDicts); - final boolean shouldKeepUserHistoryDictionaries; - if (currentSettingsValues.mUsePersonalizedDicts) { - shouldKeepUserHistoryDictionaries = true; - } else { - shouldKeepUserHistoryDictionaries = false; - } - if (!shouldKeepUserHistoryDictionaries) { + if (!currentSettingsValues.mUsePersonalizedDicts) { // Remove user history dictionaries. PersonalizationHelper.removeAllUserHistoryDictionaries(this); - mDictionaryFacilitator.clearUserHistoryDictionary(); + mDictionaryFacilitator.clearUserHistoryDictionary(this); } } @@ -621,44 +623,49 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } if (mHandler.hasPendingWaitForDictionaryLoad()) { mHandler.cancelWaitForDictionaryLoad(); - mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */, - false /* shouldDelay */); + mHandler.postResumeSuggestions(false /* shouldDelay */); } } - private void resetSuggest() { - final Locale switcherSubtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale(); - final String switcherLocaleStr = switcherSubtypeLocale.toString(); + void resetDictionaryFacilitatorIfNecessary() { + final Locale subtypeSwitcherLocale = mRichImm.getCurrentSubtypeLocale(); final Locale subtypeLocale; - if (TextUtils.isEmpty(switcherLocaleStr)) { + if (subtypeSwitcherLocale == null) { // This happens in very rare corner cases - for example, immediately after a switch // to LatinIME has been requested, about a frame later another switch happens. In this // case, we are about to go down but we still don't know it, however the system tells - // us there is no current subtype so the locale is the empty string. Take the best - // possible guess instead -- it's bound to have no consequences, and we have no way - // of knowing anyway. + // us there is no current subtype. Log.e(TAG, "System is reporting no current subtype."); subtypeLocale = getResources().getConfiguration().locale; } else { - subtypeLocale = switcherSubtypeLocale; + subtypeLocale = subtypeSwitcherLocale; + } + if (mDictionaryFacilitator.isForLocale(subtypeLocale) + && mDictionaryFacilitator.isForAccount(mSettings.getCurrent().mAccount)) { + return; } - resetSuggestForLocale(subtypeLocale); + resetDictionaryFacilitator(subtypeLocale); } /** - * Reset suggest by loading dictionaries for the locale and the current settings values. + * Reset the facilitator by loading dictionaries for the given locale and + * the current settings values. * * @param locale the locale */ - private void resetSuggestForLocale(final Locale locale) { + // TODO: make sure the current settings always have the right locales, and read from them. + private void resetDictionaryFacilitator(final Locale locale) { final SettingsValues settingsValues = mSettings.getCurrent(); mDictionaryFacilitator.resetDictionaries(this /* context */, locale, settingsValues.mUseContactsDict, settingsValues.mUsePersonalizedDicts, - false /* forceReloadMainDictionary */, this); + false /* forceReloadMainDictionary */, + settingsValues.mAccount, "" /* dictNamePrefix */, + this /* DictionaryInitializationListener */); if (settingsValues.mAutoCorrectionEnabledPerUserSettings) { mInputLogic.mSuggest.setAutoCorrectionThreshold( settingsValues.mAutoCorrectionThreshold); } + mInputLogic.mSuggest.setPlausibilityThreshold(settingsValues.mPlausibilityThreshold); } /** @@ -668,19 +675,20 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final SettingsValues settingsValues = mSettings.getCurrent(); mDictionaryFacilitator.resetDictionaries(this /* context */, mDictionaryFacilitator.getLocale(), settingsValues.mUseContactsDict, - settingsValues.mUsePersonalizedDicts, true /* forceReloadMainDictionary */, this); + settingsValues.mUsePersonalizedDicts, + true /* forceReloadMainDictionary */, + settingsValues.mAccount, "" /* dictNamePrefix */, + this /* DictionaryInitializationListener */); } @Override public void onDestroy() { mDictionaryFacilitator.closeDictionaries(); - mPersonalizationDictionaryUpdater.onDestroy(); - mContextualDictionaryUpdater.onDestroy(); mSettings.onDestroy(); - unregisterReceiver(mConnectivityAndRingerModeChangeReceiver); + unregisterReceiver(mRingerModeChangeReceiver); unregisterReceiver(mDictionaryPackInstallReceiver); unregisterReceiver(mDictionaryDumpBroadcastReceiver); - StatsUtils.onDestroy(); + mStatsUtilsManager.onDestroy(this /* context */); super.onDestroy(); } @@ -688,7 +696,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void recycle() { unregisterReceiver(mDictionaryPackInstallReceiver); unregisterReceiver(mDictionaryDumpBroadcastReceiver); - unregisterReceiver(mConnectivityAndRingerModeChangeReceiver); + unregisterReceiver(mRingerModeChangeReceiver); mInputLogic.recycle(); } @@ -715,15 +723,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen cleanupInternalStateForFinishInput(); } } - // TODO: Remove this test. - if (!conf.locale.equals(mPersonalizationDictionaryUpdater.getLocale())) { - refreshPersonalizationDictionarySession(settingsValues); - } super.onConfigurationChanged(conf); } @Override public View onCreateInputView() { + StatsUtils.onCreateInputView(); return mKeyboardSwitcher.onCreateInputView(mIsHardwareAcceleratedDrawingEnabled); } @@ -731,60 +736,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void setInputView(final View view) { super.setInputView(view); mInputView = view; + mInsetsUpdater = ViewOutlineProviderCompatUtils.setInsetsOutlineProvider(view); + updateSoftInputWindowLayoutParameters(); mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view); if (hasSuggestionStripView()) { mSuggestionStripView.setListener(this, view); } - mInputLogic.setTextDecoratorUi(new TextDecoratorUi(this, view)); - } - - @Override - public void setExtractView(final View view) { - final TextView prevExtractEditText = mExtractEditText; - super.setExtractView(view); - TextView nextExtractEditText = null; - if (view != null) { - final View extractEditText = view.findViewById(android.R.id.inputExtractEditText); - if (extractEditText instanceof TextView) { - nextExtractEditText = (TextView)extractEditText; - } - } - if (prevExtractEditText == nextExtractEditText) { - return; - } - if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK && prevExtractEditText != null) { - prevExtractEditText.getViewTreeObserver().removeOnPreDrawListener( - mExtractTextViewPreDrawListener); - } - mExtractEditText = nextExtractEditText; - if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK && mExtractEditText != null) { - mExtractEditText.getViewTreeObserver().addOnPreDrawListener( - mExtractTextViewPreDrawListener); - } - } - - private final ViewTreeObserver.OnPreDrawListener mExtractTextViewPreDrawListener = - new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - onExtractTextViewPreDraw(); - return true; - } - }; - - private void onExtractTextViewPreDraw() { - if (!ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK || !isFullscreenMode() - || mExtractEditText == null) { - return; - } - final CursorAnchorInfo info = CursorAnchorInfoUtils.getCursorAnchorInfo(mExtractEditText); - mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.fromObject(info)); } @Override public void setCandidatesView(final View view) { // To ensure that CandidatesView will never be set. - return; } @Override @@ -795,11 +757,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void onStartInputView(final EditorInfo editorInfo, final boolean restarting) { mHandler.onStartInputView(editorInfo, restarting); + mStatsUtilsManager.onStartInputView(); } @Override public void onFinishInputView(final boolean finishingInput) { + StatsUtils.onFinishInputView(); mHandler.onFinishInputView(finishingInput); + mStatsUtilsManager.onFinishInputView(); + mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER; } @Override @@ -811,20 +777,27 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void onCurrentInputMethodSubtypeChanged(final InputMethodSubtype subtype) { // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged() // is not guaranteed. It may even be called at the same time on a different thread. - mSubtypeSwitcher.onSubtypeChanged(subtype); + InputMethodSubtype oldSubtype = mRichImm.getCurrentSubtype().getRawSubtype(); + StatsUtils.onSubtypeChanged(oldSubtype, subtype); + mRichImm.onSubtypeChanged(subtype); mInputLogic.onSubtypeChanged(SubtypeLocaleUtils.getCombiningRulesExtraValue(subtype), mSettings.getCurrent()); loadKeyboard(); } - private void onStartInputInternal(final EditorInfo editorInfo, final boolean restarting) { + void onStartInputInternal(final EditorInfo editorInfo, final boolean restarting) { super.onStartInput(editorInfo, restarting); } @SuppressWarnings("deprecation") - private void onStartInputViewInternal(final EditorInfo editorInfo, final boolean restarting) { + void onStartInputViewInternal(final EditorInfo editorInfo, final boolean restarting) { super.onStartInputView(editorInfo, restarting); - mRichImm.clearSubtypeCaches(); + + mDictionaryFacilitator.onStartInput(); + // Switch to the null consumer to handle cases leading to early exit below, for which we + // also wouldn't be consuming gesture data. + mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER; + mRichImm.refreshSubtypeCaches(); final KeyboardSwitcher switcher = mKeyboardSwitcher; switcher.updateKeyboardTheme(); final MainKeyboardView mainKeyboardView = switcher.getMainKeyboardView(); @@ -839,7 +812,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } return; } - if (DEBUG) { + if (DebugFlags.DEBUG_ENABLED) { Log.d(TAG, "onStartInputView: editorInfo:" + String.format("inputType=0x%08x imeOptions=0x%08x", editorInfo.inputType, editorInfo.imeOptions)); @@ -867,6 +840,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } + // Update to a gesture consumer with the current editor and IME state. + mGestureConsumer = GestureConsumer.newInstance(editorInfo, + mInputLogic.getPrivateCommandPerformer(), + mRichImm.getCurrentSubtypeLocale(), + switcher.getKeyboard()); + // Forward this event to the accessibility utilities, if enabled. final AccessibilityUtils accessUtils = AccessibilityUtils.getInstance(); if (accessUtils.isTouchExplorationEnabled()) { @@ -875,9 +854,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final boolean inputTypeChanged = !currentSettingsValues.isSameInputType(editorInfo); final boolean isDifferentTextField = !restarting || inputTypeChanged; - if (isDifferentTextField) { - mSubtypeSwitcher.updateParametersOnStartInputView(); - } + + StatsUtils.onStartInputView(editorInfo.inputType, + Settings.getInstance().getCurrent().mDisplayOrientation, + !isDifferentTextField); // The EditorInfo might have a flag that affects fullscreen mode. // Note: This call should be done by InputMethodService? @@ -897,15 +877,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // span, so we should reset our state unconditionally, even if restarting is true. // We also tell the input logic about the combining rules for the current subtype, so // it can adjust its combiners if needed. - mInputLogic.startInput(mSubtypeSwitcher.getCombiningRulesExtraValueOfCurrentSubtype(), + mInputLogic.startInput(mRichImm.getCombiningRulesExtraValueOfCurrentSubtype(), currentSettingsValues); - // Note: the following does a round-trip IPC on the main thread: be careful - final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale(); - if (null != currentLocale && !currentLocale.equals(suggest.getLocale())) { - // TODO: Do this automatically. - resetSuggest(); - } + resetDictionaryFacilitatorIfNecessary(); // TODO[IL]: Can the following be moved to InputLogic#startInput? if (!mInputLogic.mConnection.resetCachesUponCursorMoveAndReturnSuccess( @@ -919,11 +894,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // mLastSelection{Start,End} are reset later in this method, no need to do it here needToCallLoadKeyboardLater = true; } else { - // When rotating, initialSelStart and initialSelEnd sometimes are lying. Make a best - // effort to work around this bug. + // When rotating, and when input is starting again in a field from where the focus + // didn't move (the keyboard having been closed with the back key), + // initialSelStart and initialSelEnd sometimes are lying. Make a best effort to + // work around this bug. mInputLogic.mConnection.tryFixLyingCursorPosition(); - mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */, - true /* shouldDelay */); + mHandler.postResumeSuggestionsForStartInput(true /* shouldDelay */); needToCallLoadKeyboardLater = false; } } else { @@ -943,6 +919,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen suggest.setAutoCorrectionThreshold( currentSettingsValues.mAutoCorrectionThreshold); } + suggest.setPlausibilityThreshold(currentSettingsValues.mPlausibilityThreshold); switcher.loadKeyboard(editorInfo, currentSettingsValues, getCurrentAutoCapsState(), getCurrentRecapitalizeState()); @@ -970,7 +947,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.cancelUpdateSuggestionStrip(); mainKeyboardView.setMainDictionaryAvailability( - mDictionaryFacilitator.hasInitializedMainDictionary()); + mDictionaryFacilitator.hasAtLeastOneInitializedMainDictionary()); mainKeyboardView.setKeyPreviewPopupEnabled(currentSettingsValues.mKeyPreviewPopupOn, currentSettingsValues.mKeyPreviewPopupDismissDelay); mainKeyboardView.setSlidingKeyInputPreviewEnabled( @@ -980,8 +957,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen currentSettingsValues.mGestureTrailEnabled, currentSettingsValues.mGestureFloatingPreviewTextEnabled); - // Contextual dictionary should be updated for the current application. - mContextualDictionaryUpdater.onStartInputView(editorInfo.packageName); if (TRACE) Debug.startMethodTracing("/data/trace/latinime"); } @@ -994,45 +969,50 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - private void onFinishInputInternal() { + void onFinishInputInternal() { super.onFinishInput(); + mDictionaryFacilitator.onFinishInput(); final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); if (mainKeyboardView != null) { mainKeyboardView.closing(); } } - private void onFinishInputViewInternal(final boolean finishingInput) { + void onFinishInputViewInternal(final boolean finishingInput) { super.onFinishInputView(finishingInput); cleanupInternalStateForFinishInput(); } private void cleanupInternalStateForFinishInput() { - mKeyboardSwitcher.deallocateMemory(); // Remove pending messages related to update suggestions mHandler.cancelUpdateSuggestionStrip(); // Should do the following in onFinishInputInternal but until JB MR2 it's not called :( mInputLogic.finishInput(); } + protected void deallocateMemory() { + mKeyboardSwitcher.deallocateMemory(); + } + @Override public void onUpdateSelection(final int oldSelStart, final int oldSelEnd, final int newSelStart, final int newSelEnd, final int composingSpanStart, final int composingSpanEnd) { super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, composingSpanStart, composingSpanEnd); - if (DEBUG) { + if (DebugFlags.DEBUG_ENABLED) { Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart + ", ose=" + oldSelEnd + ", nss=" + newSelStart + ", nse=" + newSelEnd + ", cs=" + composingSpanStart + ", ce=" + composingSpanEnd); } - // This call happens when we have a hardware keyboard as well as when we don't. While we - // don't support hardware keyboards yet we should avoid doing the processing associated - // with cursor movement when we have a hardware keyboard since we are not in charge. + // This call happens whether our view is displayed or not, but if it's not then we should + // not attempt recorrection. This is true even with a hardware keyboard connected: if the + // view is not displayed we have no means of showing suggestions anyway, and if it is then + // we want to show suggestions anyway. final SettingsValues settingsValues = mSettings.getCurrent(); - if ((!settingsValues.mHasHardwareKeyboard || ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) + if (isInputViewShown() && mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, settingsValues)) { mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(), @@ -1040,15 +1020,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - // We cannot mark this method as @Override until new SDK becomes publicly available. - // @Override - public void onUpdateCursorAnchorInfo(final CursorAnchorInfo info) { - if (!ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK || isFullscreenMode()) { - return; - } - mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.fromObject(info)); - } - /** * This is called when the user has clicked on the extracted text view, * when running in fullscreen mode. The default implementation hides @@ -1098,7 +1069,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void onDisplayCompletions(final CompletionInfo[] applicationSpecifiedCompletions) { - if (DEBUG) { + if (DebugFlags.DEBUG_ENABLED) { Log.i(TAG, "Received completions:"); if (applicationSpecifiedCompletions != null) { for (int i = 0; i < applicationSpecifiedCompletions.length; i++) { @@ -1121,9 +1092,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen SuggestedWords.getFromApplicationSpecifiedCompletions( applicationSpecifiedCompletions); final SuggestedWords suggestedWords = new SuggestedWords(applicationSuggestedWords, - null /* rawSuggestions */, false /* typedWordValid */, false /* willAutoCorrect */, + null /* rawSuggestions */, + null /* typedWord */, + false /* typedWordValid */, + false /* willAutoCorrect */, false /* isObsoleteSuggestions */, - SuggestedWords.INPUT_STYLE_APPLICATION_SPECIFIED /* inputStyle */); + SuggestedWords.INPUT_STYLE_APPLICATION_SPECIFIED /* inputStyle */, + SuggestedWords.NOT_A_SEQUENCE_NUMBER); // When in fullscreen mode, show completions generated by the application forcibly setSuggestedWords(suggestedWords); } @@ -1131,6 +1106,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void onComputeInsets(final InputMethodService.Insets outInsets) { super.onComputeInsets(outInsets); + // This method may be called before {@link #setInputView(View)}. + if (mInputView == null) { + return; + } final SettingsValues settingsValues = mSettings.getCurrent(); final View visibleKeyboardView = mKeyboardSwitcher.getVisibleKeyboardView(); if (visibleKeyboardView == null || !hasSuggestionStripView()) { @@ -1141,8 +1120,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (hasHardwareKeyboard && visibleKeyboardView.getVisibility() == View.GONE) { // If there is a hardware keyboard and a visible software keyboard view has been hidden, // no visual element will be shown on the screen. - outInsets.touchableInsets = inputHeight; + outInsets.contentTopInsets = inputHeight; outInsets.visibleTopInsets = inputHeight; + mInsetsUpdater.setInsets(outInsets); return; } final int suggestionsHeight = (!mKeyboardSwitcher.isShowingEmojiPalettes() @@ -1150,7 +1130,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen ? mSuggestionStripView.getHeight() : 0; final int visibleTopY = inputHeight - visibleKeyboardView.getHeight() - suggestionsHeight; mSuggestionStripView.setMoreSuggestionsHeight(visibleTopY); - // Need to set touchable region only if a keyboard view is being shown. + // Need to set expanded touchable region only if a keyboard view is being shown. if (visibleKeyboardView.isShown()) { final int touchLeft = 0; final int touchTop = mKeyboardSwitcher.isShowingMoreKeysPanel() ? 0 : visibleTopY; @@ -1163,14 +1143,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } outInsets.contentTopInsets = visibleTopY; outInsets.visibleTopInsets = visibleTopY; + mInsetsUpdater.setInsets(outInsets); } - public void startShowingInputView() { + public void startShowingInputView(final boolean needsToLoadKeyboard) { mIsExecutingStartShowingInputView = true; // This {@link #showWindow(boolean)} will eventually call back // {@link #onEvaluateInputViewShown()}. showWindow(true /* showInput */); mIsExecutingStartShowingInputView = false; + if (needsToLoadKeyboard) { + loadKeyboard(); + } } public void stopShowingInputView() { @@ -1178,6 +1162,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } @Override + public boolean onShowInputRequested(final int flags, final boolean configChange) { + if (Settings.getInstance().getCurrent().mHasHardwareKeyboard) { + return true; + } + return super.onShowInputRequested(flags, configChange); + } + + @Override public boolean onEvaluateInputViewShown() { if (mIsExecutingStartShowingInputView) { return true; @@ -1207,8 +1199,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void updateFullscreenMode() { + super.updateFullscreenMode(); + updateSoftInputWindowLayoutParameters(); + } + + private void updateSoftInputWindowLayoutParameters() { // Override layout parameters to expand {@link SoftInputWindow} to the entire screen. - // See {@link InputMethodService#setinputView(View) and + // See {@link InputMethodService#setinputView(View)} and // {@link SoftInputWindow#updateWidthHeight(WindowManager.LayoutParams)}. final Window window = getWindow().getWindow(); ViewLayoutUtils.updateLayoutHeightOf(window, LayoutParams.MATCH_PARENT); @@ -1227,22 +1224,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen ViewLayoutUtils.updateLayoutGravityOf(inputArea, Gravity.BOTTOM); ViewLayoutUtils.updateLayoutHeightOf(mInputView, layoutHeight); } - super.updateFullscreenMode(); - mInputLogic.onUpdateFullscreenMode(isFullscreenMode()); } - private int getCurrentAutoCapsState() { + int getCurrentAutoCapsState() { return mInputLogic.getCurrentAutoCapsState(mSettings.getCurrent()); } - private int getCurrentRecapitalizeState() { + int getCurrentRecapitalizeState() { return mInputLogic.getCurrentRecapitalizeState(); } - public Locale getCurrentSubtypeLocale() { - return mSubtypeSwitcher.getCurrentSubtypeLocale(); - } - /** * @param codePoints code points to get coordinates for. * @return x,y coordinates for this keyboard, as a flattened array. @@ -1256,24 +1247,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return keyboard.getCoordinates(codePoints); } - // Callback for the {@link SuggestionStripView}, to call when the "add to dictionary" hint is - // pressed. - @Override - public void addWordToUserDictionary(final String word) { - if (TextUtils.isEmpty(word)) { - // Probably never supposed to happen, but just in case. - return; - } - final String wordToEdit; - if (CapsModeUtils.isAutoCapsMode(mInputLogic.mLastComposedWord.mCapitalizedMode)) { - wordToEdit = word.toLowerCase(getCurrentSubtypeLocale()); - } else { - wordToEdit = word; - } - mDictionaryFacilitator.addWordToUserDictionary(this /* context */, wordToEdit); - mInputLogic.onAddWordToUserDictionary(); - } - // Callback for the {@link SuggestionStripView}, to call when the important notice strip is // pressed. @Override @@ -1284,7 +1257,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Implement {@link ImportantNoticeDialog.ImportantNoticeDialogListener} @Override public void onClickSettingsOfImportantNoticeDialog(final int nextVersion) { - launchSettings(); + launchSettings(SettingsActivity.EXTRA_ENTRY_VALUE_NOTICE_DIALOG); } // Implement {@link ImportantNoticeDialog.ImportantNoticeDialogListener} @@ -1328,48 +1301,56 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSubtypeState.switchSubtype(token, mRichImm); } + // TODO: Instead of checking for alphabetic keyboard here, separate keycodes for + // alphabetic shift and shift while in symbol layout and get rid of this method. + private int getCodePointForKeyboard(final int codePoint) { + if (Constants.CODE_SHIFT == codePoint) { + final Keyboard currentKeyboard = mKeyboardSwitcher.getKeyboard(); + if (null != currentKeyboard && currentKeyboard.mId.isAlphabetKeyboard()) { + return codePoint; + } + return Constants.CODE_SYMBOL_SHIFT; + } + return codePoint; + } + // Implementation of {@link KeyboardActionListener}. @Override public void onCodeInput(final int codePoint, final int x, final int y, final boolean isKeyRepeat) { + // TODO: this processing does not belong inside LatinIME, the caller should be doing this. final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); // x and y include some padding, but everything down the line (especially native // code) needs the coordinates in the keyboard frame. // TODO: We should reconsider which coordinate system should be used to represent // keyboard event. Also we should pull this up -- LatinIME has no business doing - // this transformation, it should be done already before calling onCodeInput. + // this transformation, it should be done already before calling onEvent. final int keyX = mainKeyboardView.getKeyX(x); final int keyY = mainKeyboardView.getKeyY(y); - final int codeToSend; - if (Constants.CODE_SHIFT == codePoint) { - // TODO: Instead of checking for alphabetic keyboard here, separate keycodes for - // alphabetic shift and shift while in symbol layout. - final Keyboard currentKeyboard = mKeyboardSwitcher.getKeyboard(); - if (null != currentKeyboard && currentKeyboard.mId.isAlphabetKeyboard()) { - codeToSend = codePoint; - } else { - codeToSend = Constants.CODE_SYMBOL_SHIFT; - } - } else { - codeToSend = codePoint; - } - if (Constants.CODE_SHORTCUT == codePoint) { - mSubtypeSwitcher.switchToShortcutIME(this); - // Still call the *#onCodeInput methods for readability. + final Event event = createSoftwareKeypressEvent(getCodePointForKeyboard(codePoint), + keyX, keyY, isKeyRepeat); + onEvent(event); + } + + // This method is public for testability of LatinIME, but also in the future it should + // completely replace #onCodeInput. + public void onEvent(@Nonnull final Event event) { + if (Constants.CODE_SHORTCUT == event.mKeyCode) { + mRichImm.switchToShortcutIme(this); } - final Event event = createSoftwareKeypressEvent(codeToSend, keyX, keyY, isKeyRepeat); final InputTransaction completeInputTransaction = mInputLogic.onCodeInput(mSettings.getCurrent(), event, mKeyboardSwitcher.getKeyboardShiftMode(), mKeyboardSwitcher.getCurrentKeyboardScriptId(), mHandler); updateStateAfterInputTransaction(completeInputTransaction); - mKeyboardSwitcher.onCodeInput(codePoint, getCurrentAutoCapsState(), - getCurrentRecapitalizeState()); + mKeyboardSwitcher.onEvent(event, getCurrentAutoCapsState(), getCurrentRecapitalizeState()); } // A helper method to split the code point and the key code. Ultimately, they should not be // squashed into the same variable, and this method should be removed. - private static Event createSoftwareKeypressEvent(final int keyCodeOrCodePoint, final int keyX, + // public for testing, as we don't want to copy the same logic into test code + @Nonnull + public static Event createSoftwareKeypressEvent(final int keyCodeOrCodePoint, final int keyX, final int keyY, final boolean isKeyRepeat) { final int keyCode; final int codePoint; @@ -1387,44 +1368,59 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void onTextInput(final String rawText) { // TODO: have the keyboard pass the correct key code when we need it. - final Event event = Event.createSoftwareTextEvent(rawText, Event.NOT_A_KEY_CODE); + final Event event = Event.createSoftwareTextEvent(rawText, Constants.CODE_OUTPUT_TEXT); final InputTransaction completeInputTransaction = mInputLogic.onTextInput(mSettings.getCurrent(), event, mKeyboardSwitcher.getKeyboardShiftMode(), mHandler); updateStateAfterInputTransaction(completeInputTransaction); - mKeyboardSwitcher.onCodeInput(Constants.CODE_OUTPUT_TEXT, getCurrentAutoCapsState(), - getCurrentRecapitalizeState()); + mKeyboardSwitcher.onEvent(event, getCurrentAutoCapsState(), getCurrentRecapitalizeState()); } @Override public void onStartBatchInput() { mInputLogic.onStartBatchInput(mSettings.getCurrent(), mKeyboardSwitcher, mHandler); + mGestureConsumer.onGestureStarted( + mRichImm.getCurrentSubtypeLocale(), + mKeyboardSwitcher.getKeyboard()); } @Override public void onUpdateBatchInput(final InputPointers batchPointers) { - mInputLogic.onUpdateBatchInput(mSettings.getCurrent(), batchPointers, mKeyboardSwitcher); + mInputLogic.onUpdateBatchInput(batchPointers); } @Override public void onEndBatchInput(final InputPointers batchPointers) { mInputLogic.onEndBatchInput(batchPointers); + mGestureConsumer.onGestureCompleted(batchPointers); } @Override public void onCancelBatchInput() { mInputLogic.onCancelBatchInput(mHandler); + mGestureConsumer.onGestureCanceled(); + } + + /** + * To be called after the InputLogic has gotten a chance to act on the suggested words by the + * IME for the full gesture, possibly updating the TextView to reflect the first suggestion. + * <p> + * This method must be run on the UI Thread. + * @param suggestedWords suggested words by the IME for the full gesture. + */ + public void onTailBatchInputResultShown(final SuggestedWords suggestedWords) { + mGestureConsumer.onImeSuggestionsProcessed(suggestedWords, + mInputLogic.getComposingStart(), mInputLogic.getComposingLength(), + mDictionaryFacilitator); } // This method must run on the UI Thread. - private void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords, + void showGesturePreviewAndSuggestionStrip(@Nonnull final SuggestedWords suggestedWords, final boolean dismissGestureFloatingPreviewText) { showSuggestionStrip(suggestedWords); final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); - mainKeyboardView.showGestureFloatingPreviewText(suggestedWords); - if (dismissGestureFloatingPreviewText) { - mainKeyboardView.dismissGestureFloatingPreviewText(); - } + mainKeyboardView.showGestureFloatingPreviewText(suggestedWords, + dismissGestureFloatingPreviewText /* dismissDelayed */); } // Called from PointerTracker through the KeyboardActionListener interface @@ -1446,22 +1442,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return null != mSuggestionStripView; } - @Override - public boolean isShowingAddToDictionaryHint() { - return hasSuggestionStripView() && mSuggestionStripView.isShowingAddToDictionaryHint(); - } - - @Override - public void dismissAddToDictionaryHint() { - if (!hasSuggestionStripView()) { - return; - } - mSuggestionStripView.dismissAddToDictionaryHint(); - } - private void setSuggestedWords(final SuggestedWords suggestedWords) { final SettingsValues currentSettingsValues = mSettings.getCurrent(); - mInputLogic.setSuggestedWords(suggestedWords, currentSettingsValues, mHandler); + mInputLogic.setSuggestedWords(suggestedWords); // TODO: Modify this when we support suggestions with hard keyboard if (!hasSuggestionStripView()) { return; @@ -1471,7 +1454,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } final boolean shouldShowImportantNotice = - ImportantNoticeUtils.shouldShowImportantNotice(this); + ImportantNoticeUtils.shouldShowImportantNotice(this, currentSettingsValues); final boolean shouldShowSuggestionCandidates = currentSettingsValues.mInputAttributes.mShouldShowSuggestions && currentSettingsValues.isSuggestionsEnabledPerUserSettings(); @@ -1489,7 +1472,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final boolean isEmptyApplicationSpecifiedCompletions = currentSettingsValues.isApplicationSpecifiedCompletionsOn() && suggestedWords.isEmpty(); - final boolean noSuggestionsFromDictionaries = (SuggestedWords.EMPTY == suggestedWords) + final boolean noSuggestionsFromDictionaries = suggestedWords.isEmpty() || suggestedWords.isPunctuationSuggestions() || isEmptyApplicationSpecifiedCompletions; final boolean isBeginningOfSentencePrediction = (suggestedWords.mInputStyle @@ -1507,7 +1490,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // We should clear the contextual strip if there is no suggestion from dictionaries. || noSuggestionsFromDictionaries) { mSuggestionStripView.setSuggestions(suggestedWords, - SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype())); + mRichImm.getCurrentSubtype().isRtlSubtype()); } } @@ -1516,26 +1499,23 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final OnGetSuggestedWordsCallback callback) { final Keyboard keyboard = mKeyboardSwitcher.getKeyboard(); if (keyboard == null) { - callback.onGetSuggestedWords(SuggestedWords.EMPTY); + callback.onGetSuggestedWords(SuggestedWords.getEmptyInstance()); return; } - mInputLogic.getSuggestedWords(mSettings.getCurrent(), keyboard.getProximityInfo(), + mInputLogic.getSuggestedWords(mSettings.getCurrent(), keyboard, mKeyboardSwitcher.getKeyboardShiftMode(), inputStyle, sequenceNumber, callback); } @Override - public void showSuggestionStrip(final SuggestedWords sourceSuggestedWords) { - final SuggestedWords suggestedWords = - sourceSuggestedWords.isEmpty() ? SuggestedWords.EMPTY : sourceSuggestedWords; - if (SuggestedWords.EMPTY == suggestedWords) { + public void showSuggestionStrip(final SuggestedWords suggestedWords) { + if (suggestedWords.isEmpty()) { setNeutralSuggestionStrip(); } else { setSuggestedWords(suggestedWords); } // Cache the auto-correction in accessibility code so we can speak it if the user // touches a key that will insert it. - AccessibilityUtils.getInstance().setAutoCorrection(suggestedWords, - sourceSuggestedWords.mTypedWord); + AccessibilityUtils.getInstance().setAutoCorrection(suggestedWords); } // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener} @@ -1550,25 +1530,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen updateStateAfterInputTransaction(completeInputTransaction); } - @Override - public void showAddToDictionaryHint(final String word) { - if (!hasSuggestionStripView()) { - return; - } - mSuggestionStripView.showAddToDictionaryHint(word); - } - // This will show either an empty suggestion strip (if prediction is enabled) or // punctuation suggestions (if it's disabled). @Override public void setNeutralSuggestionStrip() { final SettingsValues currentSettings = mSettings.getCurrent(); final SuggestedWords neutralSuggestions = currentSettings.mBigramPredictionEnabled - ? SuggestedWords.EMPTY : currentSettings.mSpacingAndPunctuations.mSuggestPuncList; + ? SuggestedWords.getEmptyInstance() + : currentSettings.mSpacingAndPunctuations.mSuggestPuncList; setSuggestedWords(neutralSuggestions); } - // TODO: Make this private // Outside LatinIME, only used by the {@link InputTestsBase} test suite. @UsedForTesting void loadKeyboard() { @@ -1676,7 +1648,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Hooks for hardware keyboard @Override public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) { - mSpecialKeyDetector.onKeyDown(keyEvent); + // TODO: This should be processed in {@link InputLogic}. + mEmojiAltPhysicalKeyDetector.onKeyDown(keyEvent); if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) { return super.onKeyDown(keyCode, keyEvent); } @@ -1697,7 +1670,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public boolean onKeyUp(final int keyCode, final KeyEvent keyEvent) { - mSpecialKeyDetector.onKeyUp(keyEvent); + // TODO: This should be processed in {@link InputLogic}. + mEmojiAltPhysicalKeyDetector.onKeyUp(keyEvent); if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) { return super.onKeyUp(keyCode, keyEvent); } @@ -1713,21 +1687,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // boolean onKeyLongPress(final int keyCode, final KeyEvent event); // boolean onKeyMultiple(final int keyCode, final int count, final KeyEvent event); - // receive ringer mode change and network state change. - private final BroadcastReceiver mConnectivityAndRingerModeChangeReceiver = - new BroadcastReceiver() { + // receive ringer mode change. + private final BroadcastReceiver mRingerModeChangeReceiver = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { final String action = intent.getAction(); - if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { - mSubtypeSwitcher.onNetworkStateChanged(intent); - } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) { + if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) { AudioAndHapticFeedbackManager.getInstance().onRingerModeChanged(); } } }; - private void launchSettings() { + void launchSettings(final String extraEntryValue) { mInputLogic.commitTyped(mSettings.getCurrent(), LastComposedWord.NOT_A_SEPARATOR); requestHideSelf(0); final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); @@ -1740,6 +1711,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.putExtra(SettingsActivity.EXTRA_SHOW_HOME_AS_UP, false); + intent.putExtra(SettingsActivity.EXTRA_ENTRY_KEY, extraEntryValue); startActivity(intent); } @@ -1751,6 +1723,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen languageSelectionTitle, getString(ApplicationUtils.getActivityTitleResId(this, SettingsActivity.class)) }; + final String imeId = mRichImm.getInputMethodIdOfThisIme(); final OnClickListener listener = new OnClickListener() { @Override public void onClick(DialogInterface di, int position) { @@ -1758,7 +1731,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen switch (position) { case 0: final Intent intent = IntentUtils.getInputLanguageSelectionIntent( - mRichImm.getInputMethodIdOfThisIme(), + imeId, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_CLEAR_TOP); @@ -1766,7 +1739,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen startActivity(intent); break; case 1: - launchSettings(); + launchSettings(SettingsActivity.EXTRA_ENTRY_VALUE_LONG_PRESS_COMMA); break; } } @@ -1798,45 +1771,45 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen dialog.show(); } - // TODO: can this be removed somehow without breaking the tests? @UsedForTesting - /* package for test */ SuggestedWords getSuggestedWordsForTest() { + SuggestedWords getSuggestedWordsForTest() { // You may not use this method for anything else than debug - return DEBUG ? mInputLogic.mSuggestedWords : null; + return DebugFlags.DEBUG_ENABLED ? mInputLogic.mSuggestedWords : null; } // DO NOT USE THIS for any other purpose than testing. This is information private to LatinIME. @UsedForTesting - /* package for test */ void waitForLoadingDictionaries(final long timeout, final TimeUnit unit) + void waitForLoadingDictionaries(final long timeout, final TimeUnit unit) throws InterruptedException { mDictionaryFacilitator.waitForLoadingDictionariesForTesting(timeout, unit); } // DO NOT USE THIS for any other purpose than testing. This can break the keyboard badly. @UsedForTesting - /* package for test */ void replaceDictionariesForTest(final Locale locale) { + void replaceDictionariesForTest(final Locale locale) { final SettingsValues settingsValues = mSettings.getCurrent(); mDictionaryFacilitator.resetDictionaries(this, locale, settingsValues.mUseContactsDict, settingsValues.mUsePersonalizedDicts, - false /* forceReloadMainDictionary */, this /* listener */); + false /* forceReloadMainDictionary */, + settingsValues.mAccount, "", /* dictionaryNamePrefix */ + this /* DictionaryInitializationListener */); } // DO NOT USE THIS for any other purpose than testing. @UsedForTesting - /* package for test */ void clearPersonalizedDictionariesForTest() { - mDictionaryFacilitator.clearUserHistoryDictionary(); - mDictionaryFacilitator.clearPersonalizationDictionary(); + void clearPersonalizedDictionariesForTest() { + mDictionaryFacilitator.clearUserHistoryDictionary(this); } @UsedForTesting - /* package for test */ List<InputMethodSubtype> getEnabledSubtypesForTest() { + List<InputMethodSubtype> getEnabledSubtypesForTest() { return (mRichImm != null) ? mRichImm.getMyEnabledInputMethodSubtypeList( true /* allowsImplicitlySelectedSubtypes */) : new ArrayList<InputMethodSubtype>(); } public void dumpDictionaryForDebug(final String dictName) { - if (mDictionaryFacilitator.getLocale() == null) { - resetSuggest(); + if (!mDictionaryFacilitator.isActive()) { + resetDictionaryFacilitatorIfNecessary(); } mDictionaryFacilitator.dumpDictionaryForDebug(dictName); } @@ -1862,6 +1835,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen p.println(" Keyboard mode = " + keyboardMode); final SettingsValues settingsValues = mSettings.getCurrent(); p.println(settingsValues.dump()); + p.println(mDictionaryFacilitator.dump(this /* context */)); // TODO: Dump all settings values } |