Merge "[HD04] Make the locale mandatory."
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 71e3eee..f3cfae2 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -484,11 +484,9 @@
             return;
         }
 
-        final TextView previewTextView = mKeyPreviewChoreographer.getKeyPreviewTextView(
-                key, getContext());
         locatePreviewPlacerView();
-        mDrawingPreviewPlacerView.addView(
-                previewTextView, ViewLayoutUtils.newLayoutParam(mDrawingPreviewPlacerView, 0, 0));
+        final TextView previewTextView = mKeyPreviewChoreographer.getKeyPreviewTextView(
+                key, mDrawingPreviewPlacerView);
         getLocationInWindow(mOriginCoords);
         mKeyPreviewChoreographer.placeKeyPreview(key, previewTextView, keyboard.mIconsSet,
                 mKeyDrawParams, getWidth(), mOriginCoords);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java
index c1922b7..df869b2 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java
@@ -58,7 +58,7 @@
         mParams = params;
     }
 
-    public TextView getKeyPreviewTextView(final Key key, final Context context) {
+    public TextView getKeyPreviewTextView(final Key key, final ViewGroup placerView) {
         TextView previewTextView = mShowingKeyPreviewTextViews.remove(key);
         if (previewTextView != null) {
             return previewTextView;
@@ -67,10 +67,15 @@
         if (previewTextView != null) {
             return previewTextView;
         }
+        final Context context = placerView.getContext();
         if (mParams.mLayoutId != 0) {
-            return (TextView)LayoutInflater.from(context).inflate(mParams.mLayoutId, null);
+            previewTextView = (TextView)LayoutInflater.from(context)
+                    .inflate(mParams.mLayoutId, null);
+        } else {
+            previewTextView = new TextView(context);
         }
-        return new TextView(context);
+        placerView.addView(previewTextView, ViewLayoutUtils.newLayoutParam(placerView, 0, 0));
+        return previewTextView;
     }
 
     public boolean isShowingKeyPreview(final Key key) {
@@ -244,7 +249,7 @@
         zoomOutAnimation.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(final Animator animation) {
-                dismissKeyPreview(key, true /* withAnimation */);
+                dismissKeyPreview(key, false /* withAnimation */);
             }
         });
         return zoomOutAnimation;
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 2e163c4..e55c08d 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -67,7 +67,6 @@
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.define.ProductionFlag;
 import com.android.inputmethod.latin.inputlogic.InputLogic;
-import com.android.inputmethod.latin.inputlogic.SpaceState;
 import com.android.inputmethod.latin.personalization.DictionaryDecayBroadcastReciever;
 import com.android.inputmethod.latin.personalization.PersonalizationDictionarySessionRegistrar;
 import com.android.inputmethod.latin.personalization.PersonalizationHelper;
@@ -75,6 +74,7 @@
 import com.android.inputmethod.latin.settings.SettingsActivity;
 import com.android.inputmethod.latin.settings.SettingsValues;
 import com.android.inputmethod.latin.suggestions.SuggestionStripView;
+import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor;
 import com.android.inputmethod.latin.utils.ApplicationUtils;
 import com.android.inputmethod.latin.utils.CapsModeUtils;
 import com.android.inputmethod.latin.utils.CompletionInfoUtils;
@@ -82,7 +82,6 @@
 import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
 import com.android.inputmethod.latin.utils.IntentUtils;
 import com.android.inputmethod.latin.utils.JniUtils;
-import com.android.inputmethod.latin.utils.LatinImeLoggerUtils;
 import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
 import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
 import com.android.inputmethod.research.ResearchLogger;
@@ -97,7 +96,7 @@
  * Input method implementation for Qwerty'ish keyboard.
  */
 public class LatinIME extends InputMethodService implements KeyboardActionListener,
-        SuggestionStripView.Listener,
+        SuggestionStripView.Listener, SuggestionStripViewAccessor,
         DictionaryFacilitatorForSuggest.DictionaryInitializationListener {
     private static final String TAG = LatinIME.class.getSimpleName();
     private static final boolean TRACE = false;
@@ -116,13 +115,15 @@
     private static final String SCHEME_PACKAGE = "package";
 
     private final Settings mSettings;
-    private final InputLogic mInputLogic = new InputLogic(this);
+    private final InputLogic mInputLogic = new InputLogic(this /* LatinIME */,
+            this /* SuggestionStripViewAccessor */);
 
     private View mExtractArea;
     private View mKeyPreviewBackingView;
     private SuggestionStripView mSuggestionStripView;
 
-    private CompletionInfo[] mApplicationSpecifiedCompletions;
+    // TODO[IL]: remove this member completely.
+    public CompletionInfo[] mApplicationSpecifiedCompletions;
 
     private RichInputMethodManager mRichImm;
     @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher;
@@ -1305,13 +1306,10 @@
         // Nothing to do so far.
     }
 
-    // TODO[IL]: Move this to InputLogic and make it private
-    // Outside LatinIME, only used by the test suite.
+    // TODO: remove this, read this directly from mInputLogic or something in the tests
     @UsedForTesting
     public boolean isShowingPunctuationList() {
-        if (mInputLogic.mSuggestedWords == null) return false;
-        return mSettings.getCurrent().mSpacingAndPunctuations.mSuggestPuncList
-                == mInputLogic.mSuggestedWords;
+        return mInputLogic.isShowingPunctuationList(mSettings.getCurrent());
     }
 
     // TODO[IL]: Define a clear interface for this
@@ -1330,6 +1328,17 @@
         return currentSettings.isSuggestionsRequested();
     }
 
