Implement popup suggestions pane

Bug: 5023981
Change-Id: Ie1d69131dbf884a3f6a2beb3ac3427e5437c1486
diff --git a/java/res/layout/more_suggestions.xml b/java/res/layout/more_suggestions.xml
new file mode 100644
index 0000000..d387a15
--- /dev/null
+++ b/java/res/layout/more_suggestions.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        style="?attr/miniKeyboardPanelStyle"
+        >
+    <com.android.inputmethod.latin.MoreSuggestionsView
+            xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+            android:id="@+id/more_suggestions_view"
+            android:layout_alignParentBottom="true"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            latin:keyLabelSize="@dimen/candidate_text_size"
+            latin:keyHintLetterRatio="@fraction/more_suggestions_info_ratio"
+            latin:keyHintLetterColor="@android:color/white"
+            />
+</LinearLayout>
diff --git a/java/res/values-land/dimens.xml b/java/res/values-land/dimens.xml
index 73e1aff..4bf0e20 100644
--- a/java/res/values-land/dimens.xml
+++ b/java/res/values-land/dimens.xml
@@ -59,6 +59,8 @@
     <dimen name="key_preview_offset_ics">0.01in</dimen>
 
     <dimen name="candidate_strip_height">36dip</dimen>
+    <dimen name="more_suggestions_row_height">36dip</dimen>
+    <dimen name="candidate_strip_minimum_height">160sp</dimen>
     <dimen name="candidate_strip_fading_edge_length">63dip</dimen>
     <!-- Amount of allowance for selecting keys in a mini popup keyboard by sliding finger. -->
     <!-- popup_key_height x 1.2 -->
diff --git a/java/res/values-sw600dp/dimens.xml b/java/res/values-sw600dp/dimens.xml
index 942bc72..6ce23cc 100644
--- a/java/res/values-sw600dp/dimens.xml
+++ b/java/res/values-sw600dp/dimens.xml
@@ -67,6 +67,7 @@
     <dimen name="key_preview_offset_ics">0.05in</dimen>
 
     <dimen name="candidate_strip_height">44dip</dimen>
+    <dimen name="more_suggestions_row_height">44dip</dimen>
     <dimen name="candidate_strip_padding">15.0mm</dimen>
     <dimen name="candidate_min_width">0.3in</dimen>
     <dimen name="candidate_padding">12dip</dimen>
diff --git a/java/res/values-sw768dp/dimens.xml b/java/res/values-sw768dp/dimens.xml
index f0340bc..9b307e6 100644
--- a/java/res/values-sw768dp/dimens.xml
+++ b/java/res/values-sw768dp/dimens.xml
@@ -70,9 +70,8 @@
     <dimen name="key_preview_offset_ics">0.05in</dimen>
 
     <dimen name="candidate_strip_height">44dip</dimen>
-    <!-- candidate_strip_minimum_height =
-         key_preview_height_holo - key_preview_offset_holo + alpha -->
-    <dimen name="candidate_strip_minimum_height">18mm</dimen>
+    <dimen name="more_suggestions_row_height">44dip</dimen>
+    <dimen name="candidate_strip_minimum_height">200sp</dimen>
     <dimen name="candidate_strip_padding">15.0mm</dimen>
     <dimen name="candidate_min_width">46dip</dimen>
     <dimen name="candidate_padding">8dip</dimen>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index fdeca25..9d34268 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -27,6 +27,7 @@
         <attr name="miniKeyboardPanelStyle" format="reference" />
         <!-- Suggestions strip style -->
         <attr name="suggestionsStripBackgroundStyle" format="reference" />
+        <attr name="suggestionsPaneViewStyle" format="reference" />
         <attr name="suggestionBackgroundStyle" format="reference" />
         <attr name="suggestionPreviewBackgroundStyle" format="reference" />
         <attr name="candidateViewStyle" format="reference" />
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index 2c4b35e..b7609a4 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -77,9 +77,11 @@
     <dimen name="key_preview_offset_ics">0.05in</dimen>
 
     <dimen name="candidate_strip_height">40dip</dimen>
