Word suggestion popup added to EditText.

A double tap on a word will now display a list of suggestions based
on the CorrectionSpans that were added by the IME.

This is a first implementation of this feature. It needs some attention from
designers.

Change-Id: If863107681ce82a1639f21315878f830c2991fb7
diff --git a/api/current.xml b/api/current.xml
index 58aa1fd..2ee325b 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -10025,6 +10025,39 @@
  visibility="public"
 >
 </field>
+<field name="textEditSuggestionItemLayout"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843626"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="textEditSuggestionsBottomWindowLayout"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843624"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="textEditSuggestionsTopWindowLayout"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843625"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="textFilterEnabled"
  type="int"
  transient="false"
@@ -10146,6 +10179,17 @@
  visibility="public"
 >
 </field>
+<field name="textSuggestionsWindowStyle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843623"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="textViewStyle"
  type="int"
  transient="false"
@@ -267716,7 +267760,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="arg0" type="T">
+<parameter name="t" type="T">
 </parameter>
 </method>
 </interface>
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9e482b4..f5de16f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -43,7 +43,6 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Message;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -88,6 +87,7 @@
 import android.text.style.UpdateAppearance;
 import android.text.util.Linkify;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
 import android.util.FloatMath;
 import android.util.Log;
 import android.util.TypedValue;
@@ -311,6 +311,10 @@
     private int mTextEditPasteWindowLayout, mTextEditSidePasteWindowLayout;
     private int mTextEditNoPasteWindowLayout, mTextEditSideNoPasteWindowLayout;
 
+    private int mTextEditSuggestionsBottomWindowLayout, mTextEditSuggestionsTopWindowLayout;
+    private int mTextEditSuggestionItemLayout;
+    private SuggestionsPopupWindow mSuggestionsPopupWindow;
+
     private int mCursorDrawableRes;
     private final Drawable[] mCursorDrawable = new Drawable[2];
     private int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2
@@ -779,6 +783,18 @@
                 mTextEditSideNoPasteWindowLayout = a.getResourceId(attr, 0);
                 break;
 
+            case com.android.internal.R.styleable.TextView_textEditSuggestionsBottomWindowLayout:
+                mTextEditSuggestionsBottomWindowLayout = a.getResourceId(attr, 0);
+                break;
+
+            case com.android.internal.R.styleable.TextView_textEditSuggestionsTopWindowLayout:
+                mTextEditSuggestionsTopWindowLayout = a.getResourceId(attr, 0);
+                break;
+
+            case com.android.internal.R.styleable.TextView_textEditSuggestionItemLayout:
+                mTextEditSuggestionItemLayout = a.getResourceId(attr, 0);
+                break;
+
             case com.android.internal.R.styleable.TextView_textIsSelectable:
                 mTextIsSelectable = a.getBoolean(attr, false);
                 break;
@@ -7351,6 +7367,7 @@
                     startSelectionActionMode();
                 } else {
                     stopSelectionActionMode();
+                    hideSuggestions();
                     if (hasInsertionController() && !selectAllGotFocus && mText.length() > 0) {
                         getInsertionController().show();
                     }
@@ -7784,7 +7801,7 @@
                 // Cases where the text ends with a '.' and we select from the end of the line
                 // (right after the dot), or when we select from the space character in "aaa, bbb".
                 continue;
-            }              
+            }
             if (type == Character.SURROGATE) { // Two Character codepoint
                 end = start - 1; // Recheck as a pair when scanning forward
                 continue;
@@ -8229,6 +8246,201 @@
         return ((minOffset >= selectionStart) && (maxOffset < selectionEnd));
     }
 
