Merge "Add SpacebarLanguageUtils class"
diff --git a/java/src/com/android/inputmethod/event/InputTransaction.java b/java/src/com/android/inputmethod/event/InputTransaction.java
index 2e9014f..4fe9b40 100644
--- a/java/src/com/android/inputmethod/event/InputTransaction.java
+++ b/java/src/com/android/inputmethod/event/InputTransaction.java
@@ -40,6 +40,7 @@
 
     // Outputs
     private int mRequiredShiftUpdate = SHIFT_NO_UPDATE;
+    private boolean mRequiresUpdateSuggestions = false;
 
     public InputTransaction(final SettingsValues settingsValues, final Event event,
             final long timestamp, final int spaceState, final int shiftState) {
@@ -50,10 +51,34 @@
         mShiftState = shiftState;
     }
 
+    /**
+     * Indicate that this transaction requires some type of shift update.
+     * @param updateType What type of shift update this requires.
+     */
     public void requireShiftUpdate(final int updateType) {
         mRequiredShiftUpdate = Math.max(mRequiredShiftUpdate, updateType);
     }
+
+    /**
+     * Gets what type of shift update this transaction requires.
+     * @return The shift update type.
+     */
     public int getRequiredShiftUpdate() {
         return mRequiredShiftUpdate;
     }
+
+    /**
+     * Indicate that this transaction requires updating the suggestions.
+     */
+    public void setRequiresUpdateSuggestions() {
+        mRequiresUpdateSuggestions = true;
+    }
+
+    /**
+     * Find out whether this transaction requires updating the suggestions.
+     * @return Whether this transaction requires updating the suggestions.
+     */
+    public boolean requiresUpdateSuggestions() {
+        return mRequiresUpdateSuggestions;
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 0c0be82..53e6232 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -38,7 +38,6 @@
 import android.os.Debug;
 import android.os.IBinder;
 import android.os.Message;
-import android.os.SystemClock;
 import android.preference.PreferenceManager;
 import android.text.InputType;
 import android.text.TextUtils;
@@ -1230,7 +1229,7 @@
         final InputTransaction completeInputTransaction =
                 mInputLogic.onCodeInput(mSettings.getCurrent(), event,
                         mKeyboardSwitcher.getKeyboardShiftMode(), mHandler);
-        updateShiftModeAfterInputTransaction(completeInputTransaction.getRequiredShiftUpdate());
+        updateStateAfterInputTransaction(completeInputTransaction);
         mKeyboardSwitcher.onCodeInput(codePoint);
     }
 
@@ -1450,7 +1449,7 @@
         final InputTransaction completeInputTransaction = mInputLogic.onPickSuggestionManually(
                 mSettings.getCurrent(), index, suggestionInfo,
                 mKeyboardSwitcher.getKeyboardShiftMode(), mHandler);
-        updateShiftModeAfterInputTransaction(completeInputTransaction.getRequiredShiftUpdate());
+        updateStateAfterInputTransaction(completeInputTransaction);
     }
 
     @Override
@@ -1488,8 +1487,14 @@
         }
     }
 