-    <!-- candidate_strip_minimum_height =
-         key_preview_height_holo - key_preview_offset_holo + alpha -->
-    <dimen name="candidate_strip_minimum_height">100sp</dimen>
+    <dimen name="more_suggestions_key_horizontal_padding">12dip</dimen>
+    <dimen name="more_suggestions_row_height">40dip</dimen>
+    <dimen name="more_suggestions_slide_allowance">0.2in</dimen>
+    <fraction name="more_suggestions_info_ratio">12%</fraction>
+    <dimen name="candidate_strip_minimum_height">200sp</dimen>
     <dimen name="candidate_strip_fading_edge_length">63dip</dimen>
     <dimen name="candidate_strip_padding">0dip</dimen>
     <dimen name="candidate_min_width">44dip</dimen>
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index ddc0634..4cc2a53 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -79,6 +79,11 @@
         <item name="android:paddingLeft">@dimen/mini_keyboard_horizontal_edges_padding</item>
         <item name="android:paddingRight">@dimen/mini_keyboard_horizontal_edges_padding</item>
     </style>
+    <style
+        name="SuggestionsPaneViewStyle"
+        parent="MiniKeyboardView"
+    >
+    </style>
     <style name="SuggestionsStripBackgroundStyle">
         <item name="android:background">@drawable/keyboard_suggest_strip</item>
     </style>
@@ -236,6 +241,11 @@
     >
     </style>
     <style
+        name="SuggestionsPaneViewStyle.IceCreamSandwich"
+        parent="MiniKeyboardView.IceCreamSandwich"
+    >
+    </style>
+    <style
         name="CandidateViewStyle.IceCreamSandwich"
         parent="SuggestionsStripBackgroundStyle.IceCreamSandwich"
     >
diff --git a/java/res/values/themes-basic-highcontrast.xml b/java/res/values/themes-basic-highcontrast.xml
index 17c06a2..51bf2f1 100644
--- a/java/res/values/themes-basic-highcontrast.xml
+++ b/java/res/values/themes-basic-highcontrast.xml
@@ -22,6 +22,7 @@
         <item name="miniKeyboardViewStyle">@style/MiniKeyboardView</item>
         <item name="miniKeyboardPanelStyle">@style/MiniKeyboardPanelStyle</item>
         <item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
+        <item name="suggestionsPaneViewStyle">@style/SuggestionsPaneViewStyle</item>
         <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
         <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
         <item name="candidateViewStyle">@style/CandidateViewStyle</item>
diff --git a/java/res/values/themes-basic.xml b/java/res/values/themes-basic.xml
index 23c71bd..7288dba 100644
--- a/java/res/values/themes-basic.xml
+++ b/java/res/values/themes-basic.xml
@@ -22,6 +22,7 @@
         <item name="miniKeyboardViewStyle">@style/MiniKeyboardView</item>
         <item name="miniKeyboardPanelStyle">@style/MiniKeyboardPanelStyle</item>
         <item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
+        <item name="suggestionsPaneViewStyle">@style/SuggestionsPaneViewStyle</item>
         <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
         <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
         <item name="candidateViewStyle">@style/CandidateViewStyle</item>
diff --git a/java/res/values/themes-gingerbread.xml b/java/res/values/themes-gingerbread.xml
index 1e3419f..5b62378 100644
--- a/java/res/values/themes-gingerbread.xml
+++ b/java/res/values/themes-gingerbread.xml
@@ -22,6 +22,7 @@
         <item name="miniKeyboardViewStyle">@style/MiniKeyboardView.Gingerbread</item>
         <item name="miniKeyboardPanelStyle">@style/MiniKeyboardPanelStyle</item>
         <item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
+        <item name="suggestionsPaneViewStyle">@style/SuggestionsPaneViewStyle</item>
         <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
         <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
         <item name="candidateViewStyle">@style/CandidateViewStyle</item>
diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml
index 7fa879c..597d87b 100644
--- a/java/res/values/themes-ics.xml
+++ b/java/res/values/themes-ics.xml
@@ -22,6 +22,7 @@
         <item name="miniKeyboardViewStyle">@style/MiniKeyboardView.IceCreamSandwich</item>
         <item name="miniKeyboardPanelStyle">@style/MiniKeyboardPanelStyle.IceCreamSandwich</item>
         <item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle.IceCreamSandwich</item>
+        <item name="suggestionsPaneViewStyle">@style/SuggestionsPaneViewStyle.IceCreamSandwich</item>
         <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle.IceCreamSandwich</item>
         <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle.IceCreamSandwich</item>
         <item name="candidateViewStyle">@style/CandidateViewStyle.IceCreamSandwich</item>
diff --git a/java/res/values/themes-stone-bold.xml b/java/res/values/themes-stone-bold.xml
index fa2ee2d..ff7107d 100644
--- a/java/res/values/themes-stone-bold.xml
+++ b/java/res/values/themes-stone-bold.xml
@@ -22,7 +22,8 @@
         <item name="miniKeyboardViewStyle">@style/MiniKeyboardView.Stone</item>
         <item name="miniKeyboardPanelStyle">@style/MiniKeyboardPanelStyle</item>
         <item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
