Merge "Import revised translations."
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index cb0a9a2..d680f18 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -21,6 +21,14 @@
             <meta-data android:name="android.view.im" android:resource="@xml/method" />
         </service>
 
+        <service android:name=".spellcheck.AndroidSpellCheckerService"
+                 android:label="@string/spell_checker_service_name"
+                 android:permission="android.permission.BIND_TEXT_SERVICE">
+            <intent-filter>
+                <action android:name="android.service.textservice.SpellCheckerService" />
+            </intent-filter>
+            <meta-data android:name="android.view.textservice.scs" android:resource="@xml/spellchecker" />
+        </service>
         <activity android:name="Settings" android:label="@string/english_ime_settings">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
diff --git a/java/res/drawable-hdpi/keyboard_background_holo.9.png b/java/res/drawable-hdpi/keyboard_background_holo.9.png
index 9f700c2..714db43 100644
--- a/java/res/drawable-hdpi/keyboard_background_holo.9.png
+++ b/java/res/drawable-hdpi/keyboard_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png b/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png
index 85b6360..e173beb 100644
--- a/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png
+++ b/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/more_suggestions_hint.png b/java/res/drawable-hdpi/more_suggestions_hint.png
new file mode 100644
index 0000000..4515f44
--- /dev/null
+++ b/java/res/drawable-hdpi/more_suggestions_hint.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_background_holo.9.png b/java/res/drawable-mdpi/keyboard_background_holo.9.png
index 4fe79b9..2776621 100644
--- a/java/res/drawable-mdpi/keyboard_background_holo.9.png
+++ b/java/res/drawable-mdpi/keyboard_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_suggest_strip_holo.9.png b/java/res/drawable-mdpi/keyboard_suggest_strip_holo.9.png
index e488323..b1c18b4 100644
--- a/java/res/drawable-mdpi/keyboard_suggest_strip_holo.9.png
+++ b/java/res/drawable-mdpi/keyboard_suggest_strip_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/more_suggestions_hint.png b/java/res/drawable-mdpi/more_suggestions_hint.png
new file mode 100644
index 0000000..6168de3
--- /dev/null
+++ b/java/res/drawable-mdpi/more_suggestions_hint.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_background_holo.9.png
index 7b79f60..a0aa4ba 100644
--- a/java/res/drawable-xhdpi/keyboard_background_holo.9.png
+++ b/java/res/drawable-xhdpi/keyboard_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_suggest_strip_holo.9.png b/java/res/drawable-xhdpi/keyboard_suggest_strip_holo.9.png
index b40f766..4c27072 100644
--- a/java/res/drawable-xhdpi/keyboard_suggest_strip_holo.9.png
+++ b/java/res/drawable-xhdpi/keyboard_suggest_strip_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/more_suggestions_hint.png b/java/res/drawable-xhdpi/more_suggestions_hint.png
new file mode 100644
index 0000000..f577a36
--- /dev/null
+++ b/java/res/drawable-xhdpi/more_suggestions_hint.png
Binary files differ
diff --git a/java/res/layout/candidates_strip.xml b/java/res/layout/candidates_strip.xml
index 269bc1b..bcc1322 100644
--- a/java/res/layout/candidates_strip.xml
+++ b/java/res/layout/candidates_strip.xml
@@ -29,31 +29,6 @@
         android:layout_width="0dp"
         android:layout_height="match_parent" />
     <LinearLayout
-        android:id="@+id/candidates_pane_control"
-        android:orientation="horizontal"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-    >
-        <TextView
-            android:id="@+id/expand_candidates_pane"
-            android:text="@string/label_expand_candidates_pane"
-            android:gravity="center"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:minWidth="28dp"
-            android:textSize="@dimen/candidate_text_size"
-            android:visibility="visible" />
-        <TextView
-            android:id="@+id/close_candidates_pane"
-            android:text="@string/label_close_candidates_pane"
-            android:gravity="center"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:minWidth="28dp"
-            android:textSize="@dimen/candidate_text_size"
-            android:visibility="gone" />
-    </LinearLayout>
-    <LinearLayout
         android:id="@+id/touch_to_save"
         android:orientation="horizontal"
         android:layout_width="match_parent"
diff --git a/java/res/values-sw600dp-land/dimens.xml b/java/res/values-sw600dp-land/dimens.xml
index 5d4967f..c75fcc0 100644
--- a/java/res/values-sw600dp-land/dimens.xml
+++ b/java/res/values-sw600dp-land/dimens.xml
@@ -51,5 +51,4 @@
     <fraction name="key_uppercase_letter_ratio">29%</fraction>
 
     <dimen name="candidate_strip_padding">40.0mm</dimen>
-    <integer name="candidate_count_in_strip">5</integer>
 </resources>
diff --git a/java/res/values-sw768dp-land/dimens.xml b/java/res/values-sw768dp-land/dimens.xml
index 18837fe..0cfdffd 100644
--- a/java/res/values-sw768dp-land/dimens.xml
+++ b/java/res/values-sw768dp-land/dimens.xml
@@ -59,5 +59,4 @@
     <dimen name="key_preview_offset_ics">0.05in</dimen>
 
     <dimen name="candidate_strip_padding">40.0mm</dimen>
-    <integer name="candidate_count_in_strip">5</integer>
 </resources>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index ab52271..172ca2f 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -116,6 +116,7 @@
         <attr name="colorAutoCorrect" format="color" />
         <attr name="colorSuggested" format="color" />
         <attr name="candidateCountInStrip" format="integer" />
+        <attr name="centerCandidatePercentile" format="integer" />
     </declare-styleable>
 
     <declare-styleable name="Keyboard">
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index a66caa7..2c4b35e 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -86,6 +86,7 @@
     <dimen name="candidate_padding">6dip</dimen>
     <dimen name="candidate_text_size">18dip</dimen>
     <integer name="candidate_count_in_strip">3</integer>
+    <integer name="center_candidate_percentile">36</integer>
 
     <!-- If the screen height in landscape is larger than the below value, then the keyboard
          will not go into extract (fullscreen) mode. -->
diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml
index 9727746..fb28766 100644
--- a/java/res/values/donottranslate.xml
+++ b/java/res/values/donottranslate.xml
@@ -19,7 +19,7 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Symbols that are suggested between words -->
-    <string name="suggested_punctuations">!?,\u0022\u0027:();-/@_</string>
+    <string name="suggested_punctuations">!?,\u0022:;()\u0027-/@_</string>
     <!-- Symbols that should be swapped with a magic space -->
     <string name="magic_space_swapping_symbols">.,;:!?)]}\u0022</string>
     <!-- Symbols that should strip a magic space -->
diff --git a/java/res/values/keycodes.xml b/java/res/values/keycodes.xml
index ee34529..59cc075 100644
--- a/java/res/values/keycodes.xml
+++ b/java/res/values/keycodes.xml
@@ -27,5 +27,5 @@
     <integer name="key_switch_alpha_symbol">-2</integer>
     <integer name="key_delete">-5</integer>
     <integer name="key_settings">-6</integer>
-    <integer name="key_shortcut">-8</integer>
+    <integer name="key_shortcut">-7</integer>
 </resources>
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index 9f91ef5..c3a0f8b 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -90,6 +90,7 @@
         <item name="colorAutoCorrect">#FFFCAE00</item>
         <item name="colorSuggested">#FFFCAE00</item>
         <item name="candidateCountInStrip">@integer/candidate_count_in_strip</item>
+        <item name="centerCandidatePercentile">@integer/center_candidate_percentile</item>
     </style>
     <!-- Theme "Basic high contrast" -->
     <style name="KeyboardView.HighContrast" parent="KeyboardView">
@@ -148,6 +149,7 @@
         <item name="verticalGap">@fraction/key_bottom_gap_ics</item>
     </style>
     <style name="LatinKeyboard.IceCreamSandwich" parent="LatinKeyboard">
+        <item name="autoCorrectionSpacebarLedEnabled">false</item>
         <item name="disabledShortcutIcon">@drawable/sym_keyboard_voice_off_holo</item>
     </style>
     <style name="KeyboardView.IceCreamSandwich" parent="KeyboardView">
@@ -192,6 +194,7 @@
         <item name="colorAutoCorrect">#FF3DC8FF</item>
         <item name="colorSuggested">#FFFFFFFF</item>
         <item name="candidateCountInStrip">@integer/candidate_count_in_strip</item>
+        <item name="centerCandidatePercentile">@integer/center_candidate_percentile</item>
     </style>
     <style name="PopupMiniKeyboardAnimation">
         <item name="android:windowEnterAnimation">@anim/mini_keyboard_fadein</item>
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index 7a4e7ea..fbbc7fb 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -3,16 +3,16 @@
 /**
  * Copyright (c) 2008, 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 
+ * 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 
+ *     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 
+ * 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.
  */
 -->
diff --git a/java/res/xml/spellchecker.xml b/java/res/xml/spellchecker.xml
new file mode 100644
index 0000000..ce09264
--- /dev/null
+++ b/java/res/xml/spellchecker.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+
+<!-- The attributes in this XML file provide the configuration information -->
+<!-- for the spell checker -->
+
+<spell-checker xmlns:android="http://schemas.android.com/apk/res/android"
+        android:label="@string/spell_checker_service_name">
+    <subtype
+            android:label="@string/subtype_en_US"
+            android:subtypeLocale="en_US"
+    />
+    <subtype
+            android:label="@string/subtype_en_GB"
+            android:subtypeLocale="en_GB"
+    />
+</spell-checker>
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 518bc8e..f8e08b0 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -79,8 +79,7 @@
     public static final int CODE_CANCEL = -4;
     public static final int CODE_DELETE = -5;
     public static final int CODE_SETTINGS = -6;
-    public static final int CODE_SETTINGS_LONGPRESS = -7;
-    public static final int CODE_SHORTCUT = -8;
+    public static final int CODE_SHORTCUT = -7;
     // Code value representing the code is not specified.
     public static final int CODE_UNSPECIFIED = -99;
 
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
index 905f779..8640912 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
@@ -70,4 +70,10 @@
      * Called when user released a finger outside any key.
      */
     public void onCancelInput();
+
+    /**
+     * Send a non-"code input" custom request to the listener.
+     * @return true if the request has been consumed, false otherwise.
+     */
+    public boolean onCustomRequest(int requestCode);
 }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index aab58b5..da3aa50 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -35,6 +35,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.RelativeLayout;
 import android.widget.TextView;
 
 import com.android.inputmethod.compat.FrameLayoutCompatUtils;