+    @Override
+    public boolean hasSuggestionStripView() {
+        return null != mSuggestionStripView;
+    }
+
+    @Override
+    public boolean isShowingAddToDictionaryHint() {
+        return hasSuggestionStripView() && mSuggestionStripView.isShowingAddToDictionaryHint();
+    }
+
+    @Override
     public void dismissAddToDictionaryHint() {
         if (null != mSuggestionStripView) {
             mSuggestionStripView.dismissAddToDictionaryHint();
@@ -1399,8 +1408,7 @@
         // the "add to dictionary" hint, we need to revert to suggestions - although it is unclear
         // how we can come here if it's displayed.
         if (suggestedWords.size() > 1 || typedWord.length() <= 1
-                || null == mSuggestionStripView
-                || mSuggestionStripView.isShowingAddToDictionaryHint()) {
+                || null == mSuggestionStripView || isShowingAddToDictionaryHint()) {
             return suggestedWords;
         } else {
             final SuggestedWords punctuationList =
@@ -1418,7 +1426,7 @@
         }
     }
 
-    // TODO[IL]: Define a clean interface for this
+    @Override
     public void showSuggestionStrip(final SuggestedWords sourceSuggestedWords) {
         final SuggestedWords suggestedWords =
                 sourceSuggestedWords.isEmpty() ? SuggestedWords.EMPTY : sourceSuggestedWords;
@@ -1446,99 +1454,20 @@
     // interface
     @Override
     public void pickSuggestionManually(final int index, final SuggestedWordInfo suggestionInfo) {
-        final SuggestedWords suggestedWords = mInputLogic.mSuggestedWords;
-        final String suggestion = suggestionInfo.mWord;
-        // If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
-        if (suggestion.length() == 1 && isShowingPunctuationList()) {
-            // Word separators are suggested before the user inputs something.
-            // So, LatinImeLogger logs "" as a user's input.
-            LatinImeLogger.logOnManualSuggestion("", suggestion, index, suggestedWords);
-            // Rely on onCodeInput to do the complicated swapping/stripping logic consistently.
-            final int primaryCode = suggestion.charAt(0);
-            onCodeInput(primaryCode,
-                    Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE);
-            if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
-                ResearchLogger.latinIME_punctuationSuggestion(index, suggestion,
-                        false /* isBatchMode */, suggestedWords.mIsPrediction);
-            }
-            return;
-        }
+        mInputLogic.onPickSuggestionManually(mSettings.getCurrent(), index, suggestionInfo,
+                mHandler, mKeyboardSwitcher);
+    }
 
-        mInputLogic.mConnection.beginBatchEdit();
-        final SettingsValues currentSettings = mSettings.getCurrent();
-        if (SpaceState.PHANTOM == mInputLogic.mSpaceState && suggestion.length() > 0
-                // In the batch input mode, a manually picked suggested word should just replace
-                // the current batch input text and there is no need for a phantom space.
-                && !mInputLogic.mWordComposer.isBatchMode()) {
-            final int firstChar = Character.codePointAt(suggestion, 0);
-            if (!currentSettings.isWordSeparator(firstChar)
-                    || currentSettings.isUsuallyPrecededBySpace(firstChar)) {
-                mInputLogic.promotePhantomSpace(currentSettings);
-            }
-        }
-
-        if (currentSettings.isApplicationSpecifiedCompletionsOn()
-                && mApplicationSpecifiedCompletions != null
-                && index >= 0 && index < mApplicationSpecifiedCompletions.length) {
-            mInputLogic.mSuggestedWords = SuggestedWords.EMPTY;
-            if (mSuggestionStripView != null) {
-                mSuggestionStripView.clear();
-            }
-            mKeyboardSwitcher.updateShiftState();
-            mInputLogic.resetComposingState(true /* alsoResetLastComposedWord */);
-            final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index];
-            mInputLogic.mConnection.commitCompletion(completionInfo);
-            mInputLogic.mConnection.endBatchEdit();
-            return;
-        }
-
-        // We need to log before we commit, because the word composer will store away the user
-        // typed word.
-        final String replacedWord = mInputLogic.mWordComposer.getTypedWord();
-        LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion, index, suggestedWords);
-        mInputLogic.commitChosenWord(currentSettings, suggestion,
-                LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR);
-        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
-            ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion,
-                    mInputLogic.mWordComposer.isBatchMode(), suggestionInfo.mScore,
-                    suggestionInfo.mKind, suggestionInfo.mSourceDict.mDictType);
-        }
-        mInputLogic.mConnection.endBatchEdit();
-        // Don't allow cancellation of manual pick
-        mInputLogic.mLastComposedWord.deactivate();
-        // Space state must be updated before calling updateShiftState
-        mInputLogic.mSpaceState = SpaceState.PHANTOM;
-        mKeyboardSwitcher.updateShiftState();
-
-        // We should show the "Touch again to save" hint if the user pressed the first entry
-        // AND it's in none of our current dictionaries (main, user or otherwise).
-        // Please note that if mSuggest is null, it means that everything is off: suggestion
-        // and correction, so we shouldn't try to show the hint
-        final Suggest suggest = mInputLogic.mSuggest;
-        final boolean showingAddToDictionaryHint =
-                (SuggestedWordInfo.KIND_TYPED == suggestionInfo.mKind
-                        || SuggestedWordInfo.KIND_OOV_CORRECTION == suggestionInfo.mKind)
-                        && suggest != null
-                        // If the suggestion is not in the dictionary, the hint should be shown.
-                        && !suggest.mDictionaryFacilitator.isValidWord(suggestion,
-                                true /* ignoreCase */);
-
-        if (currentSettings.mIsInternal) {
-            LatinImeLoggerUtils.onSeparator((char)Constants.CODE_SPACE,
-                    Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
-        }
-        if (showingAddToDictionaryHint
-                && suggest.mDictionaryFacilitator.isUserDictionaryEnabled()) {
-            mSuggestionStripView.showAddToDictionaryHint(suggestion);
-        } else {
-            // If we're not showing the "Touch again to save", then update the suggestion strip.
-            mHandler.postUpdateSuggestionStrip();
-        }
+    @Override
+    public void showAddToDictionaryHint(final String word) {
+        if (null == mSuggestionStripView) return;
+        mSuggestionStripView.showAddToDictionaryHint(word);
     }
 
     // TODO[IL]: Define a clean interface for this
     // This will show either an empty suggestion strip (if prediction is enabled) or
     // punctuation suggestions (if it's disabled).
+    @Override
     public void setNeutralSuggestionStrip() {
         final SettingsValues currentSettings = mSettings.getCurrent();
         if (currentSettings.mBigramPredictionEnabled) {
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index a994a43..edca998 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -23,6 +23,7 @@
 import android.util.Log;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
+import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
 import android.view.inputmethod.EditorInfo;
 
@@ -44,6 +45,7 @@
 import com.android.inputmethod.latin.define.ProductionFlag;
 import com.android.inputmethod.latin.settings.SettingsValues;
 import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
+import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor;
 import com.android.inputmethod.latin.utils.AsyncResultHolder;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 import com.android.inputmethod.latin.utils.InputTypeUtils;
@@ -65,6 +67,7 @@
 
     // TODO : Remove this member when we can.
     private final LatinIME mLatinIME;
+    private final SuggestionStripViewAccessor mSuggestionStripViewAccessor;
 
     // Never null.
     private InputLogicHandler mInputLogicHandler = InputLogicHandler.NULL_HANDLER;
@@ -94,8 +97,10 @@
     // Find a way to remove it for readability.
     public boolean mIsAutoCorrectionIndicatorOn;
 
-    public InputLogic(final LatinIME latinIME) {
+    public InputLogic(final LatinIME latinIME,
+            final SuggestionStripViewAccessor suggestionStripViewAccessor) {
         mLatinIME = latinIME;
+        mSuggestionStripViewAccessor = suggestionStripViewAccessor;
         mWordComposer = new WordComposer();
         mEventInterpreter = new EventInterpreter(latinIME);
         mConnection = new RichInputConnection(latinIME);
@@ -179,6 +184,108 @@
     }
 
     /**
+     * A suggestion was picked from the suggestion strip.
+     * @param settingsValues the current values of the settings.
+     * @param index the index of the suggestion.
+     * @param suggestionInfo the suggestion info.
+     */
+    // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener}
+    // interface
+    public void onPickSuggestionManually(final SettingsValues settingsValues,
+            final int index, final SuggestedWordInfo suggestionInfo,
+            // TODO: remove these two arguments
+            final LatinIME.UIHandler handler, final KeyboardSwitcher keyboardSwitcher) {
+        final SuggestedWords suggestedWords = mSuggestedWords;
+        final String suggestion = suggestionInfo.mWord;
+        // If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
+        if (suggestion.length() == 1 && isShowingPunctuationList(settingsValues)) {
+            // Word separators are suggested before the user inputs something.
+            // So, LatinImeLogger logs "" as a user's input.
+            LatinImeLogger.logOnManualSuggestion("", suggestion, index, suggestedWords);
+            // Rely on onCodeInput to do the complicated swapping/stripping logic consistently.
+            final int primaryCode = suggestion.charAt(0);
+            onCodeInput(primaryCode,
+                    Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE,
+                    settingsValues, handler, keyboardSwitcher);
+            if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
+                ResearchLogger.latinIME_punctuationSuggestion(index, suggestion,
+                        false /* isBatchMode */, suggestedWords.mIsPrediction);
+            }
+            return;
+        }
+
+        mConnection.beginBatchEdit();
+        if (SpaceState.PHANTOM == mSpaceState && suggestion.length() > 0
+                // In the batch input mode, a manually picked suggested word should just replace
+                // the current batch input text and there is no need for a phantom space.
+                && !mWordComposer.isBatchMode()) {
+            final int firstChar = Character.codePointAt(suggestion, 0);
+            if (!settingsValues.isWordSeparator(firstChar)
+                    || settingsValues.isUsuallyPrecededBySpace(firstChar)) {
+                promotePhantomSpace(settingsValues);
+            }
+        }
+
+        // TODO: stop relying on mApplicationSpecifiedCompletions. The SuggestionInfo object
+        // should contain a reference to the CompletionInfo instead.
+        if (settingsValues.isApplicationSpecifiedCompletionsOn()
+                && mLatinIME.mApplicationSpecifiedCompletions != null
+                && index >= 0 && index < mLatinIME.mApplicationSpecifiedCompletions.length) {
+            mSuggestedWords = SuggestedWords.EMPTY;
+            mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
+            keyboardSwitcher.updateShiftState();
+            resetComposingState(true /* alsoResetLastComposedWord */);
+            final CompletionInfo completionInfo = mLatinIME.mApplicationSpecifiedCompletions[index];
+            mConnection.commitCompletion(completionInfo);
+            mConnection.endBatchEdit();
+            return;
+        }
+
+        // We need to log before we commit, because the word composer will store away the user
+        // typed word.
+        final String replacedWord = mWordComposer.getTypedWord();
+        LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion, index, suggestedWords);
+        commitChosenWord(settingsValues, suggestion,
+                LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR);
+        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
+            ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion,
+                    mWordComposer.isBatchMode(), suggestionInfo.mScore,
+                    suggestionInfo.mKind, suggestionInfo.mSourceDict.mDictType);
+        }
+        mConnection.endBatchEdit();
+        // Don't allow cancellation of manual pick
+        mLastComposedWord.deactivate();
+        // Space state must be updated before calling updateShiftState
+        mSpaceState = SpaceState.PHANTOM;
+        keyboardSwitcher.updateShiftState();
+
+        // We should show the "Touch again to save" hint if the user pressed the first entry
+        // AND it's in none of our current dictionaries (main, user or otherwise).
+        // Please note that if mSuggest is null, it means that everything is off: suggestion
+        // and correction, so we shouldn't try to show the hint
+        final Suggest suggest = mSuggest;
+        final boolean showingAddToDictionaryHint =
+                (SuggestedWordInfo.KIND_TYPED == suggestionInfo.mKind
+                        || SuggestedWordInfo.KIND_OOV_CORRECTION == suggestionInfo.mKind)
+                        && suggest != null
+                        // If the suggestion is not in the dictionary, the hint should be shown.
+                        && !suggest.mDictionaryFacilitator.isValidWord(suggestion,
+                                true /* ignoreCase */);
+
+        if (settingsValues.mIsInternal) {
+            LatinImeLoggerUtils.onSeparator((char)Constants.CODE_SPACE,
+                    Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
+        }
+        if (showingAddToDictionaryHint
+                && suggest.mDictionaryFacilitator.isUserDictionaryEnabled()) {
+            mSuggestionStripViewAccessor.showAddToDictionaryHint(suggestion);
+        } else {
+            // If we're not showing the "Touch again to save", then update the suggestion strip.
+            handler.postUpdateSuggestionStrip();
+        }
+    }
+
+    /**
      * Consider an update to the cursor position. Evaluate whether this update has happened as
      * part of normal typing or whether it was an explicit cursor move by the user. In any case,
      * do the necessary adjustments.
@@ -638,7 +745,7 @@
                 mSpaceState = SpaceState.WEAK;
             }
             // In case the "add to dictionary" hint was still displayed.
-            mLatinIME.dismissAddToDictionaryHint();
+            mSuggestionStripViewAccessor.dismissAddToDictionaryHint();
         }
         handler.postUpdateSuggestionStrip();
         if (settingsValues.mIsInternal) {
@@ -714,7 +821,7 @@
                 if (maybeDoubleSpacePeriod(settingsValues, handler)) {
                     keyboardSwitcher.updateShiftState();
                     mSpaceState = SpaceState.DOUBLE;
-                } else if (!mLatinIME.isShowingPunctuationList()) {
+                } else if (!isShowingPunctuationList(settingsValues)) {
                     mSpaceState = SpaceState.WEAK;
                 }
             }
@@ -745,7 +852,7 @@
 
             // Set punctuation right away. onUpdateSelection will fire but tests whether it is
             // already displayed or not, so it's okay.
-            mLatinIME.setNeutralSuggestionStrip();
+            mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
         }
 
         keyboardSwitcher.updateShiftState();
@@ -1098,7 +1205,7 @@
         }
 
         if (!mWordComposer.isComposingWord() && !settingsValues.mBigramPredictionEnabled) {
-            mLatinIME.setNeutralSuggestionStrip();
+            mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
             return;
         }
 
@@ -1120,7 +1227,7 @@
         final SuggestedWords suggestedWords = holder.get(null,
                 Constants.GET_SUGGESTED_WORDS_TIMEOUT);
         if (suggestedWords != null) {
-            mLatinIME.showSuggestionStrip(suggestedWords);
+            mSuggestionStripViewAccessor.showSuggestionStrip(suggestedWords);
         }
     }
 
@@ -1326,6 +1433,15 @@
     }
 
     /**
+     * Find out if the punctuation list is shown in the suggestion strip.
+     * @return whether the current suggestions are the punctuation list.
+     */
+    // TODO: make this private. It's used through LatinIME for tests.
+    public boolean isShowingPunctuationList(final SettingsValues settingsValues) {
+        return settingsValues.mSpacingAndPunctuations.mSuggestPuncList == mSuggestedWords;
+    }
+
+    /**
      * Factor in auto-caps and manual caps and compute the current caps mode.
      * @param settingsValues the current settings values.
      * @param keyboardShiftMode the current shift mode of the keyboard. See
@@ -1482,7 +1598,7 @@
             final int newSelStart, final int newSelEnd) {
         final boolean shouldFinishComposition = mWordComposer.isComposingWord();
         resetComposingState(true /* alsoResetLastComposedWord */);