-    private void updateShiftModeAfterInputTransaction(final int requiredShiftUpdate) {
-        switch (requiredShiftUpdate) {
+    /**
+     * After an input transaction has been executed, some state must be updated. This includes
+     * the shift state of the keyboard and suggestions. This method looks at the finished
+     * inputTransaction to find out what is necessary and updates the state accordingly.
+     * @param inputTransaction The transaction that has been executed.
+     */
+    private void updateStateAfterInputTransaction(final InputTransaction inputTransaction) {
+        switch (inputTransaction.getRequiredShiftUpdate()) {
         case InputTransaction.SHIFT_UPDATE_LATER:
             mHandler.postUpdateShiftState();
             break;
@@ -1498,6 +1503,9 @@
             break;
         default: // SHIFT_NO_UPDATE
         }
+        if (inputTransaction.requiresUpdateSuggestions()) {
+            mHandler.postUpdateSuggestionStrip();
+        }
     }
 
     private void hapticAndAudioFeedback(final int code, final int repeatCount) {
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index e2cdbb3..ec6bd28 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -130,8 +130,11 @@
         // so we try using some heuristics to find out about these and fix them.
         mConnection.tryFixLyingCursorPosition();
         cancelDoubleSpacePeriodCountdown();
-        mInputLogicHandler.destroy();
-        mInputLogicHandler = new InputLogicHandler(mLatinIME, this);
+        if (InputLogicHandler.NULL_HANDLER == mInputLogicHandler) {
+            mInputLogicHandler = new InputLogicHandler(mLatinIME, this);
+        } else {
+            mInputLogicHandler.reset();
+        }
     }
 
     /**
@@ -142,8 +145,7 @@
             mConnection.finishComposingText();
         }
         resetComposingState(true /* alsoResetLastComposedWord */);
-        mInputLogicHandler.destroy();
-        mInputLogicHandler = InputLogicHandler.NULL_HANDLER;
+        mInputLogicHandler.reset();
     }
 
     /**
@@ -403,7 +405,7 @@
             // A special key, like delete, shift, emoji, or the settings key.
             switch (event.mKeyCode) {
             case Constants.CODE_DELETE:
-                handleBackspace(inputTransaction, handler);
+                handleBackspace(inputTransaction);
                 LatinImeLogger.logOnDelete(event.mX, event.mY);
                 break;
             case Constants.CODE_SHIFT:
@@ -672,7 +674,7 @@
                     commitTyped(inputTransaction.mSettingsValues, LastComposedWord.NOT_A_SEPARATOR);
                 }
             }
-            handleNonSeparator(inputTransaction.mSettingsValues, inputTransaction, handler);
+            handleNonSeparator(inputTransaction.mSettingsValues, inputTransaction);
         }
         return didAutoCorrect;
     }
@@ -683,9 +685,7 @@
      * @param inputTransaction The transaction in progress.
      */
     private void handleNonSeparator(final SettingsValues settingsValues,
-            final InputTransaction inputTransaction,
-            // TODO: Remove this argument
-            final LatinIME.UIHandler handler) {
+            final InputTransaction inputTransaction) {
         final int codePoint = inputTransaction.mEvent.mCodePoint;
         // TODO: refactor this method to stop flipping isComposingWord around all the time, and
         // make it shorter (possibly cut into several pieces). Also factor handleNonSpecialCharacter
@@ -761,7 +761,7 @@
             // In case the "add to dictionary" hint was still displayed.
             mSuggestionStripViewAccessor.dismissAddToDictionaryHint();
         }
-        handler.postUpdateSuggestionStrip();
+        inputTransaction.setRequiresUpdateSuggestions();
         if (settingsValues.mIsInternal) {
             LatinImeLoggerUtils.onNonSeparator((char)codePoint, inputTransaction.mEvent.mX,
                     inputTransaction.mEvent.mY);
@@ -843,7 +843,7 @@
             }
 
             startDoubleSpacePeriodCountdown(inputTransaction);