@@ -349,8 +350,11 @@
      * @param keyboard the keyboard to display in this view
      */
     public void setKeyboard(Keyboard keyboard) {
-        // Remove any pending messages, except dismissing preview
+        // Remove any pending dismissing preview
         mDrawingHandler.cancelAllShowKeyPreviews();
+        if (mKeyboard != null) {
+            PointerTracker.dismissAllKeyPreviews();
+        }
         mKeyboard = keyboard;
         LatinImeLogger.onSetKeyboard(keyboard);
         requestLayout();
@@ -788,14 +792,15 @@
 
     private void addKeyPreview(TextView keyPreview) {
         if (mPreviewPlacer == null) {
-            mPreviewPlacer = FrameLayoutCompatUtils.getPlacer(
-                    (ViewGroup)getRootView().findViewById(android.R.id.content));
+            mPreviewPlacer = new RelativeLayout(getContext());
+            final ViewGroup windowContentView =
+                    (ViewGroup)getRootView().findViewById(android.R.id.content);
+            windowContentView.addView(mPreviewPlacer);
         }
-        final ViewGroup placer = mPreviewPlacer;
-        placer.addView(keyPreview, FrameLayoutCompatUtils.newLayoutParam(placer, 0, 0));
+        mPreviewPlacer.addView(
+                keyPreview, FrameLayoutCompatUtils.newLayoutParam(mPreviewPlacer, 0, 0));
     }
 
-    // TODO: Introduce minimum duration for displaying key previews
     private void showKey(final int keyIndex, PointerTracker tracker) {
         final TextView previewText = tracker.getKeyPreviewText();
         // If the key preview has no parent view yet, add it to the ViewGroup which can place
@@ -914,5 +919,8 @@
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         closing();
+        if (mPreviewPlacer != null) {
+            mPreviewPlacer.removeAllViews();
+        }
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
index abf28c7..12aadcb 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
@@ -281,9 +281,6 @@
      */
     @Override
     public void setKeyboard(Keyboard keyboard) {
-        if (getKeyboard() != null) {
-            PointerTracker.dismissAllKeyPreviews();
-        }
         // Remove any pending messages, except dismissing preview
         mKeyTimerHandler.cancelKeyTimers();
         super.setKeyboard(keyboard);
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
index 0409677..dad37e7 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
@@ -23,6 +23,7 @@
 import android.view.MotionEvent;
 
 import com.android.inputmethod.deprecated.VoiceProxy;
+import com.android.inputmethod.latin.LatinIME;
 import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.Utils;
 
@@ -99,9 +100,14 @@
             }
         }
         if (primaryCode == Keyboard.CODE_SETTINGS || primaryCode == Keyboard.CODE_SPACE) {
-            tracker.onLongPressed();
             // Both long pressing settings key and space key invoke IME switcher dialog.
-            return invokeOnKey(Keyboard.CODE_SETTINGS_LONGPRESS);
+            if (getKeyboardActionListener().onCustomRequest(
+                    LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) {
+                tracker.onLongPressed();
+                return true;
+            } else {
+                return super.onLongPress(key, tracker);
+            }
         } else {
             return super.onLongPress(key, tracker);
         }
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index dc2d6e4..1f8119a 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -151,6 +151,8 @@
         public void onTextInput(CharSequence text) {}
         @Override
         public void onCancelInput() {}
+        @Override
+        public boolean onCustomRequest(int requestCode) { return false; }
     };
 
     public static void init(boolean hasDistinctMultitouch, Context context) {
diff --git a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
index dfaaa70..fb932e3 100644
--- a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
@@ -79,6 +79,8 @@
         public void onRelease(int primaryCode, boolean withSliding) {
             mParentKeyboardView.getKeyboardActionListener().onRelease(primaryCode, withSliding);
         }
+        @Override
+        public boolean onCustomRequest(int requestCode) { return false; }
     };
 
     public PopupMiniKeyboardView(Context context, AttributeSet attrs) {
diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java
index 915e73c..d779c85 100644
--- a/java/src/com/android/inputmethod/latin/CandidateView.java
+++ b/java/src/com/android/inputmethod/latin/CandidateView.java
@@ -38,6 +38,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 import android.widget.PopupWindow;
@@ -50,8 +51,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-public class CandidateView extends LinearLayout implements OnClickListener {
-
+public class CandidateView extends LinearLayout implements OnClickListener, OnLongClickListener {
     public interface Listener {
         public boolean addWordToDictionary(String word);
         public void pickSuggestionManually(int index, CharSequence word);
@@ -65,9 +65,6 @@
     private static final boolean DBG = LatinImeLogger.sDBG;
 
     private final ViewGroup mCandidatesStrip;
-    private final ViewGroup mCandidatesPaneControl;
-    private final TextView mExpandCandidatesPane;
-    private final TextView mCloseCandidatesPane;
     private ViewGroup mCandidatesPane;
     private ViewGroup mCandidatesPaneContainer;
     private View mKeyboardView;
@@ -89,7 +86,7 @@
 
     private final SuggestionsStripParams mStripParams;
     private final SuggestionsPaneParams mPaneParams;
-    private static final float MIN_TEXT_XSCALE = 0.75f;
+    private static final float MIN_TEXT_XSCALE = 0.70f;
 
     private final UiHandler mHandler = new UiHandler(this);
 
@@ -146,7 +143,6 @@
         public final int mPadding;
         public final int mDividerWidth;
         public final int mDividerHeight;
-        public final int mControlWidth;
         public final int mCandidateStripHeight;
 
         protected final List<TextView> mWords;
@@ -154,7 +150,7 @@
         protected final List<TextView> mInfos;
 
         protected CandidateViewParams(List<TextView> words, List<View> dividers,
-                List<TextView> infos, View control) {
+                List<TextView> infos) {
             mWords = words;
             mDividers = dividers;
             mInfos = infos;
@@ -165,7 +161,6 @@
             divider.measure(WRAP_CONTENT, MATCH_PARENT);
             mDividerWidth = divider.getMeasuredWidth();
             mDividerHeight = divider.getMeasuredHeight();
-            mControlWidth = control.getMeasuredWidth();
 
             final Resources res = word.getResources();
             mCandidateStripHeight = res.getDimensionPixelOffset(R.dimen.candidate_strip_height);
@@ -174,8 +169,8 @@
 
     private static class SuggestionsPaneParams extends CandidateViewParams {
         public SuggestionsPaneParams(List<TextView> words, List<View> dividers,
-                List<TextView> infos, View control) {
-            super(words, dividers, infos, control);
+                List<TextView> infos) {
+            super(words, dividers, infos);
         }
 
         public int layout(SuggestedWords suggestions, ViewGroup paneView, int from, int textColor,
@@ -259,12 +254,16 @@
 
     private static class SuggestionsStripParams extends CandidateViewParams {
         private static final int DEFAULT_CANDIDATE_COUNT_IN_STRIP = 3;
+        private static final int DEFAULT_CENTER_CANDIDATE_PERCENTILE = 40;
         private static final int PUNCTUATIONS_IN_STRIP = 6;
 
         private final int mColorTypedWord;
         private final int mColorAutoCorrect;
         private final int mColorSuggestedCandidate;
         private final int mCandidateCountInStrip;
+        private final float mCenterCandidateWeight;
+        private final int mCenterCandidateIndex;
+        private final Drawable mMoreCandidateHint;
 
         private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD);
         private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan();
@@ -278,21 +277,12 @@
         private final int mAutoCorrectHighlight;
 
         private final ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>();
-        private SuggestedWords mSuggestedWords;
 
-        private int mCountInStrip;
-        // True if the mCountInStrip suggestions can fit in suggestion strip in equally divided
-        // width without squeezing the text.
-        private boolean mCanUseFixedWidthColumns;
-        private int mMaxWidth;
-        private int mAvailableWidthForWords;
-        private int mConstantWidthForPaddings;
-        private int mVariableWidthForWords;
-        private float mScaleX;
+        public boolean mMoreSuggestionsAvailable;
 
         public SuggestionsStripParams(Context context, AttributeSet attrs, int defStyle,
-                List<TextView> words, List<View> dividers, List<TextView> infos, View control) {
-            super(words, dividers, infos, control);
+                List<TextView> words, List<View> dividers, List<TextView> infos) {
+            super(words, dividers, infos);
             final TypedArray a = context.obtainStyledAttributes(
                     attrs, R.styleable.CandidateView, defStyle, R.style.CandidateViewStyle);
             mAutoCorrectHighlight = a.getInt(R.styleable.CandidateView_autoCorrectHighlight, 0);
@@ -302,13 +292,20 @@
             mCandidateCountInStrip = a.getInt(
                     R.styleable.CandidateView_candidateCountInStrip,
                     DEFAULT_CANDIDATE_COUNT_IN_STRIP);
+            mCenterCandidateWeight = a.getInt(
+                    R.styleable.CandidateView_centerCandidatePercentile,
+                    DEFAULT_CENTER_CANDIDATE_PERCENTILE) / 100.0f;
             a.recycle();
 
+            mCenterCandidateIndex = mCandidateCountInStrip / 2;
+            final Resources res = context.getResources();
+            mMoreCandidateHint = res.getDrawable(R.drawable.more_suggestions_hint);
+
             mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff);
             mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord);
 
             mPaint = new TextPaint();
-            final float textSize = context.getResources().getDimension(R.dimen.candidate_text_size);
+            final float textSize = res.getDimension(R.dimen.candidate_text_size);
             mPaint.setTextSize(textSize);
         }
 
@@ -328,35 +325,38 @@
             return spannedWord;
         }
 
-        private int getWordPosition(int index) {
-            if (index >= 2) {
-                return index;
-            }
-            final boolean willAutoCorrect = !mSuggestedWords.mTypedWordValid
-                    && mSuggestedWords.mHasMinimalSuggestion;
-            return willAutoCorrect ? 1 - index : index;
+        private static boolean willAutoCorrect(SuggestedWords suggestions) {
+            return !suggestions.mTypedWordValid && suggestions.mHasMinimalSuggestion;
         }
 
-        private int getCandidateTextColor(int pos) {
-            final SuggestedWords suggestions = mSuggestedWords;
-            final boolean isAutoCorrect = suggestions.mHasMinimalSuggestion
-                    && ((pos == 1 && !suggestions.mTypedWordValid)
-                            || (pos == 0 && suggestions.mTypedWordValid));
+        private int getWordPosition(int index, SuggestedWords suggestions) {
+            // TODO: This works for 3 suggestions. Revisit this algorithm when there are 5 or more
+            // suggestions.
+            final int centerPos = willAutoCorrect(suggestions) ? 1 : 0;
+            if (index == mCenterCandidateIndex) {
+                return centerPos;
+            } else if (index == centerPos) {
+                return mCenterCandidateIndex;
+            } else {
+                return index;
+            }
+        }
+
+        private int getCandidateTextColor(int index, SuggestedWords suggestions, int pos) {
             // TODO: Need to revisit this logic with bigram suggestions
             final boolean isSuggestedCandidate = (pos != 0);
-            final boolean isPunctuationSuggestions = suggestions.isPunctuationSuggestions();
 
             final int color;
-            if (isPunctuationSuggestions) {
-                color = mColorTypedWord;
-            } else if (isAutoCorrect) {
+            if (index == mCenterCandidateIndex && willAutoCorrect(suggestions)) {
                 color = mColorAutoCorrect;
             } else if (isSuggestedCandidate) {
                 color = mColorSuggestedCandidate;
             } else {
                 color = mColorTypedWord;
             }
-            final SuggestedWordInfo info = suggestions.getInfo(pos);
+
+            final SuggestedWordInfo info = (pos < suggestions.size())
+                    ? suggestions.getInfo(pos) : null;
             if (info != null && info.isPreviousSuggestedWord()) {
                 return applyAlpha(color, 0.5f);
             } else {
@@ -381,136 +381,113 @@
 
         public int layout(SuggestedWords suggestions, ViewGroup stripView, ViewGroup paneView,
                 int stripWidth) {
-            mSuggestedWords = suggestions;
-            final int maxCount = suggestions.isPunctuationSuggestions()
-                    ? PUNCTUATIONS_IN_STRIP : mCandidateCountInStrip;
-            final int size = suggestions.size();
-            setupTexts(suggestions, size);
-            mCountInStrip = Math.min(maxCount, size);
-            mScaleX = 1.0f;
-            calculateParameters(size, stripWidth);
+            if (suggestions.isPunctuationSuggestions()) {
+                return layoutPunctuationSuggestions(suggestions, stripView);
+            }
 
-            int infoX = 0;
-            for (int index = 0; index < mCountInStrip; index++) {
-                final int pos = getWordPosition(index);
-                final TextView word = mWords.get(pos);
-                final View divider = mDividers.get(pos);
-                final TextPaint paint = word.getPaint();
-                // TODO: Reorder candidates in strip as appropriate. The center candidate should 
-                // hold the word when space is typed (valid typed word or auto corrected word).
-                word.setTextColor(getCandidateTextColor(pos));
-                final CharSequence styled = mTexts.get(pos);
+            final int countInStrip = mCandidateCountInStrip;
+            setupTexts(suggestions, countInStrip);
+            mMoreSuggestionsAvailable = (suggestions.size() > countInStrip);
+            int x = 0;
+            for (int index = 0; index < countInStrip; index++) {
+                final int pos = getWordPosition(index, suggestions);
 
-                final TextView info;
-                if (DBG) {
-                    final CharSequence debugInfo = getDebugInfo(mSuggestedWords, index);
-                    if (debugInfo != null) {
-                        info = mInfos.get(index);
-                        info.setText(debugInfo);
-                    } else {
-                        info = null;
-                    }
-                } else {
-                    info = null;
+                if (index != 0) {
+                    final View divider = mDividers.get(pos);
+                    // Add divider if this isn't the left most suggestion in candidate strip.
+                    stripView.addView(divider);
                 }
 
-                final CharSequence text;
-                final float scaleX;
-                    if (index == 0 && mCountInStrip == 1) {
-                        text = getEllipsizedText(styled, mMaxWidth, paint);
-                        scaleX = paint.getTextScaleX();
-                    } else {
-                        text = styled;
-                        scaleX = mScaleX;
-                    }
-                    word.setText(text);
-                    word.setTextScaleX(scaleX);
-                    if (index != 0) {
-                        // Add divider if this isn't the left most suggestion in candidate strip.
-                        stripView.addView(divider);
-                    }
-                    stripView.addView(word);
-                    if (mCanUseFixedWidthColumns) {
-                        setLayoutWeight(word, 1.0f, mCandidateStripHeight);
-                    } else {
-                        final int width = getTextWidth(text, paint) + mPadding;
-                        setLayoutWeight(word, width, mCandidateStripHeight);
-                    }
-                    if (info != null) {
+                final CharSequence styled = mTexts.get(pos);
+                final TextView word = mWords.get(pos);
+                if (index == mCenterCandidateIndex && mMoreSuggestionsAvailable) {
+                    // TODO: This "more suggestions hint" should have nicely designed icon.
+                    word.setCompoundDrawablesWithIntrinsicBounds(
+                            null, null, null, mMoreCandidateHint);
+                } else {
+                    word.setCompoundDrawables(null, null, null, null);
+                }
+
+                // Disable this candidate if the suggestion is null or empty.
+                word.setEnabled(!TextUtils.isEmpty(styled));
+                word.setTextColor(getCandidateTextColor(index, suggestions, pos));
+                final int width = getCandidateWidth(index, stripWidth);
+                final CharSequence text = getEllipsizedText(styled, width, word.getPaint());
+                final float scaleX = word.getTextScaleX();
+                word.setText(text); // TextView.setText() resets text scale x to 1.0.
+                word.setTextScaleX(scaleX);
+                stripView.addView(word);
+                setLayoutWeight(word, getCandidateWeight(index), mCandidateStripHeight);
+
+                if (DBG) {
+                    final CharSequence debugInfo = getDebugInfo(suggestions, pos);
+                    if (debugInfo != null) {
+                        final TextView info = mInfos.get(pos);
+                        info.setText(debugInfo);
                         paneView.addView(info);
                         info.measure(WRAP_CONTENT, WRAP_CONTENT);
-                        final int width = info.getMeasuredWidth();
+                        final int infoWidth = info.getMeasuredWidth();
                         final int y = info.getMeasuredHeight();
-                        FrameLayoutCompatUtils.placeViewAt(info, infoX, 0, width, y);
-                        infoX += width * 2;
+                        FrameLayoutCompatUtils.placeViewAt(info, x, 0, infoWidth, y);
+                        x += infoWidth * 2;
                     }
+                }
             }
 
-            return mCountInStrip;
+            return countInStrip;
         }
 
-        private void calculateParameters(int size, int maxWidth) {
-            do {
-                mMaxWidth = maxWidth;
-                if (size > mCountInStrip) {
-                    mMaxWidth -= mControlWidth;
-                }
-
-                tryLayout();
-
-                if (mCanUseFixedWidthColumns) {
-                    return;
-                }
-                if (mVariableWidthForWords <= mAvailableWidthForWords) {
-                    return;
-                }
-
-                final float scaleX = mAvailableWidthForWords / (float)mVariableWidthForWords;
-                if (scaleX >= MIN_TEXT_XSCALE) {
-                    mScaleX = scaleX;
-                    return;
-                }
-
-                mCountInStrip--;
-            } while (mCountInStrip > 1);
+        private int getCandidateWidth(int index, int maxWidth) {
+            final int paddings = mPadding * mCandidateCountInStrip;
+            final int dividers = mDividerWidth * (mCandidateCountInStrip - 1);
+            final int availableWidth = maxWidth - paddings - dividers;
+            return (int)(availableWidth * getCandidateWeight(index));
         }
 
-        private void tryLayout() {
-            final int maxCount = mCountInStrip;
-            final int dividers = mDividerWidth * (maxCount - 1);
-            mConstantWidthForPaddings = dividers + mPadding * maxCount;
-            mAvailableWidthForWords = mMaxWidth - mConstantWidthForPaddings;
-
-            mPaint.setTextScaleX(mScaleX);
-            final int maxFixedWidthForWord = (mMaxWidth - dividers) / maxCount - mPadding;
-            mCanUseFixedWidthColumns = true;
-            mVariableWidthForWords = 0;
-            for (int i = 0; i < maxCount; i++) {
-                final int width = getTextWidth(mTexts.get(i), mPaint);
-                if (width > maxFixedWidthForWord)
-                    mCanUseFixedWidthColumns = false;
-                mVariableWidthForWords += width;
+        private float getCandidateWeight(int index) {
+            if (index == mCenterCandidateIndex) {
+                return mCenterCandidateWeight;
+            } else {
+                // TODO: Revisit this for cases of 5 or more suggestions
+                return (1.0f - mCenterCandidateWeight) / (mCandidateCountInStrip - 1);
             }
         }
 
-        private void setupTexts(SuggestedWords suggestions, int count) {
+        private void setupTexts(SuggestedWords suggestions, int countInStrip) {
             mTexts.clear();
-            for (int i = 0; i < count; i++) {
-                final CharSequence word = suggestions.getWord(i);
-                final boolean isAutoCorrect = suggestions.mHasMinimalSuggestion
-                        && ((i == 1 && !suggestions.mTypedWordValid)
-                                || (i == 0 && suggestions.mTypedWordValid));
+            final int count = Math.min(suggestions.size(), countInStrip);
+            for (int pos = 0; pos < count; pos++) {
+                final CharSequence word = suggestions.getWord(pos);
+                final boolean isAutoCorrect = pos == 1 && willAutoCorrect(suggestions);
                 final CharSequence styled = getStyledCandidateWord(word, isAutoCorrect);
                 mTexts.add(styled);
             }
+            for (int pos = count; pos < countInStrip; pos++) {
+                // Make this inactive for touches in layout().
+                mTexts.add(null);
+            }
         }
 
-        @Override
-        public String toString() {
-            return String.format(
-                    "count=%d width=%d avail=%d fixcol=%s scaleX=%4.2f const=%d var=%d",
-                    mCountInStrip, mMaxWidth, mAvailableWidthForWords, mCanUseFixedWidthColumns,
-                    mScaleX, mConstantWidthForPaddings, mVariableWidthForWords);
+        private int layoutPunctuationSuggestions(SuggestedWords suggestions, ViewGroup stripView) {
+            final int countInStrip = Math.min(suggestions.size(), PUNCTUATIONS_IN_STRIP);
+            for (int index = 0; index < countInStrip; index++) {
+                if (index != 0) {
+                    // Add divider if this isn't the left most suggestion in candidate strip.
+                    stripView.addView(mDividers.get(index));
+                }
+
+                final TextView word = mWords.get(index);
+                word.setEnabled(true);
+                word.setTextColor(mColorTypedWord);
+                final CharSequence text = suggestions.getWord(index);
+                word.setText(text);
+                word.setTextScaleX(1.0f);
+                word.setCompoundDrawables(null, null, null, null);
+                stripView.addView(word);
+                setLayoutWeight(word, 1.0f, mCandidateStripHeight);
+            }
+            mMoreSuggestionsAvailable = false;
+            return countInStrip;
         }
     }
 
@@ -548,13 +525,14 @@
         mPreviewPopup.setBackgroundDrawable(null);
 
         mCandidatesStrip = (ViewGroup)findViewById(R.id.candidates_strip);
-        for (int i = 0; i < MAX_SUGGESTIONS; i++) {
+        for (int pos = 0; pos < MAX_SUGGESTIONS; pos++) {
             final TextView word = (TextView)inflater.inflate(R.layout.candidate_word, null);
-            word.setTag(i);
+            word.setTag(pos);
             word.setOnClickListener(this);
+            word.setOnLongClickListener(this);
             mWords.add(word);
             final View divider = inflater.inflate(R.layout.candidate_divider, null);
-            divider.setTag(i);
+            divider.setTag(pos);
             divider.setOnClickListener(this);
             mDividers.add(divider);
             mInfos.add((TextView)inflater.inflate(R.layout.candidate_info, null));
@@ -564,41 +542,9 @@
         mWordToSave = (TextView)findViewById(R.id.word_to_save);
         mWordToSave.setOnClickListener(this);
 
-        final TypedArray keyboardViewAttr = context.obtainStyledAttributes(
-                attrs, R.styleable.KeyboardView, R.attr.keyboardViewStyle, R.style.KeyboardView);
-        final Drawable expandBackground = keyboardViewAttr.getDrawable(
-                R.styleable.KeyboardView_keyBackground);
-        final Drawable closeBackground = keyboardViewAttr.getDrawable(
-                R.styleable.KeyboardView_keyBackground);
-        final int keyTextColor = keyboardViewAttr.getColor(
-                R.styleable.KeyboardView_keyTextColor, 0xFF000000);
-        keyboardViewAttr.recycle();
-
-        mCandidatesPaneControl = (ViewGroup)findViewById(R.id.candidates_pane_control);
-        mExpandCandidatesPane = (TextView)findViewById(R.id.expand_candidates_pane);
-        mExpandCandidatesPane.setBackgroundDrawable(expandBackground);
-        mExpandCandidatesPane.setTextColor(keyTextColor);
-        mExpandCandidatesPane.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                expandCandidatesPane();
-            }
-        });
-        mCloseCandidatesPane = (TextView)findViewById(R.id.close_candidates_pane);
-        mCloseCandidatesPane.setBackgroundDrawable(closeBackground);
-        mCloseCandidatesPane.setTextColor(keyTextColor);
-        mCloseCandidatesPane.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                closeCandidatesPane();
-            }
-        });
-        mCandidatesPaneControl.measure(WRAP_CONTENT, WRAP_CONTENT);
-
-        mStripParams = new SuggestionsStripParams(context, attrs, defStyle,
-                mWords, mDividers, mInfos, mCandidatesPaneControl);
-        mPaneParams = new SuggestionsPaneParams(
-                mWords, mDividers, mInfos, mCandidatesPaneControl);
+        mStripParams = new SuggestionsStripParams(context, attrs, defStyle, mWords, mDividers,
+                mInfos);
+        mPaneParams = new SuggestionsPaneParams(mWords, mDividers, mInfos);
     }
 
     /**
@@ -619,7 +565,6 @@
         if (suggestions == null)
             return;
         mSuggestions = suggestions;
-        mExpandCandidatesPane.setEnabled(false);
         if (mShowingAutoCorrectionInverted) {
             mHandler.postUpdateSuggestions();
         } else {
@@ -638,18 +583,10 @@
                 mSuggestions, mCandidatesStrip, mCandidatesPane, width);
         final int countInPane = mPaneParams.layout(
                 mSuggestions, mCandidatesPane, countInStrip, mStripParams.getTextColor(), width);
-
-        if (countInPane <= 0 && !DBG) {
-            mCandidatesPaneControl.setVisibility(GONE);
-        } else {
-            mCandidatesPaneControl.setVisibility(VISIBLE);
-            mExpandCandidatesPane.setVisibility(VISIBLE);
-            mExpandCandidatesPane.setEnabled(true);
-        }
     }
 
     private static CharSequence getDebugInfo(SuggestedWords suggestions, int pos) {
-        if (DBG) {
+        if (DBG && pos < suggestions.size()) {
             final SuggestedWordInfo wordInfo = suggestions.getInfo(pos);
             if (wordInfo != null) {
                 final CharSequence debugInfo = wordInfo.getDebugString();
@@ -695,14 +632,17 @@
             TextPaint paint) {
         paint.setTextScaleX(1.0f);
         final int width = getTextWidth(text, paint);
-        final float scaleX = Math.min(maxWidth / (float)width, 1.0f);
+        if (width <= maxWidth) {
+            return text;
+        }
+        final float scaleX = maxWidth / (float)width;
         if (scaleX >= MIN_TEXT_XSCALE) {
             paint.setTextScaleX(scaleX);
             return text;
         }
 
         // Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To get
-        // squeezed and ellipsezed text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE).
+        // squeezed and ellipsized text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE).
         final CharSequence ellipsized = TextUtils.ellipsize(
                 text, paint, maxWidth / MIN_TEXT_XSCALE, TextUtils.TruncateAt.MIDDLE);
         paint.setTextScaleX(MIN_TEXT_XSCALE);
@@ -741,20 +681,24 @@
     }
 
     private void expandCandidatesPane() {
-        mExpandCandidatesPane.setVisibility(GONE);
-        mCloseCandidatesPane.setVisibility(VISIBLE);
         mCandidatesPaneContainer.setMinimumHeight(mKeyboardView.getMeasuredHeight());
         mCandidatesPaneContainer.setVisibility(VISIBLE);
         mKeyboardView.setVisibility(GONE);
     }
 
     private void closeCandidatesPane() {
-        mExpandCandidatesPane.setVisibility(VISIBLE);
-        mCloseCandidatesPane.setVisibility(GONE);
         mCandidatesPaneContainer.setVisibility(GONE);
         mKeyboardView.setVisibility(VISIBLE);
     }
 
+    private void toggleCandidatesPane() {
+        if (mCandidatesPaneContainer.getVisibility() == VISIBLE) {
+            closeCandidatesPane();
+        } else {
+            expandCandidatesPane();
+        }
+    }
+
     public void onAutoCorrectionInverted(CharSequence autoCorrectedWord) {
         final CharSequence inverted = mStripParams.getInvertedText(autoCorrectedWord);
         if (inverted == null)
@@ -772,7 +716,6 @@
         mWordToSave.setText(word);
         mShowingAddToDictionary = true;
         mCandidatesStrip.setVisibility(GONE);
-        mCandidatesPaneControl.setVisibility(GONE);
         mTouchToSave.setVisibility(VISIBLE);
     }
 
@@ -831,6 +774,15 @@
     }
 
     @Override
+    public boolean onLongClick(View view) {
+        if (mStripParams.mMoreSuggestionsAvailable) {
+            toggleCandidatesPane();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
     public void onClick(View view) {
         if (view == mWordToSave) {
             addToDictionary(((TextView)view).getText());
@@ -838,6 +790,11 @@
             return;
         }
 
+        if (view == mCandidatesPane) {
+            closeCandidatesPane();
+            return;
+        }
+
         final Object tag = view.getTag();
         if (!(tag instanceof Integer))
             return;
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 6c91c45..a588158 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -170,7 +170,6 @@
     private WordComposer mWordComposer = new WordComposer();
     private CharSequence mBestWord;
     private boolean mHasUncommittedTypedChars;
-    private boolean mHasDictionary;
     // Magic space: a space that should disappear on space/apostrophe insertion, move after the
     // 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.
@@ -430,8 +429,8 @@
         public boolean postStartInputView(EditorInfo attribute) {
             if (hasMessages(MSG_CONFIRM_ORIENTATION_CHANGE) || hasMessages(MSG_START_INPUT_VIEW)) {
                 removeMessages(MSG_START_INPUT_VIEW);
-                // Postpone onStartInputView 20ms afterward and see if orientation change has
-                // finished.
+                // Postpone onStartInputView by ACCUMULATE_START_INPUT_VIEW_DELAY and see if
+                // orientation change has finished.
                 sendMessageDelayed(obtainMessage(MSG_START_INPUT_VIEW, attribute),
                         ACCUMULATE_START_INPUT_VIEW_DELAY);
                 return true;
@@ -684,8 +683,6 @@
         // Delay updating suggestions because keyboard input view may not be shown at this point.
         mHandler.postUpdateSuggestions();
 
-        updateCorrectionMode();
-
         inputView.setKeyPreviewPopupEnabled(mSettingsValues.mKeyPreviewPopupOn,
                 mSettingsValues.mKeyPreviewPopupDismissDelay);
         inputView.setProximityCorrectionEnabled(true);
@@ -1152,25 +1149,33 @@
     }
 
     private void onSettingsKeyPressed() {
-        if (isShowingOptionDialog())
-            return;
+        if (isShowingOptionDialog()) return;
         if (InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) {
             showSubtypeSelectorAndSettings();
-        } else if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm)) {
+        } else if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm,
+                false /* should exclude auxiliary subtypes */)) {
             showOptionsMenu();
         } else {
             launchSettings();
         }
     }
 
-    private void onSettingsKeyLongPressed() {
-        if (!isShowingOptionDialog()) {
-            if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm)) {
+    // Virtual codes representing custom requests.  These are used in onCustomRequest() below.
+    public static final int CODE_SHOW_INPUT_METHOD_PICKER = 1;
+
+    @Override
+    public boolean onCustomRequest(int requestCode) {
+        if (isShowingOptionDialog()) return false;
+        switch (requestCode) {
+        case CODE_SHOW_INPUT_METHOD_PICKER:
+            if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm,
+                    true /* should include auxiliary subtypes */)) {
                 mImm.showInputMethodPicker();
-            } else {
-                launchSettings();
+                return true;
             }
+            return false;
         }
+        return false;
     }
 
     private boolean isShowingOptionDialog() {
@@ -1214,9 +1219,6 @@
         case Keyboard.CODE_SETTINGS:
             onSettingsKeyPressed();
             break;
-        case Keyboard.CODE_SETTINGS_LONGPRESS:
-            onSettingsKeyLongPressed();
-            break;
         case Keyboard.CODE_CAPSLOCK:
             switcher.toggleCapsLock();
             break;
@@ -1457,7 +1459,7 @@
             // in Italian dov' should not be expanded to dove' because the elision
             // requires the last vowel to be removed.
             final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled
-                    && !mInputTypeNoAutoCorrect && mHasDictionary;
+                    && !mInputTypeNoAutoCorrect;
             if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) {
                 pickedDefault = pickDefaultSuggestion(primaryCode);
             } else {
@@ -1752,9 +1754,10 @@
             sendMagicSpace();
         }
 
-        // We should show the hint if the user pressed the first entry AND either:
+        // We should show the "Touch again to save" hint if the user pressed the first entry
+        // AND either:
         // - There is no dictionary (we know that because we tried to load it => null != mSuggest
-        //   AND mHasDictionary is false)
+        //   AND mSuggest.hasMainDictionary() is false)
         // - There is a dictionary and the word is not in it
         // 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
@@ -1762,7 +1765,7 @@
         // to do with the autocorrection setting.
         final boolean showingAddToDictionaryHint = index == 0 && mSuggest != null
                 // If there is no dictionary the hint should be shown.
-                && (!mHasDictionary
+                && (!mSuggest.hasMainDictionary()
                         // If "suggestion" is not in the dictionary, the hint should be shown.
                         || !AutoCorrection.isValidWord(
                                 mSuggest.getUnigramDictionaries(), suggestion, true));
@@ -1925,15 +1928,16 @@
         }
 
         final CharSequence separator = ic.getTextBeforeCursor(1, 0);
-        ic.deleteSurroundingText(mCommittedLength + 1 /* separator */, 0);
+        ic.deleteSurroundingText(1, 0);
+        final CharSequence textToTheLeft = ic.getTextBeforeCursor(mCommittedLength, 0);
+        ic.deleteSurroundingText(mCommittedLength, 0);
 
         // Re-insert "separator" only when the deleted character was word separator and the
         // composing text wasn't equal to the auto-corrected text which can be found before
         // the cursor.
         if (!TextUtils.isEmpty(separator)
                 && mSettingsValues.isWordSeparator(separator.charAt(0))
-                && !TextUtils.equals(mComposingStringBuilder,
-                        ic.getTextBeforeCursor(mCommittedLength, 0))) {
+                && !TextUtils.equals(mComposingStringBuilder, textToTheLeft)) {
             ic.commitText(mComposingStringBuilder, 1);
             TextEntryState.acceptedTyped(mComposingStringBuilder);
             ic.commitText(separator, 1);
@@ -2096,9 +2100,8 @@
 
     private void updateCorrectionMode() {
         // TODO: cleanup messy flags
-        mHasDictionary = mSuggest != null ? mSuggest.hasMainDictionary() : false;
         final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled
-                && !mInputTypeNoAutoCorrect && mHasDictionary;
+                && !mInputTypeNoAutoCorrect;
         mCorrectionMode = (shouldAutoCorrect && mSettingsValues.mAutoCorrectEnabled)
                 ? Suggest.CORRECTION_FULL
                 : (shouldAutoCorrect ? Suggest.CORRECTION_BASIC : Suggest.CORRECTION_NONE);
@@ -2135,14 +2138,14 @@
     }
 
     protected void launchSettings() {
-        launchSettings(Settings.class);
+        launchSettingsClass(Settings.class);
     }
 
     public void launchDebugSettings() {
-        launchSettings(DebugSettings.class);
+        launchSettingsClass(DebugSettings.class);
     }
 
-    protected void launchSettings(Class<? extends PreferenceActivity> settingsClass) {
+    protected void launchSettingsClass(Class<? extends PreferenceActivity> settingsClass) {
         handleClose();
         Intent intent = new Intent();
         intent.setClass(LatinIME.this, settingsClass);
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 933a941..36a29e8 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -191,6 +191,8 @@
         mCorrectionMode = mode;
     }
 
+    // The main dictionary could have been loaded asynchronously.  Don't cache the return value
+    // of this method.
     public boolean hasMainDictionary() {
         return mMainDict != null;
     }
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index 16a2b0e..c07793c 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -111,35 +111,42 @@
         }
     }
 
-    public static boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManagerCompatWrapper imm) {
+    public static boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManagerCompatWrapper imm,
+            boolean shouldIncludeAuxiliarySubtypes) {
         final List<InputMethodInfoCompatWrapper> enabledImis = imm.getEnabledInputMethodList();
 
-        // Filters out IMEs that have auxiliary subtypes only (including either implicitly or
-        // explicitly enabled ones).
-        final ArrayList<InputMethodInfoCompatWrapper> filteredImis =
-                new ArrayList<InputMethodInfoCompatWrapper>();
+        // Number of the filtered IMEs
+        int filteredImisCount = 0;
 
-        outerloop:
         for (InputMethodInfoCompatWrapper imi : enabledImis) {
             // We can return true immediately after we find two or more filtered IMEs.
-            if (filteredImis.size() > 1) return true;
+            if (filteredImisCount > 1) return true;
             final List<InputMethodSubtypeCompatWrapper> subtypes =
                     imm.getEnabledInputMethodSubtypeList(imi, true);
-            // IMEs that have no subtypes should be included.
+            // IMEs that have no subtypes should be counted.
             if (subtypes.isEmpty()) {
-                filteredImis.add(imi);
+                ++filteredImisCount;
                 continue;
             }
-            // IMEs that have one or more non-auxiliary subtypes should be included.
+
+            int auxCount = 0;
             for (InputMethodSubtypeCompatWrapper subtype : subtypes) {
-                if (!subtype.isAuxiliary()) {
-                    filteredImis.add(imi);
-                    continue outerloop;
+                if (subtype.isAuxiliary()) {
+                    ++auxCount;
                 }
             }
+            final int nonAuxCount = subtypes.size() - auxCount;
+
+            // IMEs that have one or more non-auxiliary subtypes should be counted.
+            // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
+            // subtypes should be counted as well.
+            if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
+                ++filteredImisCount;
+                continue;
+            }
         }
 
-        return filteredImis.size() > 1
+        return filteredImisCount > 1
         // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled
         // input method subtype (The current IME should be LatinIME.)
                 || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;