-        <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
+        <item name="suggestionsPaneViewStyle">@style/SuggestionsPaneViewStyle</item>
+            <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
         <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
         <item name="candidateViewStyle">@style/CandidateViewStyle</item>
     </style>
diff --git a/java/res/values/themes-stone.xml b/java/res/values/themes-stone.xml
index 494bae6..35390d1 100644
--- a/java/res/values/themes-stone.xml
+++ b/java/res/values/themes-stone.xml
@@ -22,6 +22,7 @@
         <item name="miniKeyboardViewStyle">@style/MiniKeyboardView.Stone</item>
         <item name="miniKeyboardPanelStyle">@style/MiniKeyboardPanelStyle</item>
         <item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
+        <item name="suggestionsPaneViewStyle">@style/SuggestionsPaneViewStyle</item>
         <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
         <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
         <item name="candidateViewStyle">@style/CandidateViewStyle</item>
diff --git a/java/res/xml-sw600dp/kbd_mini_keyboard_template.xml b/java/res/xml-sw600dp/kbd_mini_keyboard_template.xml
index d976499..9955fe8 100644
--- a/java/res/xml-sw600dp/kbd_mini_keyboard_template.xml
+++ b/java/res/xml-sw600dp/kbd_mini_keyboard_template.xml
@@ -20,8 +20,6 @@
 
 <Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
     latin:keyWidth="8%p"
-    latin:horizontalGap="@fraction/key_horizontal_gap"
-    latin:verticalGap="0px"
     latin:rowHeight="@dimen/popup_key_height"
     >
 </Keyboard>
diff --git a/java/res/xml-sw768dp/kbd_mini_keyboard_template.xml b/java/res/xml-sw768dp/kbd_mini_keyboard_template.xml
index 7d39d1a..1c15a5e 100644
--- a/java/res/xml-sw768dp/kbd_mini_keyboard_template.xml
+++ b/java/res/xml-sw768dp/kbd_mini_keyboard_template.xml
@@ -20,8 +20,6 @@
 
 <Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
     latin:keyWidth="5.0%p"
-    latin:horizontalGap="0px"
-    latin:verticalGap="0px"
     latin:rowHeight="@dimen/popup_key_height"
     >
 </Keyboard>
diff --git a/java/res/xml/kbd_mini_keyboard_template.xml b/java/res/xml/kbd_mini_keyboard_template.xml
index 79db081..d25878b 100644
--- a/java/res/xml/kbd_mini_keyboard_template.xml
+++ b/java/res/xml/kbd_mini_keyboard_template.xml
@@ -20,8 +20,6 @@
 
 <Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
     latin:keyWidth="10%p"
-    latin:horizontalGap="@fraction/key_horizontal_gap"
-    latin:verticalGap="0px"
     latin:rowHeight="@dimen/popup_key_height"
     >
 </Keyboard>
diff --git a/java/res/xml/kbd_suggestions_pane_template.xml b/java/res/xml/kbd_suggestions_pane_template.xml
new file mode 100644
index 0000000..21316e6
--- /dev/null
+++ b/java/res/xml/kbd_suggestions_pane_template.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:keyWidth="10%p"
+    latin:rowHeight="@dimen/more_suggestions_row_height"
+    >
+</Keyboard>
diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboardView.java
index 520466c..f331662 100644
--- a/java/src/com/android/inputmethod/keyboard/MiniKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboardView.java
@@ -88,16 +88,7 @@
         }
     }
 
-    private static final TimerProxy EMPTY_TIMER_PROXY = new TimerProxy() {
-        @Override
-        public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker) {}
-        @Override
-        public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker) {}
-        @Override
-        public void cancelLongPressTimer() {}
-        @Override
-        public void cancelKeyTimers() {}
-    };
+    private static final TimerProxy EMPTY_TIMER_PROXY = new TimerProxy.Adapter();
 
     private final KeyboardActionListener mMiniKeyboardListener =
             new KeyboardActionListener.Adapter() {
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index d4f580d..0314867 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -76,6 +76,17 @@
         public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker);
         public void cancelLongPressTimer();
         public void cancelKeyTimers();
+
+        public static class Adapter implements TimerProxy {
+            @Override
+            public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker) {}
+            @Override
+            public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker) {}
+            @Override
+            public void cancelLongPressTimer() {}
+            @Override
+            public void cancelKeyTimers() {}
+        }
     }
 
     private static KeyboardSwitcher sKeyboardSwitcher;
diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java
index b9ded31..0d355d0 100644
--- a/java/src/com/android/inputmethod/latin/CandidateView.java
+++ b/java/src/com/android/inputmethod/latin/CandidateView.java
@@ -23,6 +23,7 @@
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.os.Message;
+import android.os.SystemClock;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.Spanned;
@@ -34,8 +35,10 @@
 import android.text.style.StyleSpan;
 import android.text.style.UnderlineSpan;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
 import android.view.Gravity;
 import android.view.LayoutInflater;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
@@ -46,6 +49,9 @@
 
 import com.android.inputmethod.compat.FrameLayoutCompatUtils;
 import com.android.inputmethod.compat.LinearLayoutCompatUtils;
+import com.android.inputmethod.keyboard.KeyboardActionListener;
+import com.android.inputmethod.keyboard.MoreKeysPanel;
+import com.android.inputmethod.keyboard.PointerTracker;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 
 import java.util.ArrayList;
@@ -58,16 +64,22 @@
     }
 
     // The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}.
-    private static final int MAX_SUGGESTIONS = 18;
+    public static final int MAX_SUGGESTIONS = 18;
 
     private static final boolean DBG = LatinImeLogger.sDBG;
 
     private final ViewGroup mCandidatesPlacer;
     private final ViewGroup mCandidatesStrip;
+    // TODO: Remove these pane related fields and stuffs.
     private ViewGroup mCandidatesPane;
     private ViewGroup mCandidatesPaneContainer;
     private View mKeyboardView;
 
+    private final View mMoreSuggestionsContainer;
+    private final MoreSuggestionsView mMoreSuggestionsView;
+    private final MoreSuggestions.Builder mMoreSuggestionsBuilder;
+    private final PopupWindow mMoreSuggestionsWindow;
+
     private final ArrayList<TextView> mWords = new ArrayList<TextView>();
     private final ArrayList<TextView> mInfos = new ArrayList<TextView>();
     private final ArrayList<View> mDividers = new ArrayList<View>();
@@ -159,7 +171,7 @@
             mDividerHeight = divider.getMeasuredHeight();
 
             final Resources res = word.getResources();
-            mCandidateStripHeight = res.getDimensionPixelOffset(R.dimen.candidate_strip_height);
+            mCandidateStripHeight = res.getDimensionPixelSize(R.dimen.candidate_strip_height);
         }
     }
 
@@ -257,7 +269,7 @@
         private final int mColorTypedWord;
         private final int mColorAutoCorrect;
         private final int mColorSuggestedCandidate;
-        private final int mCandidateCountInStrip;
+        public final int mCandidateCountInStrip;
         private final float mCenterCandidateWeight;
         private final int mCenterCandidateIndex;
         private final Drawable mMoreCandidateHint;
@@ -449,7 +461,6 @@
                     }
                 }
             }
-
             return countInStrip;
         }
 
@@ -585,6 +596,15 @@
                 mInfos);
         mPaneParams = new SuggestionsPaneParams(mWords, mDividers, mInfos);
         mStripParams.mWordToSaveView.setOnClickListener(this);
