Merge "Support terminal insertion error correction"
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 2123380..6aa43b9 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -1032,8 +1032,9 @@
     @Override
     public void onShowMoreKeysPanel(final MoreKeysPanel panel) {
         locatePreviewPlacerView();
-        if (isShowingMoreKeysPanel()) {
-            onDismissMoreKeysPanel();
+        // TODO: Remove this check
+        if (panel.isShowingInParent()) {
+            panel.dismissMoreKeysPanel();
         }
         mPreviewPlacerView.addView(panel.getContainerView());
         mMoreKeysPanel = panel;
@@ -1045,19 +1046,17 @@
     }
 
     @Override
-    public void onCancelMoreKeysPanel() {
+    public void onCancelMoreKeysPanel(final MoreKeysPanel panel) {
         PointerTracker.dismissAllMoreKeysPanels();
     }
 
     @Override
-    public boolean onDismissMoreKeysPanel() {
+    public void onDismissMoreKeysPanel(final MoreKeysPanel panel) {
         dimEntireKeyboard(false /* dimmed */);
         if (isShowingMoreKeysPanel()) {
             mPreviewPlacerView.removeView(mMoreKeysPanel.getContainerView());
             mMoreKeysPanel = null;
-            return true;
         }
-        return false;
     }
 
     @Override
@@ -1207,15 +1206,18 @@
                 eventTag + eventTime + "," + id + "," + x + "," + y + "," + size + "," + pressure);
     }
 
-    public void cancelAllMessages() {
+    public void cancelAllOngoingEvents() {
         mKeyTimerHandler.cancelAllMessages();
         mDrawingHandler.cancelAllMessages();
+        dismissAllKeyPreviews();
+        dismissGestureFloatingPreviewText();
+        dismissSlidingKeyInputPreview();
+        PointerTracker.dismissAllMoreKeysPanels();
+        PointerTracker.cancelAllPointerTrackers();
     }
 
     public void closing() {
-        dismissAllKeyPreviews();
-        cancelAllMessages();
-        onDismissMoreKeysPanel();
+        cancelAllOngoingEvents();
         mMoreKeysKeyboardCache.clear();
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
index 897ad1d..94f6a3c 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
@@ -34,7 +34,7 @@
     private final int[] mCoordinates = CoordinateUtils.newInstance();
 
     protected final KeyDetector mKeyDetector;
-    private Controller mController;
+    private Controller mController = EMPTY_CONTROLLER;
     protected KeyboardActionListener mListener;
     private int mOriginX;
     private int mOriginY;
@@ -119,7 +119,7 @@
         onMoveKeyInternal(x, y, pointerId);
         if (hasOldKey && mCurrentKey == null) {
             // If the pointer has moved too far away from any target then cancel the panel.
-            mController.onCancelMoreKeysPanel();
+            mController.onCancelMoreKeysPanel(this);
         }
     }
 
@@ -173,9 +173,11 @@
     }
 
     @Override
-    public boolean dismissMoreKeysPanel() {
-        if (mController == null) return false;
-        return mController.onDismissMoreKeysPanel();
+    public void dismissMoreKeysPanel() {
+        if (!isShowingInParent()) {
+            return;
+        }
+        mController.onDismissMoreKeysPanel(this);
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java b/java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java
index 9c677e5..886c628 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java
@@ -22,21 +22,32 @@
     public interface Controller {
         /**
          * Add the {@link MoreKeysPanel} to the target view.
-         * @param panel
+         * @param panel the panel to be shown.
          */
         public void onShowMoreKeysPanel(final MoreKeysPanel panel);
 
         /**
          * Remove the current {@link MoreKeysPanel} from the target view.
+         * @param panel the panel to be dismissed.
          */
-        public boolean onDismissMoreKeysPanel();
+        public void onDismissMoreKeysPanel(final MoreKeysPanel panel);
 
         /**
          * Instructs the parent to cancel the panel (e.g., when entering a different input mode).
+         * @param panel the panel to be canceled.
          */
-        public void onCancelMoreKeysPanel();
+        public void onCancelMoreKeysPanel(final MoreKeysPanel panel);
     }
 
+    public static final Controller EMPTY_CONTROLLER = new Controller() {
+        @Override
+        public void onShowMoreKeysPanel(final MoreKeysPanel panel) {}
+        @Override
+        public void onDismissMoreKeysPanel(final MoreKeysPanel panel) {}
+        @Override
+        public void onCancelMoreKeysPanel(final MoreKeysPanel panel) {}
+    };
+
     /**
      * Initializes the layout and event handling of this {@link MoreKeysPanel} and calls the
      * controller's onShowMoreKeysPanel to add the panel's container view.
@@ -57,7 +68,7 @@
      * Dismisses the more keys panel and calls the controller's onDismissMoreKeysPanel to remove
      * the panel's container view.
      */
-    public boolean dismissMoreKeysPanel();
+    public void dismissMoreKeysPanel();
 
     /**
      * Process a move event on the more keys panel.
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index a1cac3f..c7b0964 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -413,6 +413,10 @@
         return sPointerTrackerQueue.isAnyInSlidingKeyInput();
     }
 
+    public static void cancelAllPointerTrackers() {
+        sPointerTrackerQueue.cancelAllPointerTrackers();
+    }
+
     public static void setKeyboardActionListener(final KeyboardActionListener listener) {
         final int trackersSize = sTrackers.size();
         for (int i = 0; i < trackersSize; ++i) {
@@ -833,7 +837,7 @@
     }
 
     private void cancelBatchInput() {
-        sPointerTrackerQueue.cancelAllPointerTracker();
+        cancelAllPointerTrackers();
         mIsDetectingGesture = false;
         if (!sInGesture) {
             return;
@@ -1273,7 +1277,7 @@
         }
 
         cancelBatchInput();
-        sPointerTrackerQueue.cancelAllPointerTracker();
+        cancelAllPointerTrackers();
         sPointerTrackerQueue.releaseAllPointers(eventTime);
         onCancelEventInternal();
     }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
index 3dbb7b7..7ee45e8 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
@@ -207,7 +207,7 @@
         }
     }
 
-    public void cancelAllPointerTracker() {
+    public void cancelAllPointerTrackers() {
         synchronized (mExpandableArrayOfActivePointers) {
             if (DEBUG) {
                 Log.d(TAG, "cancelAllPointerTracker: " + this);
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index ef8a1c8..6384ef7 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -886,7 +886,7 @@
         mKeyboardSwitcher.onFinishInputView();
         final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
         if (mainKeyboardView != null) {
-            mainKeyboardView.cancelAllMessages();
+            mainKeyboardView.cancelAllOngoingEvents();
         }
         // Remove pending messages related to update suggestions
         mHandler.cancelUpdateSuggestionStrip();
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index fcebdd4..497a791 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -162,27 +162,27 @@
         mSuggestionsStrip.removeAllViews();
         removeAllViews();
         addView(mSuggestionsStrip);
-        dismissMoreSuggestions();
+        mMoreSuggestionsView.dismissMoreKeysPanel();
     }
 
     private final MoreSuggestionsListener mMoreSuggestionsListener = new MoreSuggestionsListener() {
         @Override
         public void onSuggestionSelected(final int index, final SuggestedWordInfo wordInfo) {
             mListener.pickSuggestionManually(index, wordInfo);
-            dismissMoreSuggestions();
+            mMoreSuggestionsView.dismissMoreKeysPanel();
         }
 
         @Override
         public void onCancelInput() {
-            dismissMoreSuggestions();
+            mMoreSuggestionsView.dismissMoreKeysPanel();
         }
     };
 
     private final MoreKeysPanel.Controller mMoreSuggestionsController =
             new MoreKeysPanel.Controller() {
         @Override
-        public boolean onDismissMoreKeysPanel() {
-            return mMainKeyboardView.onDismissMoreKeysPanel();
+        public void onDismissMoreKeysPanel(final MoreKeysPanel panel) {
+            mMainKeyboardView.onDismissMoreKeysPanel(panel);
         }
 
         @Override
@@ -191,15 +191,11 @@
         }
 
         @Override
-        public void onCancelMoreKeysPanel() {
-            dismissMoreSuggestions();
+        public void onCancelMoreKeysPanel(final MoreKeysPanel panel) {
+            mMoreSuggestionsView.dismissMoreKeysPanel();
         }
     };
 
-    boolean dismissMoreSuggestions() {
-        return mMoreSuggestionsView.dismissMoreKeysPanel();
-    }
-
     @Override
     public boolean onLongClick(final View view) {
         AudioAndHapticFeedbackManager.getInstance().hapticAndAudioFeedback(
@@ -330,6 +326,6 @@
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        dismissMoreSuggestions();
+        mMoreSuggestionsView.dismissMoreKeysPanel();
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java
index dad7296..ba5a684 100644
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java
@@ -65,6 +65,8 @@
     private String mLocale;
     private final String mOldWord;
     private final String mOldShortcut;
+    private String mSavedWord;
+    private String mSavedShortcut;
 
     /* package */ UserDictionaryAddWordContents(final View view, final Bundle args) {
         mWordEditText = (EditText)view.findViewById(R.id.user_dictionary_add_word_text);
@@ -96,6 +98,16 @@
         updateLocale(args.getString(EXTRA_LOCALE));
     }
 
+    /* package */ UserDictionaryAddWordContents(final View view,
+            final UserDictionaryAddWordContents oldInstanceToBeEdited) {
+        mWordEditText = (EditText)view.findViewById(R.id.user_dictionary_add_word_text);
+        mShortcutEditText = (EditText)view.findViewById(R.id.user_dictionary_add_shortcut);
+        mMode = MODE_EDIT;
+        mOldWord = oldInstanceToBeEdited.mSavedWord;
+        mOldShortcut = oldInstanceToBeEdited.mSavedShortcut;
+        updateLocale(mLocale);
+    }
+
     // locale may be null, this means default locale
     // It may also be the empty string, which means "all locales"
     /* package */ void updateLocale(final String locale) {
@@ -149,6 +161,8 @@
             // If the word is somehow empty, don't insert it.
             return CODE_CANCEL;
         }
+        mSavedWord = newWord;
+        mSavedShortcut = newShortcut;
         // If there is no shortcut, and the word already exists in the database, then we
         // should not insert, because either A. the word exists with no shortcut, in which
         // case the exact same thing we want to insert is already there, or B. the word
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java
index 58c8f26..8b8bd5e 100644
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java
@@ -57,23 +57,36 @@
     private boolean mIsDeleting = false;
 
     @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
+    public void onActivityCreated(final Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
         setHasOptionsMenu(true);
+        // Keep the instance so that we remember mContents when configuration changes (eg rotation)
+        setRetainInstance(true);
     }
 
     @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+    public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
+            final Bundle savedState) {
         mRootView = inflater.inflate(R.layout.user_dictionary_add_word_fullscreen, null);
         mIsDeleting = false;
+        // If we have a non-null mContents object, it's the old value before a configuration
+        // change (eg rotation) so we need to use its values. Otherwise, read from the arguments.
         if (null == mContents) {
             mContents = new UserDictionaryAddWordContents(mRootView, getArguments());
+        } else {
+            // We create a new mContents object to account for the new situation : a word has
+            // been added to the user dictionary when we started rotating, and we are now editing
+            // it. That means in particular if the word undergoes any change, the old version should
+            // be updated, so the mContents object needs to switch to EDIT mode if it was in
+            // INSERT mode.
+            mContents = new UserDictionaryAddWordContents(mRootView,
+                    mContents /* oldInstanceToBeEdited */);
         }
         return mRootView;
     }
 
     @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+    public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
         final MenuItem actionItemAdd = menu.add(0, OPTIONS_MENU_ADD, 0,
                 R.string.user_dict_settings_add_menu_title).setIcon(R.drawable.ic_menu_add);
         actionItemAdd.setShowAsAction(