Merge "Send touch event in usability study mode"
diff --git a/java/res/layout/input_view.xml b/java/res/layout/input_view.xml
index 2e0cddc..b9451f8 100644
--- a/java/res/layout/input_view.xml
+++ b/java/res/layout/input_view.xml
@@ -43,7 +43,7 @@
             android:layout_width="@dimen/suggestions_strip_padding"
             android:layout_height="@dimen/suggestions_strip_height"
             style="?attr/suggestionsStripBackgroundStyle" />
-        <com.android.inputmethod.latin.SuggestionsView
+        <com.android.inputmethod.latin.suggestions.SuggestionsView
             android:id="@+id/suggestions_view"
             android:layout_weight="1.0"
             android:layout_width="0dp"
diff --git a/java/res/layout/more_suggestions.xml b/java/res/layout/more_suggestions.xml
index 6aa82e1..1e59b89 100644
--- a/java/res/layout/more_suggestions.xml
+++ b/java/res/layout/more_suggestions.xml
@@ -24,7 +24,7 @@
         android:orientation="horizontal"
         style="?attr/miniKeyboardPanelStyle"
         >
-    <com.android.inputmethod.latin.MoreSuggestionsView
+    <com.android.inputmethod.latin.suggestions.MoreSuggestionsView
             xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
             android:id="@+id/more_suggestions_view"
             android:layout_alignParentBottom="true"
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 94f7ab7..d085644 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -127,9 +127,11 @@
             <flag name="autoCorrectUnderline" value="0x02" />
             <flag name="validTypedWordBold" value="0x04" />
         </attr>
+        <attr name="colorValidTypedWord" format="color" />
         <attr name="colorTypedWord" format="color" />
         <attr name="colorAutoCorrect" format="color" />
         <attr name="colorSuggested" format="color" />
+        <attr name="alphaValidTypedWord" format="integer" />
         <attr name="alphaTypedWord" format="integer" />
         <attr name="alphaAutoCorrect" format="integer" />
         <attr name="alphaSuggested" format="integer" />
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index 2b5fb08..2025a7d 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -97,7 +97,8 @@
         name="SuggestionsViewStyle"
         parent="SuggestionsStripBackgroundStyle"
     >
-        <item name="suggestionStripOption">autoCorrectBold</item>
+        <item name="suggestionStripOption">autoCorrectBold|validTypedWordBold</item>
+        <item name="colorValidTypedWord">#FFFCAE00</item>
         <item name="colorTypedWord">@android:color/white</item>
         <item name="colorAutoCorrect">#FFFCAE00</item>
         <item name="colorSuggested">#FFFCAE00</item>
@@ -296,11 +297,12 @@
     >
         <item name="suggestionStripOption">autoCorrectBold|validTypedWordBold</item>
         <!-- android:color/holo_blue_light=#FF33B5E5 -->
+        <item name="colorValidTypedWord">@android:color/holo_blue_light</item>
         <item name="colorTypedWord">@android:color/holo_blue_light</item>
         <item name="colorAutoCorrect">@android:color/holo_blue_light</item>
         <item name="colorSuggested">@android:color/holo_blue_light</item>
+        <item name="alphaValidTypedWord">85</item>
         <item name="alphaTypedWord">85</item>
-        <item name="alphaAutoCorrect">100</item>
         <item name="alphaSuggested">70</item>
         <item name="alphaObsoleted">70</item>
         <item name="suggestionsCountInStrip">@integer/suggestions_count_in_strip</item>
diff --git a/java/src/com/android/inputmethod/compat/ArraysCompatUtils.java b/java/src/com/android/inputmethod/compat/ArraysCompatUtils.java
index f6afbcf..011473b 100644
--- a/java/src/com/android/inputmethod/compat/ArraysCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/ArraysCompatUtils.java
@@ -16,10 +16,14 @@
 
 package com.android.inputmethod.compat;
 
+import android.util.Log;
+
 import java.lang.reflect.Method;
 import java.util.Arrays;
 
 public class ArraysCompatUtils {
+    private static final String TAG = ArraysCompatUtils.class.getSimpleName();
+
     private static final Method METHOD_Arrays_binarySearch = CompatUtils
             .getMethod(Arrays.class, "binarySearch", int[].class, int.class, int.class, int.class);
 
@@ -33,8 +37,15 @@
         }
     }
 