+    private class SuggestionsPopupWindow implements OnClickListener {
+        private static final int MAX_NUMBER_SUGGESTIONS = 5;
+        private static final long NO_SUGGESTIONS = -1L;
+        private final PopupWindow mContainer;
+        private final ViewGroup[] mSuggestionViews = new ViewGroup[2];
+        private final int[] mSuggestionViewLayouts = new int[] {
+                mTextEditSuggestionsBottomWindowLayout, mTextEditSuggestionsTopWindowLayout};
+
+        public SuggestionsPopupWindow() {
+            mContainer = new PopupWindow(TextView.this.mContext, null,
+                    com.android.internal.R.attr.textSuggestionsWindowStyle);
+            mContainer.setSplitTouchEnabled(true);
+            mContainer.setClippingEnabled(false);
+            mContainer.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
+
+            mContainer.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
+            mContainer.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
+        }
+
+        private ViewGroup getViewGroup(boolean under) {
+            final int viewIndex = under ? 0 : 1;
+            ViewGroup viewGroup = mSuggestionViews[viewIndex];
+
+            if (viewGroup == null) {
+                final int layout = mSuggestionViewLayouts[viewIndex];
+                LayoutInflater inflater = (LayoutInflater) TextView.this.mContext.
+                        getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+                if (inflater == null) {
+                    throw new IllegalArgumentException(
+                            "Unable to create TextEdit suggestion window inflater");
+                }
+
+                View view = inflater.inflate(layout, null);
+
+                if (! (view instanceof ViewGroup)) {
+                    throw new IllegalArgumentException(
+                            "Inflated TextEdit suggestion window is not a ViewGroup: " + view);
+                }
+
+                viewGroup = (ViewGroup) view;
+
+                // Inflate the suggestion items once and for all.
+                for (int i = 0; i < MAX_NUMBER_SUGGESTIONS; i++) {
+                    View childView = inflater.inflate(mTextEditSuggestionItemLayout, viewGroup,
+                            false);
+
+                    if (! (childView instanceof TextView)) {
+                        throw new IllegalArgumentException(
+                               "Inflated TextEdit suggestion item is not a TextView: " + childView);
+                    }
+
+                    viewGroup.addView(childView);
+                    childView.setOnClickListener(this);
+                }
+
+                mSuggestionViews[viewIndex] = viewGroup;
+            }
+
+            return viewGroup;
+        }
+
+        public void show() {
+            if (!(mText instanceof Editable)) return;
+
+            final int pos = TextView.this.getSelectionStart();
+            Spannable spannable = (Spannable)TextView.this.mText;
+            CorrectionSpan[] correctionSpans = spannable.getSpans(pos, pos, CorrectionSpan.class);
+            final int nbSpans = correctionSpans.length;
+
+            ViewGroup viewGroup = getViewGroup(true);
+            mContainer.setContentView(viewGroup);
+
+            int totalNbSuggestions = 0;
+            for (int spanIndex = 0; spanIndex < nbSpans; spanIndex++) {
+                CorrectionSpan correctionSpan = correctionSpans[spanIndex];
+                final int spanStart = spannable.getSpanStart(correctionSpan);
+                final int spanEnd = spannable.getSpanEnd(correctionSpan);
+                final Long spanRange = packRangeInLong(spanStart, spanEnd);
+
+                String[] suggestions = correctionSpan.getSuggestions();
+                int nbSuggestions = suggestions.length;
+                for (int suggestionIndex = 0; suggestionIndex < nbSuggestions; suggestionIndex++) {
+                    TextView textView = (TextView) viewGroup.getChildAt(totalNbSuggestions);
+                    textView.setText(suggestions[suggestionIndex]);
+                    textView.setTag(spanRange);
+
+                    totalNbSuggestions++;
+                    if (totalNbSuggestions == MAX_NUMBER_SUGGESTIONS) {
+                        spanIndex = nbSpans;
+                        break;
+                    }
+                }
+            }
+
+            if (totalNbSuggestions == 0) {
+                // TODO Replace by final text, use a dedicated layout, add a fade out timer...
+                TextView textView = (TextView) viewGroup.getChildAt(0);
+                textView.setText("No suggestions available");
+                textView.setTag(NO_SUGGESTIONS);
+                totalNbSuggestions++;
+            }
+
+            for (int i = 0; i < MAX_NUMBER_SUGGESTIONS; i++) {
+                viewGroup.getChildAt(i).setVisibility(i < totalNbSuggestions ? VISIBLE : GONE);
+            }
+
+            final int size = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
+            viewGroup.measure(size, size);
+
+            positionAtCursor();
+        }
+
+        public void hide() {
+            mContainer.dismiss();
+        }
+
+        @Override
+        public void onClick(View view) {
+            if (view instanceof TextView) {
+                TextView textView = (TextView) view;
+                Long range = ((Long) view.getTag());
+                if (range != NO_SUGGESTIONS) {
+                    final int spanStart = extractRangeStartFromLong(range);
+                    final int spanEnd = extractRangeEndFromLong(range);
+                    ((Editable) mText).replace(spanStart, spanEnd, textView.getText());
+                }
+            }
+            hide();
+        }
+
+        void positionAtCursor() {
+            View contentView = mContainer.getContentView();
+            int width = contentView.getMeasuredWidth();
+            int height = contentView.getMeasuredHeight();
+            final int offset = TextView.this.getSelectionStart();
+            final int line = mLayout.getLineForOffset(offset);
+            final int lineBottom = mLayout.getLineBottom(line);
+            float primaryHorizontal = mLayout.getPrimaryHorizontal(offset);
+
+            final Rect bounds = sCursorControllerTempRect;
+            bounds.left = (int) (primaryHorizontal - width / 2.0f);
+            bounds.top = lineBottom;
+
+            bounds.right = bounds.left + width;
+            bounds.bottom = bounds.top + height;
+
+            convertFromViewportToContentCoordinates(bounds);
+
+            final int[] coords = mTempCoords;
+            TextView.this.getLocationInWindow(coords);
+            coords[0] += bounds.left;
+            coords[1] += bounds.top;
+
+            final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
+            final int screenHeight = displayMetrics.heightPixels;
+
+            // Vertical clipping
+            if (coords[1] + height > screenHeight) {
+                // Try to position above current line instead
+                // TODO use top layout instead, reverse suggestion order,
+                // try full screen vertical down if it still does not fit. TBD with designers.
+
+                // Update dimensions from new view
+                contentView = mContainer.getContentView();
+                width = contentView.getMeasuredWidth();
+                height = contentView.getMeasuredHeight();
+
+                final int lineTop = mLayout.getLineTop(line);
+                final int lineHeight = lineBottom - lineTop;
+                coords[1] -= height + lineHeight;
+            }
+
+            // Horizontal clipping
+            coords[0] = Math.max(0, coords[0]);
+            coords[0] = Math.min(displayMetrics.widthPixels - width, coords[0]);
+
+            mContainer.showAtLocation(TextView.this, Gravity.NO_GRAVITY, coords[0], coords[1]);
+        }
+    }
+
+    void showSuggestions() {
+        if (mSuggestionsPopupWindow == null) {
+            mSuggestionsPopupWindow = new SuggestionsPopupWindow();
+        }
+        hideControllers();
+        mSuggestionsPopupWindow.show();
+    }
+
+    void hideSuggestions() {
+        if (mSuggestionsPopupWindow != null) {
+            mSuggestionsPopupWindow.hide();
+        }
+    }
+
     /**
      * If provided, this ActionMode.Callback will be used to create the ActionMode when text
      * selection is initiated in this View.
@@ -8437,16 +8649,14 @@
         }
     }
 
-    private class PastePopupMenu implements OnClickListener {
+    private class PastePopupWindow implements OnClickListener {
         private final PopupWindow mContainer;
-        private int mPositionX;
-        private int mPositionY;
         private final View[] mPasteViews = new View[4];
         private final int[] mPasteViewLayouts = new int[] { 
                 mTextEditPasteWindowLayout,  mTextEditNoPasteWindowLayout, 
                 mTextEditSidePasteWindowLayout, mTextEditSideNoPasteWindowLayout };
         
-        public PastePopupMenu() {
+        public PastePopupWindow() {
             mContainer = new PopupWindow(TextView.this.mContext, null,
                     com.android.internal.R.attr.textSelectHandleWindowStyle);
             mContainer.setSplitTouchEnabled(true);
@@ -8529,14 +8739,10 @@
 
             convertFromViewportToContentCoordinates(bounds);
 
-            mPositionX = bounds.left;
-            mPositionY = bounds.top;
-
-
             final int[] coords = mTempCoords;
             TextView.this.getLocationInWindow(coords);
-            coords[0] += mPositionX;
-            coords[1] += mPositionY;
+            coords[0] += bounds.left;
+            coords[1] += bounds.top;
 
             final int screenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
             if (coords[1] < 0) {
@@ -8881,7 +9087,7 @@
         void hideAssociatedPopupWindow() {
             // No associated popup window by default
         }
-        
+
         public void onDetached() {
             // Should be overriden to clean possible Runnable
         }
@@ -8891,10 +9097,10 @@
         private static final int DELAY_BEFORE_FADE_OUT = 4000;
         private static final int RECENT_CUT_COPY_DURATION = 15 * 1000; // seconds
 
-        // Used to detect taps on the insertion handle, which will affect the PastePopupMenu
+        // Used to detect taps on the insertion handle, which will affect the PastePopupWindow
         private long mTouchTimer;
         private float mDownPositionX, mDownPositionY;
-        private PastePopupMenu mPastePopupWindow;
+        private PastePopupWindow mPastePopupWindow;
         private Runnable mHider;
         private Runnable mPastePopupShower;
 
@@ -9034,7 +9240,7 @@
         void showAssociatedPopupWindow() {
             if (mPastePopupWindow == null) {
                 // Lazy initialisation: create when actually shown only.
-                mPastePopupWindow = new PastePopupMenu();
+                mPastePopupWindow = new PastePopupWindow();
             }
             mPastePopupWindow.show();
         }
@@ -9245,6 +9451,7 @@
             mEndHandle.show();
 
             hideInsertionPointCursorController();
+            hideSuggestions();
         }
 
         public void hide() {
@@ -9272,7 +9479,7 @@
                             final int deltaY = y - mPreviousTapPositionY;
                             final int distanceSquared = deltaX * deltaX + deltaY * deltaY;
                             if (distanceSquared < mSquaredTouchSlopDistance) {
-                                startSelectionActionMode();
+                                showSuggestions();
                                 mDiscardNextActionUp = true;
                             }
                         }
@@ -9362,6 +9569,7 @@
     private void hideControllers() {
         hideInsertionPointCursorController();
         stopSelectionActionMode();
+        hideSuggestions();
     }
 
     /**
diff --git a/core/res/res/drawable-mdpi/text_edit_suggestions_bottom_window.9.png b/core/res/res/drawable-mdpi/text_edit_suggestions_bottom_window.9.png
new file mode 100644
index 0000000..88be6e1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/text_edit_suggestions_bottom_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_edit_suggestions_top_window.9.png b/core/res/res/drawable-mdpi/text_edit_suggestions_top_window.9.png
new file mode 100644
index 0000000..41886eb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/text_edit_suggestions_top_window.9.png
Binary files differ
diff --git a/core/res/res/layout/text_edit_suggestion_item.xml b/core/res/res/layout/text_edit_suggestion_item.xml
new file mode 100644
index 0000000..a54cad2
--- /dev/null
+++ b/core/res/res/layout/text_edit_suggestion_item.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:paddingLeft="16dip"
+          android:paddingRight="16dip"
+          android:paddingTop="8dip"
+          android:paddingBottom="8dip"
+          android:layout_gravity="center"
+          android:textAppearance="?android:attr/textAppearanceMedium"
+          android:textColor="@android:color/black" />
+
diff --git a/core/res/res/layout/text_edit_suggestions_bottom_window.xml b/core/res/res/layout/text_edit_suggestions_bottom_window.xml
new file mode 100644
index 0000000..588bfbd
--- /dev/null
+++ b/core/res/res/layout/text_edit_suggestions_bottom_window.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:background="@android:drawable/text_edit_suggestions_bottom_window">
+
+</LinearLayout>
diff --git a/core/res/res/layout/text_edit_suggestions_top_window.xml b/core/res/res/layout/text_edit_suggestions_top_window.xml
new file mode 100644
index 0000000..67faa37
--- /dev/null
+++ b/core/res/res/layout/text_edit_suggestions_top_window.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:background="@android:drawable/text_edit_suggestions_top_window">
+
+</LinearLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 77f4e01..2cc57a9 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -525,6 +525,9 @@
         <!-- Reference to a style that will be used for the window containing a text
              selection anchor. -->
         <attr name="textSelectHandleWindowStyle" format="reference" />
+        <!-- Reference to a style that will be used for the window containing a list of possible
+             text suggestions in an EditText. -->
+        <attr name="textSuggestionsWindowStyle" format="reference" />
         <!-- Default ListPopupWindow style. -->
         <attr name="listPopupWindowStyle" format="reference" />
         <!-- Default PopupMenu style. -->
@@ -673,6 +676,15 @@
         <!-- Variation of textEditSidePasteWindowLayout displayed when the clipboard is empty. -->
         <attr name="textEditSideNoPasteWindowLayout" format="reference" />
 
+        <!-- Layout of a the view that is used to create the text suggestions popup window in an
+             EditText. This window will be displayed below the text line. -->
+        <attr name="textEditSuggestionsBottomWindowLayout" format="reference" />
+        <!-- Same as textEditSuggestionsBottomWindowLayout, but used when the popup is displayed
+             above the current line of text instead of below. -->
+        <attr name="textEditSuggestionsTopWindowLayout" format="reference" />
+        <!-- Layout of the TextView item that will populate the suggestion popup window. -->
+        <attr name="textEditSuggestionItemLayout" format="reference" />
+
         <!-- Theme to use for dialogs spawned from this theme. -->
         <attr name="dialogTheme" format="reference" />
         <!-- Window decor layout to use in dialog mode with icons -->
@@ -2810,6 +2822,16 @@
         <!-- Variation of textEditSidePasteWindowLayout displayed when the clipboard is empty. -->
         <attr name="textEditSideNoPasteWindowLayout" />
 
+        <!-- Layout of a the view that is used to create the text suggestions popup window in an
+             EditText. This window will be displayed below the text line. -->
+        <attr name="textEditSuggestionsBottomWindowLayout" />
+        <!-- Same as textEditSuggestionsBottomWindowLayout, but used when the popup is displayed
+             above the current line of text instead of below. -->
+        <attr name="textEditSuggestionsTopWindowLayout" />
+        <!-- Layout of the TextView item that will populate the suggestion popup window. -->
+        <attr name="textEditSuggestionItemLayout" />
+
+
         <!-- Reference to a drawable that will be drawn under the insertion cursor. -->
         <attr name="textCursorDrawable" />
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 4109ae1..d5c374d 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1659,4 +1659,9 @@
 
   <public type="style" name="Theme.Holo.Light.NoActionBar" />
 
+  <public type="attr" name="textSuggestionsWindowStyle" />
+  <public type="attr" name="textEditSuggestionsBottomWindowLayout" />
+  <public type="attr" name="textEditSuggestionsTopWindowLayout" />
+  <public type="attr" name="textEditSuggestionItemLayout" />
+
 </resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index f7d3c3f..1a9558d 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -426,6 +426,9 @@
         <item name="android:textEditNoPasteWindowLayout">?android:attr/textEditNoPasteWindowLayout</item>
         <item name="android:textEditSidePasteWindowLayout">?android:attr/textEditSidePasteWindowLayout</item>
         <item name="android:textEditSideNoPasteWindowLayout">?android:attr/textEditSideNoPasteWindowLayout</item>
+        <item name="android:textEditSuggestionsBottomWindowLayout">?android:attr/textEditSuggestionsBottomWindowLayout</item>
+        <item name="android:textEditSuggestionsTopWindowLayout">?android:attr/textEditSuggestionsTopWindowLayout</item>
+        <item name="android:textEditSuggestionItemLayout">?android:attr/textEditSuggestionItemLayout</item>
         <item name="android:textCursorDrawable">?android:attr/textCursorDrawable</item>
     </style>
     
@@ -1047,6 +1050,17 @@
         <item name="windowExitAnimation">@android:anim/fade_out</item>
     </style>
 
+    <!-- Style for the popup window that contains text suggestions. -->
+    <style name="Widget.TextSuggestions">
+        <item name="android:popupAnimationStyle">@android:style/Animation.TextSuggestions</item>
+    </style>
+
+    <!-- Animation effects when showing/hiding the text suggestions popup window. -->
+    <style name="Animation.TextSuggestions">
+        <item name="windowEnterAnimation">@android:anim/fade_in</item>
+        <item name="windowExitAnimation">@android:anim/fade_out</item>
+    </style>
+
     <style name="Widget.ActionBar">
         <item name="android:background">@android:drawable/action_bar_background</item>
         <item name="android:displayOptions">useLogo|showHome|showTitle</item>
@@ -1460,6 +1474,9 @@
     <style name="Widget.Holo.TextSelectHandle" parent="Widget.TextSelectHandle">
     </style>
 
+    <style name="Widget.Holo.TextSuggestions" parent="Widget.TextSuggestions">
+    </style>
+
     <style name="Widget.Holo.AbsListView" parent="Widget.AbsListView">
     </style>
 
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index b127747..41085e9 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -180,6 +180,10 @@
         <item name="textEditNoPasteWindowLayout">@android:layout/text_edit_no_paste_window</item>
         <item name="textEditSidePasteWindowLayout">@android:layout/text_edit_side_paste_window</item>
         <item name="textEditSideNoPasteWindowLayout">@android:layout/text_edit_side_no_paste_window</item>
+        <item name="textSuggestionsWindowStyle">@android:style/Widget.TextSuggestions</item>
+        <item name="textEditSuggestionsBottomWindowLayout">@android:layout/text_edit_suggestions_bottom_window</item>
+        <item name="textEditSuggestionsTopWindowLayout">@android:layout/text_edit_suggestions_top_window</item>
+        <item name="textEditSuggestionItemLayout">@android:layout/text_edit_suggestion_item</item>
         <item name="textCursorDrawable">@null</item>
 
         <!-- Widget styles -->
@@ -917,6 +921,7 @@
         <item name="textSelectHandleRight">@android:drawable/text_select_handle_right</item>
         <item name="textSelectHandle">@android:drawable/text_select_handle_middle</item>
         <item name="textSelectHandleWindowStyle">@android:style/Widget.Holo.TextSelectHandle</item>
+        <item name="textSuggestionsWindowStyle">@android:style/Widget.Holo.TextSuggestions</item>
         <item name="textCursorDrawable">@android:drawable/text_cursor_holo_dark</item>
 
         <!-- Widget styles -->
@@ -1200,6 +1205,7 @@
         <item name="textSelectHandleRight">@android:drawable/text_select_handle_right</item>
         <item name="textSelectHandle">@android:drawable/text_select_handle_middle</item>
         <item name="textSelectHandleWindowStyle">@android:style/Widget.Holo.TextSelectHandle</item>
+        <item name="textSuggestionsWindowStyle">@android:style/Widget.Holo.TextSuggestions</item>
         <item name="textCursorDrawable">@android:drawable/text_cursor_holo_light</item>
 
         <!-- Widget styles -->