-            handler.postUpdateSuggestionStrip();
+            inputTransaction.setRequiresUpdateSuggestions();
         } else {
             if (swapWeakSpace) {
                 swapSwapperAndSpace(inputTransaction);
@@ -879,9 +879,7 @@
      * Handle a press on the backspace key.
      * @param inputTransaction The transaction in progress.
      */
-    private void handleBackspace(final InputTransaction inputTransaction,
-            // TODO: remove this argument
-            final LatinIME.UIHandler handler) {
+    private void handleBackspace(final InputTransaction inputTransaction) {
         mSpaceState = SpaceState.NONE;
         mDeleteCount++;
 
@@ -910,7 +908,7 @@
                 mWordComposer.deleteLast(inputTransaction.mEvent);
             }
             mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
-            handler.postUpdateSuggestionStrip();
+            inputTransaction.setRequiresUpdateSuggestions();
             if (!mWordComposer.isComposingWord()) {
                 // If we just removed the last character, auto-caps mode may have changed so we
                 // need to re-evaluate.
@@ -921,7 +919,7 @@
                 if (inputTransaction.mSettingsValues.mIsInternal) {
                     LatinImeLoggerUtils.onAutoCorrectionCancellation();
                 }
-                revertCommit(inputTransaction.mSettingsValues, handler);
+                revertCommit(inputTransaction);
                 return;
             }
             if (mEnteredText != null && mConnection.sameAsTextBeforeCursor(mEnteredText)) {
@@ -1400,11 +1398,9 @@
      *
      * This is triggered upon pressing backspace just after a commit with auto-correction.
      *
-     * @param settingsValues the current settings values.
+     * @param inputTransaction The transaction in progress.
      */
-    private void revertCommit(final SettingsValues settingsValues,
-            // TODO: remove this argument
-            final LatinIME.UIHandler handler) {
+    private void revertCommit(final InputTransaction inputTransaction) {
         final String previousWord = mLastComposedWord.mPrevWord;
         final CharSequence originallyTypedWord = mLastComposedWord.mTypedWord;
         final CharSequence committedWord = mLastComposedWord.mCommittedWord;
@@ -1448,7 +1444,8 @@
                 // Given this, we add it to the list of suggestions, otherwise we discard it.
                 if (span instanceof SuggestionSpan) {
                     final SuggestionSpan suggestionSpan = (SuggestionSpan)span;
-                    if (!suggestionSpan.getLocale().equals(settingsValues.mLocale.toString())) {
+                    if (!suggestionSpan.getLocale().equals(
+                            inputTransaction.mSettingsValues.mLocale.toString())) {
                         continue;
                     }
                     for (final String suggestion : suggestionSpan.getSuggestions()) {
@@ -1463,11 +1460,11 @@
                 }
             }
             // Add the suggestion list to the list of suggestions.
-            textToCommit.setSpan(new SuggestionSpan(settingsValues.mLocale,
+            textToCommit.setSpan(new SuggestionSpan(inputTransaction.mSettingsValues.mLocale,
                     suggestions.toArray(new String[suggestions.size()]), 0 /* flags */),
                     0 /* start */, lastCharIndex /* end */, 0 /* flags */);
         }
-        if (settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces) {
+        if (inputTransaction.mSettingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces) {
             // For languages with spaces, we revert to the typed string, but the cursor is still
             // after the separator so we don't resume suggestions. If the user wants to correct
             // the word, they have to press backspace again.
@@ -1480,7 +1477,7 @@
                     mLatinIME.getCoordinatesForCurrentKeyboard(codePoints), previousWord);
             mConnection.setComposingText(textToCommit, 1);
         }
-        if (settingsValues.mIsInternal) {
+        if (inputTransaction.mSettingsValues.mIsInternal) {
             LatinImeLoggerUtils.onSeparator(mLastComposedWord.mSeparatorString,
                     Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
         }
@@ -1493,7 +1490,7 @@
         // separator.
         mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
         // We have a separator between the word and the cursor: we should show predictions.
-        handler.postUpdateSuggestionStrip();
+        inputTransaction.setRequiresUpdateSuggestions();
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
index db96de3..42f0d7c 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
@@ -43,7 +43,7 @@
     // is initialized, though probably only the monkey can actually do this.
     public static final InputLogicHandler NULL_HANDLER = new InputLogicHandler() {
         @Override
-        public void destroy() {}
+        public void reset() {}
         @Override
         public boolean handleMessage(final Message msg) { return true; }
         @Override
@@ -75,8 +75,8 @@
         mInputLogic = inputLogic;
     }
 
-    public void destroy() {
-        mNonUIThreadHandler.getLooper().quit();
+    public void reset() {
+        mNonUIThreadHandler.removeCallbacksAndMessages(null);
     }
 
     /**