Merge "Remove ProbabilityDictContent and use LanguageModelDictContent" into lmp-dev
diff --git a/java/src/com/android/inputmethod/event/InputTransaction.java b/java/src/com/android/inputmethod/event/InputTransaction.java
index cdff265..5bc9111 100644
--- a/java/src/com/android/inputmethod/event/InputTransaction.java
+++ b/java/src/com/android/inputmethod/event/InputTransaction.java
@@ -42,6 +42,7 @@
     private int mRequiredShiftUpdate = SHIFT_NO_UPDATE;
     private boolean mRequiresUpdateSuggestions = false;
     private boolean mDidAffectContents = false;
+    private boolean mDidAutoCorrect = false;
 
     public InputTransaction(final SettingsValues settingsValues, final Event event,
             final long timestamp, final int spaceState, final int shiftState) {
@@ -97,4 +98,19 @@
     public boolean didAffectContents() {
         return mDidAffectContents;
     }
+
+    /**
+     * Indicate that this transaction performed an auto-correction.
+     */
+    public void setDidAutoCorrect() {
+        mDidAutoCorrect = true;
+    }
+
+    /**
+     * Find out whether this transaction performed an auto-correction.
+     * @return Whether this transaction performed an auto-correction.
+     */
+    public boolean didAutoCorrect() {
+        return mDidAutoCorrect;
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 719aeac..4adc28d 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -248,7 +248,7 @@
                 break;
             case MSG_RESET_CACHES:
                 final SettingsValues settingsValues = latinIme.mSettings.getCurrent();
-                if (latinIme.mInputLogic.retryResetCachesAndReturnSuccess(settingsValues,
+                if (latinIme.mInputLogic.retryResetCachesAndReturnSuccess(
                         msg.arg1 == 1 /* tryResumeSuggestions */,
                         msg.arg2 /* remainingTries */, this /* handler */)) {
                     // If we were able to reset the caches, then we can reload the keyboard.
@@ -752,8 +752,30 @@
         loadKeyboard();
     }
 
+    /**
+     * A class that holds information to pass from onStartInputInternal to onStartInputViewInternal
+     *
+     * OnStartInput needs to reload the settings and that will prevent onStartInputViewInternal
+     * from comparing the old settings with the new ones, so we use this memory to pass the
+     * necessary information along.
+     */
+    private static class EditorChangeInfo {
+        public final boolean mIsSameInputType;
+        public final boolean mHasSameOrientation;
+        public EditorChangeInfo(final boolean isSameInputType, final boolean hasSameOrientation) {
+            mIsSameInputType = isSameInputType;
+            mHasSameOrientation = hasSameOrientation;
+        }
+    }
+
+    private EditorChangeInfo mLastEditorChangeInfo;
+
     private void onStartInputInternal(final EditorInfo editorInfo, final boolean restarting) {
         super.onStartInput(editorInfo, restarting);
+        SettingsValues currentSettingsValues = mSettings.getCurrent();
+        mLastEditorChangeInfo = new EditorChangeInfo(
+                currentSettingsValues.isSameInputType(editorInfo),
+                currentSettingsValues.hasSameOrientation(getResources().getConfiguration()));
     }
 
     @SuppressWarnings("deprecation")
@@ -763,9 +785,6 @@
         final KeyboardSwitcher switcher = mKeyboardSwitcher;
         switcher.updateKeyboardTheme();
         final MainKeyboardView mainKeyboardView = switcher.getMainKeyboardView();
-        // If we are starting input in a different text field from before, we'll have to reload
-        // settings, so currentSettingsValues can't be final.
-        SettingsValues currentSettingsValues = mSettings.getCurrent();
 
         if (editorInfo == null) {
             Log.e(TAG, "Null EditorInfo in onStartInputView()");
@@ -808,7 +827,7 @@
             accessUtils.onStartInputViewInternal(mainKeyboardView, editorInfo, restarting);
         }
 
-        final boolean inputTypeChanged = !currentSettingsValues.isSameInputType(editorInfo);
+        final boolean inputTypeChanged = !mLastEditorChangeInfo.mIsSameInputType;
         final boolean isDifferentTextField = !restarting || inputTypeChanged;
         if (isDifferentTextField) {
             mSubtypeSwitcher.updateParametersOnStartInputView();
@@ -853,13 +872,12 @@
             canReachInputConnection = true;
         }
 
-        if (isDifferentTextField ||
-                !currentSettingsValues.hasSameOrientation(getResources().getConfiguration())) {
+        if (isDifferentTextField || !mLastEditorChangeInfo.mHasSameOrientation) {
             loadSettings();
         }
+        final SettingsValues currentSettingsValues = mSettings.getCurrent();
         if (isDifferentTextField) {
             mainKeyboardView.closing();
-            currentSettingsValues = mSettings.getCurrent();
 
             if (currentSettingsValues.mAutoCorrectionEnabledPerUserSettings) {
                 suggest.setAutoCorrectionThreshold(
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index dee7cd4..9e20abc 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -426,121 +426,14 @@
             cancelDoubleSpacePeriodCountdown();
         }
 
-        boolean didAutoCorrect = false;
         if (processedEvent.isConsumed()) {
-            // A consumed event may have text to commit and an update to the composing state, so
-            // we evaluate both. With some combiners, it's possible than an event contains both
-            // and we enter both of the following if clauses.
-            final CharSequence textToCommit = processedEvent.getTextToCommit();
-            if (!TextUtils.isEmpty(textToCommit)) {
-                mConnection.commitText(textToCommit, 1);
-                inputTransaction.setDidAffectContents();
-            }
-            if (mWordComposer.isComposingWord()) {
-                mConnection.setComposingText(mWordComposer.getTypedWord(), 1);
-                inputTransaction.setDidAffectContents();
-                inputTransaction.setRequiresUpdateSuggestions();
-            }
+            handleConsumedEvent(inputTransaction);
         } else if (processedEvent.isFunctionalKeyEvent()) {
-            // A special key, like delete, shift, emoji, or the settings key.
-            switch (processedEvent.mKeyCode) {
-            case Constants.CODE_DELETE:
-                handleBackspace(inputTransaction, currentKeyboardScriptId);
-                // Backspace is a functional key, but it affects the contents of the editor.
-                inputTransaction.setDidAffectContents();
-                break;
-            case Constants.CODE_SHIFT:
-                performRecapitalization(inputTransaction.mSettingsValues);
-                inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
-                if (mSuggestedWords.mIsPrediction) {
-                    inputTransaction.setRequiresUpdateSuggestions();
-                }
-                break;
-            case Constants.CODE_CAPSLOCK:
-                // Note: Changing keyboard to shift lock state is handled in
-                // {@link KeyboardSwitcher#onCodeInput(int)}.
-                break;
-            case Constants.CODE_SYMBOL_SHIFT:
-                // Note: Calling back to the keyboard on the symbol Shift key is handled in
-                // {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}.
-                break;
-            case Constants.CODE_SWITCH_ALPHA_SYMBOL:
-                // Note: Calling back to the keyboard on symbol key is handled in
-                // {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}.
-                break;
-            case Constants.CODE_SETTINGS:
-                onSettingsKeyPressed();
-                break;
-            case Constants.CODE_SHORTCUT:
-                // We need to switch to the shortcut IME. This is handled by LatinIME since the
-                // input logic has no business with IME switching.
-                break;
-            case Constants.CODE_ACTION_NEXT:
-                performEditorAction(EditorInfo.IME_ACTION_NEXT);
-                break;
-            case Constants.CODE_ACTION_PREVIOUS:
-                performEditorAction(EditorInfo.IME_ACTION_PREVIOUS);
-                break;
-            case Constants.CODE_LANGUAGE_SWITCH:
-                handleLanguageSwitchKey();
-                break;
-            case Constants.CODE_EMOJI:
-                // Note: Switching emoji keyboard is being handled in
-                // {@link KeyboardState#onCodeInput(int,int)}.
-                break;
-            case Constants.CODE_ALPHA_FROM_EMOJI:
-                // Note: Switching back from Emoji keyboard to the main keyboard is being
-                // handled in {@link KeyboardState#onCodeInput(int,int)}.
-                break;
-            case Constants.CODE_SHIFT_ENTER:
-                // TODO: remove this object
-                final Event tmpEvent = Event.createSoftwareKeypressEvent(Constants.CODE_ENTER,
-                        processedEvent.mKeyCode, processedEvent.mX, processedEvent.mY,
-                        processedEvent.isKeyRepeat());
-                final InputTransaction tmpTransaction = new InputTransaction(
-                        inputTransaction.mSettingsValues, tmpEvent,
-                        inputTransaction.mTimestamp, inputTransaction.mSpaceState,
-                        inputTransaction.mShiftState);
-                didAutoCorrect = handleNonSpecialCharacter(tmpTransaction, handler);
-                // Shift + Enter is treated as a functional key but it results in adding a new
-                // line, so that does affect the contents of the editor.
-                inputTransaction.setDidAffectContents();
-                break;
-            default:
-                throw new RuntimeException("Unknown key code : " + processedEvent.mKeyCode);
-            }
+            handleFunctionalEvent(inputTransaction, currentKeyboardScriptId, handler);
         } else {
-            inputTransaction.setDidAffectContents();
-            switch (processedEvent.mCodePoint) {
-            case Constants.CODE_ENTER:
-                final EditorInfo editorInfo = getCurrentInputEditorInfo();
-                final int imeOptionsActionId =
-                        InputTypeUtils.getImeOptionsActionIdFromEditorInfo(editorInfo);
-                if (InputTypeUtils.IME_ACTION_CUSTOM_LABEL == imeOptionsActionId) {
-                    // Either we have an actionLabel and we should performEditorAction with
-                    // actionId regardless of its value.
-                    performEditorAction(editorInfo.actionId);
-                } else if (EditorInfo.IME_ACTION_NONE != imeOptionsActionId) {
-                    // We didn't have an actionLabel, but we had another action to execute.
-                    // EditorInfo.IME_ACTION_NONE explicitly means no action. In contrast,
-                    // EditorInfo.IME_ACTION_UNSPECIFIED is the default value for an action, so it
-                    // means there should be an action and the app didn't bother to set a specific
-                    // code for it - presumably it only handles one. It does not have to be treated
-                    // in any specific way: anything that is not IME_ACTION_NONE should be sent to
-                    // performEditorAction.
-                    performEditorAction(imeOptionsActionId);
-                } else {
-                    // No action label, and the action from imeOptions is NONE: this is a regular
-                    // enter key that should input a carriage return.
-                    didAutoCorrect = handleNonSpecialCharacter(inputTransaction, handler);
-                }
-                break;
-            default:
-                didAutoCorrect = handleNonSpecialCharacter(inputTransaction, handler);
-                break;
-            }
+            handleNonFunctionalEvent(inputTransaction, handler);
         }
-        if (!didAutoCorrect && processedEvent.mKeyCode != Constants.CODE_SHIFT
+        if (!inputTransaction.didAutoCorrect() && processedEvent.mKeyCode != Constants.CODE_SHIFT
                 && processedEvent.mKeyCode != Constants.CODE_CAPSLOCK
                 && processedEvent.mKeyCode != Constants.CODE_SWITCH_ALPHA_SYMBOL)
             mLastComposedWord.deactivate();
@@ -686,6 +579,155 @@
     }
 
     /**
+     * Handle a consumed event.
+     *
+     * Consumed events represent events that have already been consumed, typically by the
+     * combining chain.
+     *
+     * @param inputTransaction The transaction in progress.
+     */
+    private void handleConsumedEvent(final InputTransaction inputTransaction) {
+        // A consumed event may have text to commit and an update to the composing state, so
+        // we evaluate both. With some combiners, it's possible than an event contains both
+        // and we enter both of the following if clauses.
+        final CharSequence textToCommit = inputTransaction.mEvent.getTextToCommit();
+        if (!TextUtils.isEmpty(textToCommit)) {
+            mConnection.commitText(textToCommit, 1);
+            inputTransaction.setDidAffectContents();
+        }
+        if (mWordComposer.isComposingWord()) {
+            mConnection.setComposingText(mWordComposer.getTypedWord(), 1);
+            inputTransaction.setDidAffectContents();
+            inputTransaction.setRequiresUpdateSuggestions();
+        }
+    }
+
+    /**
+     * Handle a functional key event.
+     *
+     * A functional event is a special key, like delete, shift, emoji, or the settings key.
+     * Non-special keys are those that generate a single code point.
+     * This includes all letters, digits, punctuation, separators, emoji. It excludes keys that
+     * manage keyboard-related stuff like shift, language switch, settings, layout switch, or
+     * any key that results in multiple code points like the ".com" key.
+     *
+     * @param inputTransaction The transaction in progress.
+     */
+    private void handleFunctionalEvent(final InputTransaction inputTransaction,
+            // TODO: remove these arguments
+            final int currentKeyboardScriptId, final LatinIME.UIHandler handler) {
+        final Event event = inputTransaction.mEvent;
+        switch (event.mKeyCode) {
+            case Constants.CODE_DELETE:
+                handleBackspace(inputTransaction, currentKeyboardScriptId);
+                // Backspace is a functional key, but it affects the contents of the editor.
+                inputTransaction.setDidAffectContents();
+                break;
+            case Constants.CODE_SHIFT:
+                performRecapitalization(inputTransaction.mSettingsValues);
+                inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
+                if (mSuggestedWords.mIsPrediction) {
+                    inputTransaction.setRequiresUpdateSuggestions();
+                }
+                break;
+            case Constants.CODE_CAPSLOCK:
+                // Note: Changing keyboard to shift lock state is handled in
+                // {@link KeyboardSwitcher#onCodeInput(int)}.
+                break;
+            case Constants.CODE_SYMBOL_SHIFT:
+                // Note: Calling back to the keyboard on the symbol Shift key is handled in
+                // {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}.
+                break;
+            case Constants.CODE_SWITCH_ALPHA_SYMBOL:
+                // Note: Calling back to the keyboard on symbol key is handled in
+                // {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}.
+                break;
+            case Constants.CODE_SETTINGS:
+                onSettingsKeyPressed();
+                break;
+            case Constants.CODE_SHORTCUT:
+                // We need to switch to the shortcut IME. This is handled by LatinIME since the
+                // input logic has no business with IME switching.
+                break;
+            case Constants.CODE_ACTION_NEXT:
+                performEditorAction(EditorInfo.IME_ACTION_NEXT);
+                break;
+            case Constants.CODE_ACTION_PREVIOUS:
+                performEditorAction(EditorInfo.IME_ACTION_PREVIOUS);
+                break;
+            case Constants.CODE_LANGUAGE_SWITCH:
+                handleLanguageSwitchKey();
+                break;
+            case Constants.CODE_EMOJI:
+                // Note: Switching emoji keyboard is being handled in
+                // {@link KeyboardState#onCodeInput(int,int)}.
+                break;
+            case Constants.CODE_ALPHA_FROM_EMOJI:
+                // Note: Switching back from Emoji keyboard to the main keyboard is being
+                // handled in {@link KeyboardState#onCodeInput(int,int)}.
+                break;
+            case Constants.CODE_SHIFT_ENTER:
+                // TODO: remove this object
+                final Event tmpEvent = Event.createSoftwareKeypressEvent(Constants.CODE_ENTER,
+                        event.mKeyCode, event.mX, event.mY, event.isKeyRepeat());
+                final InputTransaction tmpTransaction = new InputTransaction(
+                        inputTransaction.mSettingsValues, tmpEvent,
+                        inputTransaction.mTimestamp, inputTransaction.mSpaceState,
+                        inputTransaction.mShiftState);
+                handleNonSpecialCharacter(tmpTransaction, handler);
+                // Shift + Enter is treated as a functional key but it results in adding a new
+                // line, so that does affect the contents of the editor.
+                inputTransaction.setDidAffectContents();
+                break;
+            default:
+                throw new RuntimeException("Unknown key code : " + event.mKeyCode);
+        }
+    }
+
+    /**
+     * Handle an event that is not a functional event.
+     *
+     * These events are generally events that cause input, but in some cases they may do other
+     * things like trigger an editor action.
+     *
+     * @param inputTransaction The transaction in progress.
+     */
+    private void handleNonFunctionalEvent(final InputTransaction inputTransaction,
+            // TODO: remove this argument
+            final LatinIME.UIHandler handler) {
+        final Event event = inputTransaction.mEvent;
+        inputTransaction.setDidAffectContents();
+        switch (event.mCodePoint) {
+            case Constants.CODE_ENTER:
+                final EditorInfo editorInfo = getCurrentInputEditorInfo();
+                final int imeOptionsActionId =
+                        InputTypeUtils.getImeOptionsActionIdFromEditorInfo(editorInfo);
+                if (InputTypeUtils.IME_ACTION_CUSTOM_LABEL == imeOptionsActionId) {
+                    // Either we have an actionLabel and we should performEditorAction with
+                    // actionId regardless of its value.
+                    performEditorAction(editorInfo.actionId);
+                } else if (EditorInfo.IME_ACTION_NONE != imeOptionsActionId) {
+                    // We didn't have an actionLabel, but we had another action to execute.
+                    // EditorInfo.IME_ACTION_NONE explicitly means no action. In contrast,
+                    // EditorInfo.IME_ACTION_UNSPECIFIED is the default value for an action, so it
+                    // means there should be an action and the app didn't bother to set a specific
+                    // code for it - presumably it only handles one. It does not have to be treated
+                    // in any specific way: anything that is not IME_ACTION_NONE should be sent to
+                    // performEditorAction.
+                    performEditorAction(imeOptionsActionId);
+                } else {
+                    // No action label, and the action from imeOptions is NONE: this is a regular
+                    // enter key that should input a carriage return.
+                    handleNonSpecialCharacter(inputTransaction, handler);
+                }
+                break;
+            default:
+                handleNonSpecialCharacter(inputTransaction, handler);
+                break;
+        }
+    }
+
+    /**
      * Handle inputting a code point to the editor.
      *
      * Non-special keys are those that generate a single code point.
@@ -694,20 +736,16 @@
      * any key that results in multiple code points like the ".com" key.
      *
      * @param inputTransaction The transaction in progress.
-     * @return whether this caused an auto-correction to happen.
      */
-    private boolean handleNonSpecialCharacter(final InputTransaction inputTransaction,
+    private void handleNonSpecialCharacter(final InputTransaction inputTransaction,
             // TODO: remove this argument
             final LatinIME.UIHandler handler) {
         final int codePoint = inputTransaction.mEvent.mCodePoint;
         mSpaceState = SpaceState.NONE;
-        final boolean didAutoCorrect;
         if (inputTransaction.mSettingsValues.isWordSeparator(codePoint)
                 || Character.getType(codePoint) == Character.OTHER_SYMBOL) {
-            didAutoCorrect = handleSeparator(inputTransaction,
-                    inputTransaction.mEvent.isSuggestionStripPress(), handler);
+            handleSeparator(inputTransaction, handler);
         } else {
-            didAutoCorrect = false;
             if (SpaceState.PHANTOM == inputTransaction.mSpaceState) {
                 if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
                     // If we are in the middle of a recorrection, we need to commit the recorrection
@@ -720,7 +758,6 @@
             }
             handleNonSeparator(inputTransaction.mSettingsValues, inputTransaction);
         }
-        return didAutoCorrect;
     }
 
     /**
@@ -805,16 +842,12 @@
     /**
      * Handle input of a separator code point.
      * @param inputTransaction The transaction in progress.
-     * @param isFromSuggestionStrip whether this code point comes from the suggestion strip.
-     * @return whether this caused an auto-correction to happen.
      */
-    private boolean handleSeparator(final InputTransaction inputTransaction,
-            final boolean isFromSuggestionStrip,
+    private void handleSeparator(final InputTransaction inputTransaction,
             // TODO: remove this argument
             final LatinIME.UIHandler handler) {
         final int codePoint = inputTransaction.mEvent.mCodePoint;
         final SettingsValues settingsValues = inputTransaction.mSettingsValues;
-        boolean didAutoCorrect = false;
         final boolean wasComposingWord = mWordComposer.isComposingWord();
         // We avoid sending spaces in languages without spaces if we were composing.
         final boolean shouldAvoidSendingCode = Constants.CODE_SPACE == codePoint
@@ -832,7 +865,7 @@
                 final String separator = shouldAvoidSendingCode ? LastComposedWord.NOT_A_SEPARATOR
                         : StringUtils.newSingleCodePointString(codePoint);
                 commitCurrentAutoCorrection(settingsValues, separator, handler);
-                didAutoCorrect = true;
+                inputTransaction.setDidAutoCorrect();
             } else {
                 commitTyped(settingsValues,
                         StringUtils.newSingleCodePointString(codePoint));
@@ -910,7 +943,6 @@
         }
 
         inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
-        return didAutoCorrect;
     }
 
     /**
@@ -1974,14 +2006,13 @@
      * This method handles the retry, and re-schedules a new retry if we still can't access.
      * We only retry up to 5 times before giving up.
      *
-     * @param settingsValues the current values of the settings.
      * @param tryResumeSuggestions Whether we should resume suggestions or not.
      * @param remainingTries How many times we may try again before giving up.
      * @return whether true if the caches were successfully reset, false otherwise.
      */
     // TODO: make this private
-    public boolean retryResetCachesAndReturnSuccess(final SettingsValues settingsValues,
-            final boolean tryResumeSuggestions, final int remainingTries,
+    public boolean retryResetCachesAndReturnSuccess(final boolean tryResumeSuggestions,
+            final int remainingTries,
             // TODO: remove these arguments
             final LatinIME.UIHandler handler) {
         final boolean shouldFinishComposition = mConnection.hasSelection()