diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 2afb1c9fcac030073351fa64a0c5ac51b4cfa07d..d6d0329bd612664e5f9d753bff8b6347155a2b52 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -68,7 +68,6 @@ import android.view.WindowManager;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedText;
-import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 import android.widget.LinearLayout;
 
@@ -152,6 +151,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
     private KeyboardSwitcher mKeyboardSwitcher;
     private SubtypeSwitcher mSubtypeSwitcher;
     private VoiceProxy mVoiceProxy;
+    private Recorrection mRecorrection;
 
     private UserDictionary mUserDictionary;
     private UserBigramDictionary mUserBigramDictionary;
@@ -176,7 +176,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
     // punctuation on punctuation insertion, and become a real space on alpha char insertion.
     private boolean mJustAddedMagicSpace; // This indicates whether the last char is a magic space.
     private boolean mAutoCorrectEnabled;
-    private boolean mRecorrectionEnabled;
     // Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary
     private boolean mBigramSuggestionEnabled;
     // Prediction: use bigrams to predict the next word when there is no input for it yet
@@ -403,6 +402,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
         SubtypeSwitcher.init(this, prefs);
         KeyboardSwitcher.init(this, prefs);
         AccessibilityUtils.init(this, prefs);
+        Recorrection.init(this, prefs);
 
         super.onCreate();
 
@@ -411,19 +411,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
         mSubtypeSwitcher = SubtypeSwitcher.getInstance();
         mKeyboardSwitcher = KeyboardSwitcher.getInstance();
         mAccessibilityUtils = AccessibilityUtils.getInstance();
+        mRecorrection = Recorrection.getInstance();
 
         final Resources res = getResources();
         mResources = res;
 
-        // If the option should not be shown, do not read the recorrection preference
-        // but always use the default setting defined in the resources.
-        if (res.getBoolean(R.bool.config_enable_show_recorrection_option)) {
-            mRecorrectionEnabled = prefs.getBoolean(Settings.PREF_RECORRECTION_ENABLED,
-                    res.getBoolean(R.bool.config_default_recorrection_enabled));
-        } else {
-            mRecorrectionEnabled = res.getBoolean(R.bool.config_default_recorrection_enabled);
-        }
-
         mConfigEnableShowSubtypeSettings = res.getBoolean(
                 R.bool.config_enable_show_subtype_settings);
         mConfigSwipeDownDismissKeyboardEnabled = res.getBoolean(
@@ -624,7 +616,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
         inputView.setProximityCorrectionEnabled(true);
         inputView.setAccessibilityEnabled(accessibilityEnabled);
         // If we just entered a text field, maybe it has some old text that requires correction
-        checkRecorrectionOnStart();
+        mRecorrection.checkRecorrectionOnStart();
         inputView.setForeground(true);
 
         voiceIme.onStartInputView(inputView.getWindowToken());
@@ -687,34 +679,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
         }
     }
 