-    /* package */ static int compatBinarySearch(int[] array, int startIndex, int endIndex,
-            int value) {
+    // TODO: Implement fast binary search
+    /* package for testing */
+    static int compatBinarySearch(int[] array, int startIndex, int endIndex, int value) {
+        // Output error log because this method has strict performance penalty.
+        // Note that this method has been called only from spell checker and spell checker exists
+        // only from IceCreamSandwich and after, so that there is no chance on pre-ICS device to
+        // invoke this method.
+        Log.e(TAG, "Invoked expensive binarySearch");
+
         if (startIndex > endIndex) throw new IllegalArgumentException();
         if (startIndex < 0 || endIndex > array.length) throw new ArrayIndexOutOfBoundsException();
 
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 4e4ccef..42d1fe1 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -73,10 +73,11 @@
     private static final int LABEL_FLAGS_WITH_ICON_RIGHT = 0x2000;
     private static final int LABEL_FLAGS_AUTO_X_SCALE = 0x4000;
 
+    // TODO: This should be public final
     /** Icon to display instead of a label. Icon takes precedence over a label */
     private Drawable mIcon;
     /** Preview version of the icon, for the preview popup */
-    private Drawable mPreviewIcon;
+    public final Drawable mPreviewIcon;
 
     /** Width of the key, not including the gap */
     public final int mWidth;
@@ -198,6 +199,7 @@
         mCode = code;
         mAltCode = Keyboard.CODE_DUMMY;
         mIcon = icon;
+        mPreviewIcon = null;
         // Horizontal gap is divided equally to both sides of the key.
         mX = x + mHorizontalGap / 2;
         mY = y;
@@ -425,18 +427,11 @@
         return mIcon;
     }
 
-    public Drawable getPreviewIcon() {
-        return mPreviewIcon;
-    }
-
+    // TODO: Get rid of this method.
     public void setIcon(Drawable icon) {
         mIcon = icon;
     }
 
-    public void setPreviewIcon(Drawable icon) {
-        mPreviewIcon = icon;
-    }
-
     /**
      * Informs the key that it has been pressed, in case it needs to change its appearance or
      * state.
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index 3b3ff07..b4172d4 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -73,15 +73,6 @@
     private final int mHashCode;
 
     public KeyboardId(int xmlId, int elementState, Locale locale, int orientation, int width,
-            int mode, EditorInfo editorInfo, boolean settingsKeyEnabled,
-            boolean clobberSettingsKey, boolean shortcutKeyEnabled, boolean hasShortcutKey) {
-        this(xmlId, elementState, locale, orientation, width, mode,
-                (editorInfo != null ? editorInfo.inputType : 0),
-                (editorInfo != null ? editorInfo.imeOptions : 0),
-                settingsKeyEnabled, clobberSettingsKey, shortcutKeyEnabled, hasShortcutKey);
-    }
-
-    private KeyboardId(int xmlId, int elementState, Locale locale, int orientation, int width,
             int mode, int inputType, int imeOptions, boolean settingsKeyEnabled,
             boolean clobberSettingsKey, boolean shortcutKeyEnabled, boolean hasShortcutKey) {
         this.mLocale = locale;
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
index a28cfa8..34296fa 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
@@ -17,11 +17,9 @@
 package com.android.inputmethod.keyboard;
 
 import android.content.Context;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
-import android.util.DisplayMetrics;
 import android.util.Xml;
 import android.view.inputmethod.EditorInfo;
 
@@ -42,9 +40,9 @@
 
 /**
  * This class has a set of {@link KeyboardId}s. Each of them represents a different keyboard
- * specific to a keyboard state, such as alphabet, symbols, and so on.  Layouts in the same
- * {@link KeyboardSet} are related to each other.
- * A {@link KeyboardSet} needs to be created for each {@link android.view.inputmethod.EditorInfo}.
+ * specific to a keyboard state, such as alphabet, symbols, and so on. Layouts in the same
+ * {@link KeyboardSet} are related to each other. A {@link KeyboardSet} needs to be created for each
+ * {@link android.view.inputmethod.EditorInfo}.
  */
 public class KeyboardSet {
     private static final String TAG_KEYBOARD_SET = "KeyboardSet";
@@ -55,56 +53,67 @@
     public final KeyboardId mSymbolsId;
     public final KeyboardId mSymbolsShiftedId;
 
-    KeyboardSet(Builder builder) {
-        mAlphabetId = builder.getKeyboardId(false, false);
-        mSymbolsId = builder.getKeyboardId(true, false);
-        mSymbolsShiftedId = builder.getKeyboardId(true, true);
+    KeyboardSet(Params params) {
+        mAlphabetId = Builder.getKeyboardId(false, false, params);
+        mSymbolsId = Builder.getKeyboardId(true, false, params);
+        mSymbolsShiftedId = Builder.getKeyboardId(true, true, params);
+    }
+
+    private static class Params {
+        int mMode;
+        int mInputTypes;
+        int mImeOptions;
+        boolean mSettingsKeyEnabled;
+        boolean mVoiceKeyEnabled;
+        boolean mVoiceKeyOnMain;
+        boolean mNoSettingsKey;
+        Locale mLocale;
+        int mOrientation;
+        int mWidth;
+        final HashMap<Integer, Integer> mElementKeyboards =
+                new HashMap<Integer, Integer>();
+
+        Params() {}
     }
 
     public static class Builder {
         private final Resources mResources;
-        private final EditorInfo mEditorInfo;
 
-        private final HashMap<Integer, Integer> mElementKeyboards =
-                new HashMap<Integer, Integer>();
-
-        private final int mMode;
-        private final boolean mSettingsKeyEnabled;
-        private final boolean mVoiceKeyEnabled;
-        private final boolean mVoiceKeyOnMain;
-        private final boolean mNoSettingsKey;
-        private final Locale mLocale;
-        private final Configuration mConf;
-        private final DisplayMetrics mMetrics;
+        private final Params mParams = new Params();
 
         public Builder(Context context, EditorInfo editorInfo, SettingsValues settingsValues) {
             mResources = context.getResources();
-            mEditorInfo = editorInfo;
             final SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance();
             final String packageName = context.getPackageName();
+            final Params params = mParams;
 
-            mMode = Utils.getKeyboardMode(mEditorInfo);
-            mSettingsKeyEnabled = settingsValues.isSettingsKeyEnabled();
+            params.mMode = Utils.getKeyboardMode(editorInfo);
+            if (editorInfo != null) {
+                params.mInputTypes = editorInfo.inputType;
+                params.mImeOptions = editorInfo.imeOptions;
+            }
+            params.mSettingsKeyEnabled = settingsValues.isSettingsKeyEnabled();
             @SuppressWarnings("deprecation")
             final boolean noMicrophone = Utils.inPrivateImeOptions(
                     packageName, LatinIME.IME_OPTION_NO_MICROPHONE, editorInfo)
                     || Utils.inPrivateImeOptions(
                             null, LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, editorInfo);
-            mVoiceKeyEnabled = settingsValues.isVoiceKeyEnabled(editorInfo) && !noMicrophone;
-            mVoiceKeyOnMain = settingsValues.isVoiceKeyOnMain();
-            mNoSettingsKey = Utils.inPrivateImeOptions(
+            params.mVoiceKeyEnabled = settingsValues.isVoiceKeyEnabled(editorInfo) && !noMicrophone;
+            params.mVoiceKeyOnMain = settingsValues.isVoiceKeyOnMain();
+            params.mNoSettingsKey = Utils.inPrivateImeOptions(
                     packageName, LatinIME.IME_OPTION_NO_SETTINGS_KEY, editorInfo);
             final boolean forceAscii = Utils.inPrivateImeOptions(
                     packageName, LatinIME.IME_OPTION_FORCE_ASCII, editorInfo);
             final boolean asciiCapable = subtypeSwitcher.currentSubtypeContainsExtraValueKey(
                     LatinIME.SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE);
-            mLocale = (forceAscii && !asciiCapable) ? Locale.US : subtypeSwitcher.getInputLocale();
-            mConf = mResources.getConfiguration();
-            mMetrics = mResources.getDisplayMetrics();
+            params.mLocale = (forceAscii && !asciiCapable)
+                    ? Locale.US : subtypeSwitcher.getInputLocale();
+            params.mOrientation = mResources.getConfiguration().orientation;
+            params.mWidth = mResources.getDisplayMetrics().widthPixels;
         }
 
         public KeyboardSet build() {
-            final Locale savedLocale = LocaleUtils.setSystemLocale(mResources, mLocale);
+            final Locale savedLocale = LocaleUtils.setSystemLocale(mResources, mParams.mLocale);
             try {
                 parseKeyboardSet(mResources, R.xml.keyboard_set);
             } catch (Exception e) {
@@ -112,16 +121,18 @@
             } finally {
                 LocaleUtils.setSystemLocale(mResources, savedLocale);
             }
-            return new KeyboardSet(this);
+            return new KeyboardSet(mParams);
         }
 
-        KeyboardId getKeyboardId(boolean isSymbols, boolean isShift) {
-            final int elementState = getElementState(mMode, isSymbols, isShift);
-            final int xmlId = mElementKeyboards.get(elementState);
-            final boolean hasShortcutKey = mVoiceKeyEnabled && (isSymbols != mVoiceKeyOnMain);
-            return new KeyboardId(xmlId, elementState, mLocale, mConf.orientation,
-                    mMetrics.widthPixels, mMode, mEditorInfo, mSettingsKeyEnabled, mNoSettingsKey,
-                    mVoiceKeyEnabled, hasShortcutKey);
+        static KeyboardId getKeyboardId(boolean isSymbols, boolean isShift, Params params) {
+            final int elementState = getElementState(params.mMode, isSymbols, isShift);
+            final int xmlId = params.mElementKeyboards.get(elementState);
+            final boolean hasShortcutKey = params.mVoiceKeyEnabled
+                    && (isSymbols != params.mVoiceKeyOnMain);
+            return new KeyboardId(xmlId, elementState, params.mLocale, params.mOrientation,
+                    params.mWidth, params.mMode, params.mInputTypes, params.mImeOptions,
+                    params.mSettingsKeyEnabled, params.mNoSettingsKey, params.mVoiceKeyEnabled,
+                    hasShortcutKey);
         }
 
         private static int getElementState(int mode, boolean isSymbols, boolean isShift) {
@@ -198,7 +209,7 @@
                         R.styleable.KeyboardSet_Element_elementName, 0);
                 final int elementKeyboard = a.getResourceId(
                         R.styleable.KeyboardSet_Element_elementKeyboard, 0);
-                mElementKeyboards.put(elementName, elementKeyboard);
+                mParams.mElementKeyboards.put(elementName, elementKeyboard);
             } finally {
                 a.recycle();
             }
@@ -208,7 +219,8 @@
     public static String parseKeyboardLocale(Resources res, int resId)
             throws XmlPullParserException, IOException {
         final XmlPullParser parser = res.getXml(resId);
-        if (parser == null) return "";
+        if (parser == null)
+            return "";
         int event;
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
             if (event == XmlPullParser.START_TAG) {
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index d2741ed..ebc0d82 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -889,7 +889,7 @@
             }
             previewText.setText(mKeyboard.adjustLabelCase(key.mLabel));
         } else {
-            final Drawable previewIcon = key.getPreviewIcon();
+            final Drawable previewIcon = key.mPreviewIcon;
             previewText.setCompoundDrawables(null, null, null,
                    previewIcon != null ? previewIcon : key.getIcon());
             previewText.setText(null);
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
index 778aac3..1a636dc 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -104,6 +104,20 @@
         final int[] keyWidths = new int[keyCount];
         final int[] keyHeights = new int[keyCount];
         final int[] keyCharCodes = new int[keyCount];
+        final float[] sweetSpotCenterXs;
+        final float[] sweetSpotCenterYs;
+        final float[] sweetSpotRadii;
+        final boolean calculateSweetSpotParams;
+        if (touchPositionCorrection != null && touchPositionCorrection.isValid()) {
+            sweetSpotCenterXs = new float[keyCount];
+            sweetSpotCenterYs = new float[keyCount];
+            sweetSpotRadii = new float[keyCount];
+            calculateSweetSpotParams = true;
+        } else {
+            sweetSpotCenterXs = sweetSpotCenterYs = sweetSpotRadii = null;
+            calculateSweetSpotParams = false;
+        }
+
         for (int i = 0; i < keyCount; ++i) {
             final Key key = keys.get(i);
             keyXCoordinates[i] = key.mX;
@@ -111,18 +125,23 @@
             keyWidths[i] = key.mWidth;
             keyHeights[i] = key.mHeight;
             keyCharCodes[i] = key.mCode;
-        }
-
-        float[] sweetSpotCenterXs = null;
-        float[] sweetSpotCenterYs = null;
-        float[] sweetSpotRadii = null;
-
-        if (touchPositionCorrection != null && touchPositionCorrection.isValid()) {
-            sweetSpotCenterXs = new float[keyCount];
-            sweetSpotCenterYs = new float[keyCount];
-            sweetSpotRadii = new float[keyCount];
-            calculateSweetSpot(keys, touchPositionCorrection,
-                    sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
+            if (calculateSweetSpotParams) {
+                final Rect hitBox = key.mHitBox;
+                final int row = hitBox.top / mKeyHeight;
+                if (row < touchPositionCorrection.mRadii.length) {
+                    final float hitBoxCenterX = (hitBox.left + hitBox.right) * 0.5f;
+                    final float hitBoxCenterY = (hitBox.top + hitBox.bottom) * 0.5f;
+                    final float hitBoxWidth = hitBox.right - hitBox.left;
+                    final float hitBoxHeight = hitBox.bottom - hitBox.top;
+                    final float x = touchPositionCorrection.mXs[row];
+                    final float y = touchPositionCorrection.mYs[row];
+                    final float radius = touchPositionCorrection.mRadii[row];
+                    sweetSpotCenterXs[i] = hitBoxCenterX + x * hitBoxWidth;
+                    sweetSpotCenterYs[i] = hitBoxCenterY + y * hitBoxHeight;
+                    sweetSpotRadii[i] = radius * (float)Math.sqrt(
+                            hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight);
+                }
+            }
         }
 
         mNativeProximityInfo = setProximityInfoNative(MAX_PROXIMITY_CHARS_SIZE,
@@ -131,32 +150,6 @@
                 sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
     }
 
-    private void calculateSweetSpot(List<Key> keys, TouchPositionCorrection touchPositionCorrection,
-            float[] sweetSpotCenterXs, float[] sweetSpotCenterYs, float[] sweetSpotRadii) {
-        final int keyCount = keys.size();
-        final float[] xs = touchPositionCorrection.mXs;
-        final float[] ys = touchPositionCorrection.mYs;
-        final float[] radii = touchPositionCorrection.mRadii;
-        for (int i = 0; i < keyCount; ++i) {
-            final Key key = keys.get(i);
-            final Rect hitBox = key.mHitBox;
-            final int row = hitBox.top / mKeyHeight;
-            if (row < radii.length) {
-                final float hitBoxCenterX = (hitBox.left + hitBox.right) * 0.5f;
-                final float hitBoxCenterY = (hitBox.top + hitBox.bottom) * 0.5f;
-                final float hitBoxWidth = hitBox.right - hitBox.left;
-                final float hitBoxHeight = hitBox.bottom - hitBox.top;
-                final float x = xs[row];
-                final float y = ys[row];
-                final float radius = radii[row];
-                sweetSpotCenterXs[i] = hitBoxCenterX + x * hitBoxWidth;
-                sweetSpotCenterYs[i] = hitBoxCenterY + y * hitBoxHeight;
-                sweetSpotRadii[i] = radius
-                        * (float)Math.sqrt(hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight);
-            }
-        }
-    }
-
     public long getNativeProximityInfo() {
         return mNativeProximityInfo;
     }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 20c87ad..32eabdb 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -67,6 +67,7 @@
 import com.android.inputmethod.keyboard.KeyboardView;
 import com.android.inputmethod.keyboard.LatinKeyboard;
 import com.android.inputmethod.keyboard.LatinKeyboardView;
+import com.android.inputmethod.latin.suggestions.SuggestionsView;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -1148,6 +1149,7 @@
         if (!mHasUncommittedTypedChars) return;
         mHasUncommittedTypedChars = false;
         final CharSequence typedWord = mWordComposer.getTypedWord();
+        mWordComposer.onCommitWord();
         if (typedWord.length() > 0) {
             if (ic != null) {
                 ic.commitText(typedWord, 1);
@@ -2032,6 +2034,7 @@
             }
         }
         mHasUncommittedTypedChars = false;
+        mWordComposer.onCommitWord();
     }
 
     private static final WordComposer sEmptyWordComposer = new WordComposer();
@@ -2201,10 +2204,11 @@
             }
         }
         ic.deleteSurroundingText(cancelLength + 1, 0);
-
-        // Re-insert the separator
+        mWordComposer.resumeSuggestionOnKeptWord();
         ic.commitText(mWordComposer.getTypedWord(), 1);
+        // Re-insert the separator
         ic.commitText(separator, 1);
+        mWordComposer.onCommitWord();
         Utils.Stats.onSeparator(separator.charAt(0), WordComposer.NOT_A_COORDINATE,
                 WordComposer.NOT_A_COORDINATE);
         mHandler.cancelUpdateBigramPredictions();
@@ -2233,6 +2237,7 @@
         // restartSuggestionsOnWordBeforeCursorIfAtEndOfWord instead, but retrieving
         // the old WordComposer allows to reuse the actual typed coordinates.
         mHasUncommittedTypedChars = true;
+        mWordComposer.resumeSuggestionOnKeptWord();
         ic.setComposingText(mWordComposer.getTypedWord(), 1);
         mHandler.cancelUpdateBigramPredictions();
         mHandler.postUpdateSuggestions();
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 60a9685..c0204c2 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -61,11 +61,10 @@
         }
     }
 
-    // The currently typing word.
-    // NOTE: this is not reset as soon as the word is committed because it may be needed again
-    // to resume suggestion if backspaced. TODO: separate cleanly what is actually being
-    // composed and what is kept for possible resuming.
+    // The currently typing word. May not be null.
     private CharacterStore mCurrentWord;
+    // The information being kept for resuming suggestion. May be null if wiped.
+    private CharacterStore mWordKeptForSuggestionResuming;
     // An auto-correction for this word out of the dictionary.
     private CharSequence mAutoCorrection;
 
@@ -82,6 +81,7 @@
 
     public WordComposer() {
         mCurrentWord = new CharacterStore();
+        mWordKeptForSuggestionResuming = null;
         mTrailingSingleQuotesCount = 0;
         mAutoCorrection = null;
     }
@@ -92,6 +92,7 @@
 
     public void init(WordComposer source) {
         mCurrentWord = new CharacterStore(source.mCurrentWord);
+        mWordKeptForSuggestionResuming = source.mWordKeptForSuggestionResuming;
         mCapsCount = source.mCapsCount;
         mIsFirstCharCapitalized = source.mIsFirstCharCapitalized;
         mAutoCapitalized = source.mAutoCapitalized;
@@ -104,6 +105,7 @@
      */
     public void reset() {
         mCurrentWord.reset();
+        mWordKeptForSuggestionResuming = null;
         mCapsCount = 0;
         mIsFirstCharCapitalized = false;
         mTrailingSingleQuotesCount = 0;
@@ -323,4 +325,21 @@
     public CharSequence getAutoCorrectionOrNull() {
         return mAutoCorrection;
     }
+
+    // TODO: pass the information about what was committed and how. Was it an auto-correction?
+    // Was it a completion? Was is what the user typed?
+    public void onCommitWord() {
+        mWordKeptForSuggestionResuming = mCurrentWord;
+        // TODO: improve performance by swapping buffers instead of creating a new object.
+        mCurrentWord = new CharacterStore();
+    }
+
+    public boolean hasWordKeptForSuggestionResuming() {
+        return null != mWordKeptForSuggestionResuming;
+    }
+
+    public void resumeSuggestionOnKeptWord() {
+        mCurrentWord = mWordKeptForSuggestionResuming;
+        mWordKeptForSuggestionResuming = null;
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
similarity index 97%
rename from java/src/com/android/inputmethod/latin/MoreSuggestions.java
rename to java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
index 86072b6..7f59189 100644
--- a/java/src/com/android/inputmethod/latin/MoreSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.latin.suggestions;
 
 import android.content.res.Resources;
 import android.graphics.Paint;
@@ -27,11 +27,12 @@
 import com.android.inputmethod.keyboard.KeyboardView;
 import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
 import com.android.inputmethod.keyboard.internal.KeyboardParams;
+import com.android.inputmethod.latin.LatinImeLogger;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.SuggestedWords;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 
 public class MoreSuggestions extends Keyboard {
-    private static final boolean DBG = LatinImeLogger.sDBG;
-
     public static final int SUGGESTION_CODE_BASE = 1024;
 
     private MoreSuggestions(Builder.MoreSuggestionsParam params) {
@@ -39,6 +40,8 @@
     }
 
     public static class Builder extends KeyboardBuilder<Builder.MoreSuggestionsParam> {
+        private static final boolean DBG = LatinImeLogger.sDBG;
+
         private final MoreSuggestionsView mPaneView;
         private SuggestedWords mSuggestions;
         private int mFromPos;
diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
similarity index 98%
rename from java/src/com/android/inputmethod/latin/MoreSuggestionsView.java
rename to java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
index c61dd63..b5f67ac 100644
--- a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.latin.suggestions;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -34,6 +34,7 @@
 import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
 import com.android.inputmethod.keyboard.PointerTracker.KeyEventHandler;
 import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
+import com.android.inputmethod.latin.R;
 
 /**
  * A view that renders a virtual {@link MoreSuggestions}. It handles rendering of keys and detecting
diff --git a/java/src/com/android/inputmethod/latin/SuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java
similarity index 97%
rename from java/src/com/android/inputmethod/latin/SuggestionsView.java
rename to java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java
index 47c7900..40d7826 100644
--- a/java/src/com/android/inputmethod/latin/SuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.latin.suggestions;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -57,7 +57,12 @@
 import com.android.inputmethod.keyboard.KeyboardView;
 import com.android.inputmethod.keyboard.MoreKeysPanel;
 import com.android.inputmethod.keyboard.PointerTracker;
+import com.android.inputmethod.latin.LatinImeLogger;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
+import com.android.inputmethod.latin.SuggestedWords;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.Utils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -72,7 +77,7 @@
     // The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}.
     public static final int MAX_SUGGESTIONS = 18;
 
-    private static final boolean DBG = LatinImeLogger.sDBG;
+    static final boolean DBG = LatinImeLogger.sDBG;
 
     private final ViewGroup mSuggestionsStrip;
     private KeyboardView mKeyboardView;
@@ -141,6 +146,7 @@
         private final List<View> mDividers;
         private final List<TextView> mInfos;
 
+        private final int mColorValidTypedWord;
         private final int mColorTypedWord;
         private final int mColorAutoCorrect;
         private final int mColorSuggested;
@@ -184,6 +190,8 @@
             final TypedArray a = context.obtainStyledAttributes(
                     attrs, R.styleable.SuggestionsView, defStyle, R.style.SuggestionsViewStyle);
             mSuggestionStripOption = a.getInt(R.styleable.SuggestionsView_suggestionStripOption, 0);
+            final float alphaValidTypedWord = getPercent(a,
+                    R.styleable.SuggestionsView_alphaValidTypedWord, 100);
             final float alphaTypedWord = getPercent(a,
                     R.styleable.SuggestionsView_alphaTypedWord, 100);
             final float alphaAutoCorrect = getPercent(a,
@@ -191,6 +199,9 @@
             final float alphaSuggested = getPercent(a,
                     R.styleable.SuggestionsView_alphaSuggested, 100);
             mAlphaObsoleted = getPercent(a, R.styleable.SuggestionsView_alphaSuggested, 100);
+            mColorValidTypedWord = applyAlpha(
+                    a.getColor(R.styleable.SuggestionsView_colorValidTypedWord, 0),
+                    alphaValidTypedWord);
             mColorTypedWord = applyAlpha(
                     a.getColor(R.styleable.SuggestionsView_colorTypedWord, 0), alphaTypedWord);
             mColorAutoCorrect = applyAlpha(
@@ -288,6 +299,8 @@
             final int color;
             if (index == mCenterSuggestionIndex && Utils.willAutoCorrect(suggestions)) {
                 color = mColorAutoCorrect;
+            } else if (index == mCenterSuggestionIndex && suggestions.mTypedWordValid) {
+                color = mColorValidTypedWord;
             } else if (isSuggested) {
                 color = mColorSuggested;
             } else {
@@ -423,7 +436,7 @@
 
                 final TextView word = mWords.get(index);
                 word.setEnabled(true);
-                word.setTextColor(mColorTypedWord);
+                word.setTextColor(mColorAutoCorrect);
                 final CharSequence text = suggestions.getWord(index);
                 word.setText(text);
                 word.setTextScaleX(1.0f);
diff --git a/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java b/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java
index 7b4c6a9..bfb0301 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java
@@ -19,6 +19,7 @@
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Configuration;
 import android.test.AndroidTestCase;
+import android.text.InputType;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
 
@@ -51,7 +52,7 @@
         }
         return new KeyboardId(com.android.inputmethod.latin.R.xml.kbd_qwerty,
                 KeyboardId.ELEMENT_ALPHABET, locale, orientation, width, KeyboardId.MODE_TEXT,
-                null, false, false, false, false);
+                InputType.TYPE_CLASS_TEXT, 0, false, false, false, false);
     }
 
     protected InputStream openTestRawResource(int resIdInTest) {