+
+        mMoreSuggestionsContainer = inflater.inflate(R.layout.more_suggestions, null);
+        mMoreSuggestionsView = (MoreSuggestionsView)mMoreSuggestionsContainer
+                .findViewById(R.id.more_suggestions_view);
+        mMoreSuggestionsBuilder = new MoreSuggestions.Builder(mMoreSuggestionsView);
+        mMoreSuggestionsWindow = new PopupWindow(context);
+        mMoreSuggestionsWindow.setWindowLayoutMode(
+                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+        mMoreSuggestionsWindow.setBackgroundDrawable(null);
     }
 
     /**
@@ -621,8 +641,6 @@
         final int width = getWidth();
         final int countInStrip = mStripParams.layout(
                 mSuggestions, mCandidatesStrip, mCandidatesPlacer, width);
-        mPaneParams.layout(
-                mSuggestions, mCandidatesPane, countInStrip, mStripParams.getTextColor(), width);
     }
 
     private static CharSequence getDebugInfo(SuggestedWords suggestions, int pos) {
@@ -787,6 +805,7 @@
         mCandidatesStrip.removeAllViews();
         mCandidatesPane.removeAllViews();
         closeCandidatesPane();
+        mMoreSuggestionsWindow.dismiss();
     }
 
     private void hidePreview() {
@@ -823,16 +842,103 @@
         }
     }
 
+    private final KeyboardActionListener mMoreSuggestionsListener =
+            new KeyboardActionListener.Adapter() {
+        @Override
+        public boolean onCustomRequest(int requestCode) {
+            final int index = requestCode;
+            final CharSequence word = mSuggestions.getWord(index);
+            mListener.pickSuggestionManually(index, word);
+            mMoreSuggestionsView.dismissMoreKeysPanel();
+            return true;
+        }
+
+        @Override
+        public void onCancelInput() {
+            mMoreSuggestionsView.dismissMoreKeysPanel();
+        }
+    };
+
+    private final MoreKeysPanel.Controller mMoreSuggestionsController =
+            new MoreKeysPanel.Controller() {
+        @Override
+        public boolean dismissMoreKeysPanel() {
+            if (mMoreSuggestionsWindow.isShowing()) {
+                mMoreSuggestionsWindow.dismiss();
+                return true;
+            }
+            return false;
+        }
+    };
+
     @Override
     public boolean onLongClick(View view) {
-        if (mStripParams.mMoreSuggestionsAvailable) {
-            toggleCandidatesPane();
+        final SuggestionsStripParams params = mStripParams;
+        if (params.mMoreSuggestionsAvailable) {
+            final int stripWidth = getWidth();
+            final View container = mMoreSuggestionsContainer;
+            final int maxWidth = stripWidth - container.getPaddingLeft()
+                    - container.getPaddingRight();
+            final DisplayMetrics dm = getContext().getResources().getDisplayMetrics();
+            // TODO: Revise how we determine the height
+            final int maxHeight = dm.heightPixels - mKeyboardView.getHeight() - getHeight() * 3;
+            final MoreSuggestions.Builder builder = mMoreSuggestionsBuilder;
+            builder.layout(mSuggestions, params.mCandidateCountInStrip, maxWidth, maxHeight);
+            mMoreSuggestionsView.setKeyboard(builder.build());
+            container.measure(
+                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+            final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView;
+            final int pointX = stripWidth / 2;
+            final int pointY = 0;
+            moreKeysPanel.showMoreKeysPanel(
+                    this, mMoreSuggestionsController, pointX, pointY,
+                    mMoreSuggestionsWindow, mMoreSuggestionsListener);
+            // TODO: Should figure out how to select the pointer tracker correctly.
+            final PointerTracker tracker = PointerTracker.getPointerTracker(0, moreKeysPanel);
+            final int translatedX = moreKeysPanel.translateX(tracker.getLastX());
+            final int translatedY = moreKeysPanel.translateY(tracker.getLastY());
+            tracker.onShowMoreKeysPanel(
+                    translatedX, translatedY, SystemClock.uptimeMillis(), moreKeysPanel);
+            view.setPressed(false);
+            // TODO: Should gray out the keyboard here as well?
             return true;
         }
         return false;
     }
 
     @Override
+    public boolean dispatchTouchEvent(MotionEvent me) {
+        if (!mMoreSuggestionsWindow.isShowing()) {
+            return super.dispatchTouchEvent(me);
+        }
+        final int action = me.getAction();
+        final long eventTime = me.getEventTime();
+        final int index = me.getActionIndex();
+        final int id = me.getPointerId(index);
+        final PointerTracker tracker = PointerTracker.getPointerTracker(id, mMoreSuggestionsView);
+        final int x = mMoreSuggestionsView.translateX((int)me.getX(index));
+        final int y = mMoreSuggestionsView.translateY((int)me.getY(index));
+        switch (action) {
+        case MotionEvent.ACTION_DOWN:
+        case MotionEvent.ACTION_POINTER_DOWN:
+            tracker.onDownEvent(x, y, eventTime, mMoreSuggestionsView);
+            break;
+        case MotionEvent.ACTION_UP:
+        case MotionEvent.ACTION_POINTER_UP:
+            tracker.onUpEvent(x, y, eventTime);
+            break;
+        case MotionEvent.ACTION_MOVE:
+            tracker.onMoveEvent(x, y, eventTime);
+            break;
+        case MotionEvent.ACTION_CANCEL:
+            tracker.onCancelEvent(x, y, eventTime);
+            break;
+        }
+        return true;
+    }
+
+    @Override
     public void onClick(View view) {
         if (view == mStripParams.mWordToSaveView) {
             addToDictionary((CharSequence)view.getTag());
diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/MoreSuggestions.java
new file mode 100644
index 0000000..0446fb2
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/MoreSuggestions.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.graphics.Paint;
+import android.text.TextUtils;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
+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.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) {
+        super(params);
+    }
+
+    public static class Builder extends KeyboardBuilder<Builder.MoreSuggestionsParam> {
+        private final MoreSuggestionsView mPaneView;
+        private SuggestedWords mSuggestions;
+        private int mFromPos;
+        private int mToPos;
+
+        public static class MoreSuggestionsParam extends KeyboardParams {
+            private final int[] mWidths = new int[CandidateView.MAX_SUGGESTIONS];
+            private final int[] mRowNumbers = new int[CandidateView.MAX_SUGGESTIONS];
+            private final int[] mColumnOrders = new int[CandidateView.MAX_SUGGESTIONS];
+            private final int[] mNumColumnsInRow = new int[CandidateView.MAX_SUGGESTIONS];
+            private static final int MAX_COLUMNS_IN_ROW = 3;
+            private int mNumRows;
+
+            public int layout(SuggestedWords suggestions, int fromPos, int maxWidth, int maxHeight,
+                    KeyboardView view) {
+                clearKeys();
+                final Paint paint = new Paint();
+                paint.setAntiAlias(true);
+                final int padding = (int) view.getContext().getResources()
+                        .getDimension(R.dimen.more_suggestions_key_horizontal_padding);
+
+                int row = 0;
+                int pos = fromPos, rowStartPos = fromPos;
+                final int size = Math.min(suggestions.size(), CandidateView.MAX_SUGGESTIONS);
+                while (pos < size) {
+                    final CharSequence word = suggestions.getWord(pos);
+                    // TODO: Should take care of text x-scaling.
+                    mWidths[pos] = (int)view.getDefaultLabelWidth(word, paint) + padding;
+                    final int numColumn = pos - rowStartPos + 1;
+                    if (numColumn > MAX_COLUMNS_IN_ROW
+                            || !fitInWidth(rowStartPos, pos + 1, maxWidth / numColumn)) {
+                        if ((row + 1) * mDefaultRowHeight > maxHeight) {
+                            break;
+                        }
+                        mNumColumnsInRow[row] = pos - rowStartPos;
+                        rowStartPos = pos;
+                        row++;
+                    }
+                    mColumnOrders[pos] = pos - rowStartPos;
+                    mRowNumbers[pos] = row;
+                    pos++;
+                }
+                mNumColumnsInRow[row] = pos - rowStartPos;
+                mNumRows = row + 1;
+                mWidth = mOccupiedWidth = calcurateMaxRowWidth(fromPos, pos);
+                mHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap;
+                return pos - fromPos;
+            }
+
+            private boolean fitInWidth(int startPos, int endPos, int width) {
+                for (int pos = startPos; pos < endPos; pos++) {
+                    if (mWidths[pos] > width)
+                        return false;
+                }
+                return true;
+            }
+
+            private int calcurateMaxRowWidth(int startPos, int endPos) {
+                int maxRowWidth = 0;
+                int pos = startPos;
+                for (int row = 0; row < mNumRows; row++) {
+                    final int numColumn = mNumColumnsInRow[row];
+                    int maxKeyWidth = 0;
+                    while (pos < endPos && mRowNumbers[pos] == row) {
+                        maxKeyWidth = Math.max(maxKeyWidth, mWidths[pos]);
+                        pos++;
+                    }
+                    maxRowWidth = Math.max(maxRowWidth, maxKeyWidth * numColumn);
+                }
+                return maxRowWidth;
+            }
+
+            private static final int[][] COLUMN_ORDER_TO_NUMBER = {
+                { 0, },
+                { 1, 0, },
+                { 2, 0, 1},
+            };
+
+            private int getColumnNumber(int pos) {
+                final int columnOrder = mColumnOrders[pos];
+                final int numColumn = mNumColumnsInRow[mRowNumbers[pos]];
+                return COLUMN_ORDER_TO_NUMBER[numColumn - 1][columnOrder];
+            }
+
+            public int getX(int pos) {
+                final int columnNumber = getColumnNumber(pos);
+                return columnNumber * getWidth(pos);
+            }
+
+            public int getY(int pos) {
+                final int row = mRowNumbers[pos];
+                return (mNumRows -1 - row) * mDefaultRowHeight + mTopPadding;
+            }
+
+            public int getWidth(int pos) {
+                final int row = mRowNumbers[pos];
+                final int numColumn = mNumColumnsInRow[row];
+                return mWidth / numColumn;
+            }
+
+            public int getFlags(int pos) {
+                int rowFlags = 0;
+
+                final int row = mRowNumbers[pos];
+                if (row == 0)
+                    rowFlags |= Keyboard.EDGE_BOTTOM;
+                if (row == mNumRows - 1)
+                    rowFlags |= Keyboard.EDGE_TOP;
+
+                final int numColumn = mNumColumnsInRow[row];
+                final int column = getColumnNumber(pos);
+                if (column == 0)
+                    rowFlags |= Keyboard.EDGE_LEFT;
+                if (column == numColumn - 1)
+                    rowFlags |= Keyboard.EDGE_RIGHT;
+
+                return rowFlags;
+            }
+        }
+
+        public Builder(MoreSuggestionsView paneView) {
+            super(paneView.getContext(), new MoreSuggestionsParam());
+            mPaneView = paneView;
+        }
+
+        public Builder layout(SuggestedWords suggestions, int fromPos, int maxWidth,
+                int maxHeight) {
+            final Keyboard keyboard = KeyboardSwitcher.getInstance().getLatinKeyboard();
+            final int xmlId = R.xml.kbd_suggestions_pane_template;
+            load(keyboard.mId.cloneWithNewXml(mResources.getResourceEntryName(xmlId), xmlId));
+            mParams.mVerticalGap = mParams.mTopPadding = keyboard.mVerticalGap / 2;
+
+            final int count = mParams.layout(suggestions, fromPos, maxWidth, maxHeight, mPaneView);
+            mFromPos = fromPos;
+            mToPos = fromPos + count;
+            mSuggestions = suggestions;
+            return this;
+        }
+
+        private static String getDebugInfo(SuggestedWords suggestions, int pos) {
+            if (!DBG) return null;
+            final SuggestedWordInfo wordInfo = suggestions.getInfo(pos);
+            if (wordInfo == null) return null;
+            final String info = wordInfo.getDebugString();
+            if (TextUtils.isEmpty(info)) return null;
+            return info;
+        }
+
+        @Override
+        public MoreSuggestions build() {
+            final MoreSuggestionsParam params = mParams;
+            for (int pos = mFromPos; pos < mToPos; pos++) {
+                final String word = mSuggestions.getWord(pos).toString();
+                final String info = getDebugInfo(mSuggestions, pos);
+                final int index = pos + SUGGESTION_CODE_BASE;
+                final Key key = new Key(
+                        params, word, info, null, index, null, params.getX(pos), params.getY(pos),
+                        params.getWidth(pos), params.mDefaultRowHeight, params.getFlags(pos));
+                params.onAddKey(key);
+            }
+            return new MoreSuggestions(params);
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java
new file mode 100644
index 0000000..8284901
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.PopupWindow;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.KeyDetector;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardActionListener;
+import com.android.inputmethod.keyboard.KeyboardView;
+import com.android.inputmethod.keyboard.MoreKeysPanel;
+import com.android.inputmethod.keyboard.PointerTracker;
+import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
+import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
+
+import java.util.List;
+
+/**
+ * A view that renders a virtual {@link MoreSuggestions}. It handles rendering of keys and detecting
+ * key presses and touch movements.
+ */
+public class MoreSuggestionsView extends KeyboardView implements MoreKeysPanel {
+    private final int[] mCoordinates = new int[2];
+
+    private final KeyDetector mKeyDetector;
+
+    private Controller mController;
+    private KeyboardActionListener mListener;
+    private int mOriginX;
+    private int mOriginY;
+
+    private static class SuggestionsPaneKeyDetector extends KeyDetector {
+        private final int mSlideAllowanceSquare;
+        private final int mSlideAllowanceSquareTop;
+
+        public SuggestionsPaneKeyDetector(float slideAllowance) {
+            super(/* keyHysteresisDistance */0);
+            mSlideAllowanceSquare = (int)(slideAllowance * slideAllowance);
+            // Top slide allowance is slightly longer (sqrt(2) times) than other edges.
+            mSlideAllowanceSquareTop = mSlideAllowanceSquare * 2;
+        }
+
+        @Override
+        public boolean alwaysAllowsSlidingInput() {
+            return true;
+        }
+
+        @Override
+        protected int getMaxNearbyKeys() {
+            // No nearby key will be returned.
+            return 1;
+        }
+
+        @Override
+        public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
+            final List<Key> keys = getKeyboard().mKeys;
+            final int touchX = getTouchX(x);
+            final int touchY = getTouchY(y);
+
+            int nearestIndex = NOT_A_KEY;
+            int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare;
+            final int keyCount = keys.size();
+            for (int index = 0; index < keyCount; index++) {
+                final int dist = keys.get(index).squaredDistanceToEdge(touchX, touchY);
+                if (dist < nearestDist) {
+                    nearestIndex = index;
+                    nearestDist = dist;
+                }
+            }
+
+            if (allCodes != null && nearestIndex != NOT_A_KEY)
+                allCodes[0] = keys.get(nearestIndex).mCode;
+            return nearestIndex;
+        }
+    }
+
+    private static final TimerProxy EMPTY_TIMER_PROXY = new TimerProxy.Adapter();
+
+    private final KeyboardActionListener mSuggestionsPaneListener =
+            new KeyboardActionListener.Adapter() {
+        @Override
+        public void onPress(int primaryCode, boolean withSliding) {
+            mListener.onPress(primaryCode, withSliding);
+        }
+
+        @Override
+        public void onRelease(int primaryCode, boolean withSliding) {
+            mListener.onRelease(primaryCode, withSliding);
+        }
+
+        @Override
+        public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) {
+            mListener.onCustomRequest(primaryCode - MoreSuggestions.SUGGESTION_CODE_BASE);
+        }
+
+        @Override
+        public void onCancelInput() {
+            mListener.onCancelInput();
+        }
+    };
+
+    public MoreSuggestionsView(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.suggestionsPaneViewStyle);
+    }
+
+    public MoreSuggestionsView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        final Resources res = context.getResources();
+        // Override default ProximityKeyDetector.
+        mKeyDetector = new SuggestionsPaneKeyDetector(res.getDimension(
+                R.dimen.more_suggestions_slide_allowance));
+        // Remove gesture detector on suggestions pane
+        setKeyPreviewPopupEnabled(false, 0);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final Keyboard keyboard = getKeyboard();
+        if (keyboard != null) {
+            final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
+            final int height = keyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
+            setMeasuredDimension(width, height);
+        } else {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+    }
+
+    @Override
+    public void setKeyboard(Keyboard keyboard) {
+        super.setKeyboard(keyboard);
+        mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
+                -getPaddingTop() + mVerticalCorrection);
+    }
+
+    @Override
+    public KeyDetector getKeyDetector() {
+        return mKeyDetector;
+    }
+
+    @Override
+    public KeyboardActionListener getKeyboardActionListener() {
+        return mSuggestionsPaneListener;
+    }
+
+    @Override
+    public DrawingProxy getDrawingProxy() {
+        return  this;
+    }
+
+    @Override
+    public TimerProxy getTimerProxy() {
+        return EMPTY_TIMER_PROXY;
+    }
+
+    @Override
+    public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) {
+        // Suggestions pane needs no pop-up key preview displayed, so we pass always false with a
+        // delay of 0. The delay does not matter actually since the popup is not shown anyway.
+        super.setKeyPreviewPopupEnabled(false, 0);
+    }
+
+    @Override
+    public void setShifted(boolean shifted) {
+        // Nothing to do with.
+    }
+
+    @Override
+    public void showMoreKeysPanel(View parentView, Controller controller, int pointX, int pointY,
+            PopupWindow window, KeyboardActionListener listener) {
+        mController = controller;
+        mListener = listener;
+        final View container = (View)getParent();
+        final MoreSuggestions pane = (MoreSuggestions)getKeyboard();
+
+        parentView.getLocationInWindow(mCoordinates);
+        final int paneLeft = pointX - (pane.mOccupiedWidth / 2) + parentView.getPaddingLeft();
+        final int x = wrapUp(Math.max(0, Math.min(paneLeft,
+                parentView.getWidth() - pane.mOccupiedWidth))
+                - container.getPaddingLeft() + mCoordinates[0],
+                container.getMeasuredWidth(), 0, parentView.getWidth());
+        final int y = pointY
+                - (container.getMeasuredHeight() - container.getPaddingBottom())
+                + parentView.getPaddingTop() + mCoordinates[1];
+
+        window.setContentView(container);
+        window.setWidth(container.getMeasuredWidth());
+        window.setHeight(container.getMeasuredHeight());
+        window.showAtLocation(parentView, Gravity.NO_GRAVITY, x, y);
+
+        mOriginX = x + container.getPaddingLeft() - mCoordinates[0];
+        mOriginY = y + container.getPaddingTop() - mCoordinates[1];
+    }
+
+    private static int wrapUp(int x, int width, int left, int right) {
+        if (x < left)
+            return left;
+        if (x + width > right)
+            return right - width;
+        return x;
+    }
+
+    @Override
+    public boolean dismissMoreKeysPanel() {
+        return mController.dismissMoreKeysPanel();
+    }
+
+    @Override
+    public int translateX(int x) {
+        return x - mOriginX;
+    }
+
+    @Override
+    public int translateY(int y) {
+        return y - mOriginY;
+    }
+}