-    private void checkRecorrectionOnStart() {
-        if (!mRecorrectionEnabled) return;
-
-        final InputConnection ic = getCurrentInputConnection();
-        if (ic == null) return;
-        // There could be a pending composing span.  Clean it up first.
-        ic.finishComposingText();
-
-        if (isShowingSuggestionsStrip() && isSuggestionsRequested()) {
-            // First get the cursor position. This is required by setOldSuggestions(), so that
-            // it can pass the correct range to setComposingRegion(). At this point, we don't
-            // have valid values for mLastSelectionStart/End because onUpdateSelection() has
-            // not been called yet.
-            ExtractedTextRequest etr = new ExtractedTextRequest();
-            etr.token = 0; // anything is fine here
-            ExtractedText et = ic.getExtractedText(etr, 0);
-            if (et == null) return;
-
-            mLastSelectionStart = et.startOffset + et.selectionStart;
-            mLastSelectionEnd = et.startOffset + et.selectionEnd;
-
-            // Then look for possible corrections in a delayed fashion
-            if (!TextUtils.isEmpty(et.text) && isCursorTouchingWord()) {
-                mHandler.postUpdateOldSuggestions();
-            }
-        }
-    }
-
     @Override
     public void onFinishInput() {
         super.onFinishInput();
@@ -808,34 +772,15 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
         mLastSelectionStart = newSelStart;
         mLastSelectionEnd = newSelEnd;
 
-        if (mRecorrectionEnabled && isShowingSuggestionsStrip()) {
-            // Don't look for corrections if the keyboard is not visible
-            if (mKeyboardSwitcher.isInputViewShown()) {
-                // Check if we should go in or out of correction mode.
-                if (isSuggestionsRequested()
-                        && (candidatesStart == candidatesEnd || newSelStart != oldSelStart
-                                || TextEntryState.isRecorrecting())
-                                && (newSelStart < newSelEnd - 1 || !mHasUncommittedTypedChars)) {
-                    if (isCursorTouchingWord() || mLastSelectionStart < mLastSelectionEnd) {
-                        mHandler.cancelUpdateBigramPredictions();
-                        mHandler.postUpdateOldSuggestions();
-                    } else {
-                        abortRecorrection(false);
-                        // If showing the "touch again to save" hint, do not replace it. Else,
-                        // show the bigrams if we are at the end of the text, punctuation otherwise.
-                        if (mCandidateView != null
-                                && !mCandidateView.isShowingAddToDictionaryHint()) {
-                            InputConnection ic = getCurrentInputConnection();
-                            if (null == ic || !TextUtils.isEmpty(ic.getTextAfterCursor(1, 0))) {
-                                if (!isShowingPunctuationList()) setPunctuationSuggestions();
-                            } else {
-                                mHandler.postUpdateBigramPredictions();
-                            }
-                        }
-                    }
-                }
-            }
-        }
+        mRecorrection.updateRecorrectionSelection(mKeyboardSwitcher,
+                mCandidateView, candidatesStart, candidatesEnd, newSelStart,
+                newSelEnd, oldSelStart, mLastSelectionStart,
+                mLastSelectionEnd, mHasUncommittedTypedChars);
+    }
+
+    public void setLastSelection(int start, int end) {
+        mLastSelectionStart = start;
+        mLastSelectionEnd = end;
     }
 
     /**
@@ -848,7 +793,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
      */
     @Override
     public void onExtractedTextClicked() {
-        if (mRecorrectionEnabled && isSuggestionsRequested()) return;
+        if (mRecorrection.isRecorrectionEnabled() && isSuggestionsRequested()) return;
 
         super.onExtractedTextClicked();
     }
@@ -864,7 +809,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
      */
     @Override
     public void onExtractedCursorMovement(int dx, int dy) {
-        if (mRecorrectionEnabled && isSuggestionsRequested()) return;
+        if (mRecorrection.isRecorrectionEnabled() && isSuggestionsRequested()) return;
 
         super.onExtractedCursorMovement(dx, dy);
     }
@@ -1318,7 +1263,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
         }
     }
 