-        mLatinIME.setNeutralSuggestionStrip();
+        mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
         mConnection.resetCachesUponCursorMoveAndReturnSuccess(newSelStart, newSelEnd,
                 shouldFinishComposition);
     }
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java
new file mode 100644
index 0000000..60f1c7a
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 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.suggestions;
+
+import com.android.inputmethod.latin.SuggestedWords;
+
+/**
+ * An object that gives basic control of a suggestion strip and some info on it.
+ */
+public interface SuggestionStripViewAccessor {
+    public boolean hasSuggestionStripView();
+    public void showAddToDictionaryHint(final String word);
+    public boolean isShowingAddToDictionaryHint();
+    public void dismissAddToDictionaryHint();
+    public void setNeutralSuggestionStrip();
+    public void showSuggestionStrip(final SuggestedWords suggestedWords);
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java
index e29181c..9b6c462 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java
@@ -21,7 +21,7 @@
 import com.android.inputmethod.latin.Constants;
 
 @SmallTest
-public final class KeySpecParserTests extends KeySpecParserBase {
+public final class KeySpecParserTests extends KeySpecParserTestsBase {
     @Override
     protected void assertParser(final String message, final String keySpec,
             final String expectedLabel, final String expectedOutputText, final int expectedIcon,
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserBase.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTestsBase.java
similarity index 98%
rename from tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserBase.java
rename to tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTestsBase.java
index aecef23..04b7008 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserBase.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTestsBase.java
@@ -30,7 +30,7 @@
 
 import java.util.Locale;
 
-abstract class KeySpecParserBase extends AndroidTestCase {
+abstract class KeySpecParserTestsBase extends AndroidTestCase {
     private final static Locale TEST_LOCALE = Locale.ENGLISH;
     protected final KeyboardCodesSet mCodesSet = new KeyboardCodesSet();
     protected final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
@@ -251,8 +251,12 @@
     }
 
     public void testFormatError() {
+        assertParserError("Null spec", null, null,
+                null, ICON_UNDEFINED, CODE_UNSPECIFIED);
         assertParserError("Empty spec", "", null,
                 null, ICON_UNDEFINED, CODE_UNSPECIFIED);
+        assertParserError("Single bar", "|",
+                "|", null, ICON_UNDEFINED, '|');
         assertParserError("Empty label with outputText", "|a",
                 null, "a", ICON_UNDEFINED, CODE_UNSPECIFIED);
         assertParserError("Empty label with code", "|" + CODE_SETTINGS,
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecTests.java b/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecTests.java
index 213e2d4..e49c624 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecTests.java
@@ -24,7 +24,7 @@
 import java.util.Locale;
 
 @SmallTest
-public final class MoreKeySpecTests extends KeySpecParserBase {
+public final class MoreKeySpecTests extends KeySpecParserTestsBase {
     @Override
     protected void assertParser(final String message, final String moreKeySpec,
             final String expectedLabel, final String expectedOutputText, final int expectedIconId,