-    private void abortRecorrection(boolean force) {
+    public void abortRecorrection(boolean force) {
         if (force || TextEntryState.isRecorrecting()) {
             TextEntryState.onAbortRecorrection();
             setCandidatesViewShown(isCandidateStripVisible());
@@ -1495,16 +1440,16 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
         mWordHistory.add(entry);
     }
 
-    private boolean isSuggestionsRequested() {
+    public boolean isSuggestionsRequested() {
         return mIsSettingsSuggestionStripOn
                 && (mCorrectionMode > 0 || isShowingSuggestionsStrip());
     }
 
-    private boolean isShowingPunctuationList() {
+    public boolean isShowingPunctuationList() {
         return mSuggestPuncList == mCandidateView.getSuggestions();
     }
 
-    private boolean isShowingSuggestionsStrip() {
+    public boolean isShowingSuggestionsStrip() {
         return (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_VALUE)
                 || (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE
                         && mOrientation == Configuration.ORIENTATION_PORTRAIT);
@@ -1877,7 +1822,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
         }
     }
 
-    private void setPunctuationSuggestions() {
+    public void setPunctuationSuggestions() {
         setSuggestions(mSuggestPuncList);
         setCandidatesViewShown(isCandidateStripVisible());
     }
@@ -1925,7 +1870,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
         }
     }
 
-    private boolean isCursorTouchingWord() {
+    public boolean isCursorTouchingWord() {
         InputConnection ic = getCurrentInputConnection();
         if (ic == null) return false;
         CharSequence toLeft = ic.getTextBeforeCursor(1, 0);
diff --git a/java/src/com/android/inputmethod/latin/Recorrection.java b/java/src/com/android/inputmethod/latin/Recorrection.java
new file mode 100644
index 0000000000000000000000000000000000000000..71d3bdddc845d80ebb7c8d2094a7878a60fae23d
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/Recorrection.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
+
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.text.TextUtils;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnection;
+
+/**
+ * Manager of re-correction functionalities
+ */
+public class Recorrection {
+    private static final Recorrection sInstance = new Recorrection();
+
+    private LatinIME mService;
+    private boolean mRecorrectionEnabled = false;
+
+    public static Recorrection getInstance() {
+        return sInstance;
+    }
+
+    public static void init(LatinIME context, SharedPreferences prefs) {
+        if (context == null || prefs == null) {
+            return;
+        }
+        sInstance.initInternal(context, prefs);
+    }
+
+    private Recorrection() {
+    }
+
+    public boolean isRecorrectionEnabled() {
+        return mRecorrectionEnabled;
+    }
+
+    private void initInternal(LatinIME context, SharedPreferences prefs) {
+        final Resources res = context.getResources();
+        // If the option should not be shown, do not read the re-correction preference
+        // but always use the default setting defined in the resources.
+        if (res.getBoolean(R.bool.config_enable_show_recorrection_option)) {
+            mRecorrectionEnabled = prefs.getBoolean(Settings.PREF_RECORRECTION_ENABLED,
+                    res.getBoolean(R.bool.config_default_recorrection_enabled));
+        } else {
+            mRecorrectionEnabled = res.getBoolean(R.bool.config_default_recorrection_enabled);
+        }
+        mService = context;
+    }
+
+    public void checkRecorrectionOnStart() {
+        if (!mRecorrectionEnabled) return;
+
+        final InputConnection ic = mService.getCurrentInputConnection();
+        if (ic == null) return;
+        // There could be a pending composing span.  Clean it up first.
+        ic.finishComposingText();
+
+        if (mService.isShowingSuggestionsStrip() && mService.isSuggestionsRequested()) {
+            // First get the cursor position. This is required by setOldSuggestions(), so that
+            // it can pass the correct range to setComposingRegion(). At this point, we don't
+            // have valid values for mLastSelectionStart/End because onUpdateSelection() has
+            // not been called yet.
+            ExtractedTextRequest etr = new ExtractedTextRequest();
+            etr.token = 0; // anything is fine here
+            ExtractedText et = ic.getExtractedText(etr, 0);
+            if (et == null) return;
+            mService.setLastSelection(
+                    et.startOffset + et.selectionStart, et.startOffset + et.selectionEnd);
+
+            // Then look for possible corrections in a delayed fashion
+            if (!TextUtils.isEmpty(et.text) && mService.isCursorTouchingWord()) {
+                mService.mHandler.postUpdateOldSuggestions();
+            }
+        }
+    }
+
+    public void updateRecorrectionSelection(KeyboardSwitcher keyboardSwitcher,
+            CandidateView candidateView, int candidatesStart, int candidatesEnd, int newSelStart,
+            int newSelEnd, int oldSelStart, int lastSelectionStart,
+            int lastSelectionEnd, boolean hasUncommittedTypedChars) {
+        if (mRecorrectionEnabled && mService.isShowingSuggestionsStrip()) {
+            // Don't look for corrections if the keyboard is not visible
+            if (keyboardSwitcher.isInputViewShown()) {
+                // Check if we should go in or out of correction mode.
+                if (mService.isSuggestionsRequested()
+                        && (candidatesStart == candidatesEnd || newSelStart != oldSelStart
+                                || TextEntryState.isRecorrecting())
+                                && (newSelStart < newSelEnd - 1 || !hasUncommittedTypedChars)) {
+                    if (mService.isCursorTouchingWord() || lastSelectionStart < lastSelectionEnd) {
+                        mService.mHandler.cancelUpdateBigramPredictions();
+                        mService.mHandler.postUpdateOldSuggestions();
+                    } else {
+                        mService.abortRecorrection(false);
+                        // If showing the "touch again to save" hint, do not replace it. Else,
+                        // show the bigrams if we are at the end of the text, punctuation otherwise.
+                        if (candidateView != null
+                                && !candidateView.isShowingAddToDictionaryHint()) {
+                            InputConnection ic = mService.getCurrentInputConnection();
+                            if (null == ic || !TextUtils.isEmpty(ic.getTextAfterCursor(1, 0))) {
+                                if (!mService.isShowingPunctuationList()) {
+                                    mService.setPunctuationSuggestions();
+                                }
+                            } else {
+                                mService.mHandler.postUpdateBigramPredictions();
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}