Added ListPopupWindow. Refactored AutoCompleteTextView and Spinner
(optionally) to use it. Added associated styles.

ListPopupWindow allows apps to present a popup window of options to
the user that will correctly dodge the IME if needed.

Change-Id: I509c6c45036856daab686a6edeb7a9de1e72eb0a
diff --git a/api/current.xml b/api/current.xml
index d79fab6..3c2fccb 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -16883,6 +16883,17 @@
  visibility="public"
 >
 </field>
+<field name="Widget_Spinner_DropDown"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973970"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="Widget_TabWidget"
  type="int"
  transient="false"
@@ -211630,6 +211641,665 @@
 </parameter>
 </method>
 </interface>
+<class name="ListPopupWindow"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ListPopupWindow"
+ type="android.widget.ListPopupWindow"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<constructor name="ListPopupWindow"
+ type="android.widget.ListPopupWindow"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+</constructor>
+<constructor name="ListPopupWindow"
+ type="android.widget.ListPopupWindow"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+<parameter name="defStyleAttr" type="int">
+</parameter>
+</constructor>
+<constructor name="ListPopupWindow"
+ type="android.widget.ListPopupWindow"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+<parameter name="defStyleAttr" type="int">
+</parameter>
+<parameter name="defStyleRes" type="int">
+</parameter>
+</constructor>
+<method name="clearListSelection"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="dismiss"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getAnchorView"
+ return="android.view.View"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getAnimationStyle"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getBackground"
+ return="android.graphics.drawable.Drawable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getHeight"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getHorizontalOffset"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getInputMethodMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getListView"
+ return="android.widget.ListView"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPromptPosition"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSelectedItem"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSelectedItemId"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSelectedItemPosition"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSelectedView"
+ return="android.view.View"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSoftInputMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getVerticalOffset"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getWidth"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isInputMethodNotNeeded"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isModal"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isShowing"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onKeyDown"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="keyCode" type="int">
+</parameter>
+<parameter name="event" type="android.view.KeyEvent">
+</parameter>
+</method>
+<method name="onKeyPreIme"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="keyCode" type="int">
+</parameter>
+<parameter name="event" type="android.view.KeyEvent">
+</parameter>
+</method>
+<method name="onKeyUp"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="keyCode" type="int">
+</parameter>
+<parameter name="event" type="android.view.KeyEvent">
+</parameter>
+</method>
+<method name="performItemClick"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="postShow"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setAdapter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.widget.ListAdapter">
+</parameter>
+</method>
+<method name="setAnchorView"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="anchor" type="android.view.View">
+</parameter>
+</method>
+<method name="setAnimationStyle"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="animationStyle" type="int">
+</parameter>
+</method>
+<method name="setBackgroundDrawable"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="d" type="android.graphics.drawable.Drawable">
+</parameter>
+</method>
+<method name="setHeight"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="height" type="int">
+</parameter>
+</method>
+<method name="setHorizontalOffset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="offset" type="int">
+</parameter>
+</method>
+<method name="setInputMethodMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mode" type="int">
+</parameter>
+</method>
+<method name="setListSelector"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="selector" type="android.graphics.drawable.Drawable">
+</parameter>
+</method>
+<method name="setModal"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="modal" type="boolean">
+</parameter>
+</method>
+<method name="setOnItemClickListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="clickListener" type="android.widget.AdapterView.OnItemClickListener">
+</parameter>
+</method>
+<method name="setOnItemSelectedListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="selectedListener" type="android.widget.AdapterView.OnItemSelectedListener">
+</parameter>
+</method>
+<method name="setPromptPosition"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="setPromptView"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="prompt" type="android.view.View">
+</parameter>
+</method>
+<method name="setSelection"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="setSoftInputMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mode" type="int">
+</parameter>
+</method>
+<method name="setVerticalOffset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="offset" type="int">
+</parameter>
+</method>
+<method name="setWidth"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="width" type="int">
+</parameter>
+</method>
+<method name="show"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<field name="INPUT_METHOD_FROM_FOCUSABLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="INPUT_METHOD_NEEDED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="INPUT_METHOD_NOT_NEEDED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MATCH_PARENT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="POSITION_PROMPT_ABOVE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="POSITION_PROMPT_BELOW"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WRAP_CONTENT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="ListView"
  extends="android.widget.AbsListView"
  abstract="false"
@@ -212670,6 +213340,22 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+<parameter name="defStyleAttr" type="int">
+</parameter>
+<parameter name="defStyleRes" type="int">
+</parameter>
+</constructor>
+<constructor name="PopupWindow"
+ type="android.widget.PopupWindow"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
 </constructor>
 <constructor name="PopupWindow"
  type="android.widget.PopupWindow"
@@ -217256,6 +217942,28 @@
 <parameter name="promptId" type="int">
 </parameter>
 </method>
+<field name="MODE_DIALOG"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MODE_DROPDOWN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <interface name="SpinnerAdapter"
  abstract="true"
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 8611901..34aef99 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -29,13 +29,12 @@
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
 
 import com.android.internal.R;
 
@@ -90,45 +89,21 @@
     static final boolean DEBUG = false;
     static final String TAG = "AutoCompleteTextView";
 
-    private static final int HINT_VIEW_ID = 0x17;
-
-    /**
-     * This value controls the length of time that the user
-     * must leave a pointer down without scrolling to expand
-     * the autocomplete dropdown list to cover the IME.
-     */
-    private static final int EXPAND_LIST_TIMEOUT = 250;
-
     private CharSequence mHintText;
+    private TextView mHintView;
     private int mHintResource;
 
     private ListAdapter mAdapter;
     private Filter mFilter;
     private int mThreshold;
 
-    private PopupWindow mPopup;
-    private DropDownListView mDropDownList;
-    private int mDropDownVerticalOffset;
-    private int mDropDownHorizontalOffset;
+    private ListPopupWindow mPopup;
     private int mDropDownAnchorId;
-    private View mDropDownAnchorView;  // view is retrieved lazily from id once needed
-    private int mDropDownWidth;
-    private int mDropDownHeight;
-    private final Rect mTempRect = new Rect();
-
-    private Drawable mDropDownListHighlight;
 
     private AdapterView.OnItemClickListener mItemClickListener;
     private AdapterView.OnItemSelectedListener mItemSelectedListener;
 
-    private final DropDownItemClickListener mDropDownItemClickListener =
-            new DropDownItemClickListener();
-
-    private boolean mDropDownAlwaysVisible = false;
-
     private boolean mDropDownDismissedOnCompletion = true;
-    
-    private boolean mForceIgnoreOutsideTouch = false;
 
     private int mLastKeyCode = KeyEvent.KEYCODE_UNKNOWN;
     private boolean mOpenBefore;
@@ -137,10 +112,6 @@
 
     private boolean mBlockCompletion;
 
-    private ListSelectorHider mHideSelector;
-    private Runnable mShowDropDownRunnable;
-    private Runnable mResizePopupRunnable = new ResizePopupRunnable();
-
     private PassThroughClickListener mPassThroughClickListener;
     private PopupDataSetObserver mObserver;
 
@@ -155,9 +126,10 @@
     public AutoCompleteTextView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
-        mPopup = new PopupWindow(context, attrs,
+        mPopup = new ListPopupWindow(context, attrs,
                 com.android.internal.R.attr.autoCompleteTextViewStyle);
         mPopup.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
+        mPopup.setPromptPosition(ListPopupWindow.POSITION_PROMPT_BELOW);
 
         TypedArray a =
             context.obtainStyledAttributes(
@@ -166,14 +138,11 @@
         mThreshold = a.getInt(
                 R.styleable.AutoCompleteTextView_completionThreshold, 2);
 
-        mHintText = a.getText(R.styleable.AutoCompleteTextView_completionHint);
-
-        mDropDownListHighlight = a.getDrawable(
-                R.styleable.AutoCompleteTextView_dropDownSelector);
-        mDropDownVerticalOffset = (int)
-                a.getDimension(R.styleable.AutoCompleteTextView_dropDownVerticalOffset, 0.0f);
-        mDropDownHorizontalOffset = (int)
-                a.getDimension(R.styleable.AutoCompleteTextView_dropDownHorizontalOffset, 0.0f);
+        mPopup.setListSelector(a.getDrawable(R.styleable.AutoCompleteTextView_dropDownSelector));
+        mPopup.setVerticalOffset((int)
+                a.getDimension(R.styleable.AutoCompleteTextView_dropDownVerticalOffset, 0.0f));
+        mPopup.setHorizontalOffset((int)
+                a.getDimension(R.styleable.AutoCompleteTextView_dropDownHorizontalOffset, 0.0f));
         
         // Get the anchor's id now, but the view won't be ready, so wait to actually get the
         // view and store it in mDropDownAnchorView lazily in getDropDownAnchorView later.
@@ -184,13 +153,18 @@
         
         // For dropdown width, the developer can specify a specific width, or MATCH_PARENT
         // (for full screen width) or WRAP_CONTENT (to match the width of the anchored view).
-        mDropDownWidth = a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownWidth,
-                ViewGroup.LayoutParams.WRAP_CONTENT);
-        mDropDownHeight = a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownHeight,
-                ViewGroup.LayoutParams.WRAP_CONTENT);
+        mPopup.setWidth(a.getLayoutDimension(
+                R.styleable.AutoCompleteTextView_dropDownWidth,
+                ViewGroup.LayoutParams.WRAP_CONTENT));
+        mPopup.setHeight(a.getLayoutDimension(
+                R.styleable.AutoCompleteTextView_dropDownHeight,
+                ViewGroup.LayoutParams.WRAP_CONTENT));
 
         mHintResource = a.getResourceId(R.styleable.AutoCompleteTextView_completionHintView,
                 R.layout.simple_dropdown_hint);
+        
+        mPopup.setOnItemClickListener(new DropDownItemClickListener());
+        setCompletionHint(a.getText(R.styleable.AutoCompleteTextView_completionHint));
 
         // Always turn on the auto complete input type flag, since it
         // makes no sense to use this widget without it.
@@ -238,6 +212,20 @@
      */
     public void setCompletionHint(CharSequence hint) {
         mHintText = hint;
+        if (hint != null) {
+            if (mHintView == null) {
+                final TextView hintView = (TextView) LayoutInflater.from(getContext()).inflate(
+                        mHintResource, null).findViewById(com.android.internal.R.id.text1);
+                hintView.setText(mHintText);
+                mHintView = hintView;
+                mPopup.setPromptView(hintView);
+            } else {
+                mHintView.setText(hint);
+            }
+        } else {
+            mPopup.setPromptView(null);
+            mHintView = null;
+        }
     }
     
     /**
@@ -250,7 +238,7 @@
      * @attr ref android.R.styleable#AutoCompleteTextView_dropDownWidth
      */
     public int getDropDownWidth() {
-        return mDropDownWidth;
+        return mPopup.getWidth();
     }
     
     /**
@@ -263,7 +251,7 @@
      * @attr ref android.R.styleable#AutoCompleteTextView_dropDownWidth
      */
     public void setDropDownWidth(int width) {
-        mDropDownWidth = width;
+        mPopup.setWidth(width);
     }
 
     /**
@@ -277,7 +265,7 @@
      * @attr ref android.R.styleable#AutoCompleteTextView_dropDownHeight
      */
     public int getDropDownHeight() {
-        return mDropDownHeight;
+        return mPopup.getHeight();
     }
 
     /**
@@ -291,7 +279,7 @@
      * @attr ref android.R.styleable#AutoCompleteTextView_dropDownHeight
      */
     public void setDropDownHeight(int height) {
-        mDropDownHeight = height;
+        mPopup.setHeight(height);
     }
     
     /**
@@ -316,7 +304,7 @@
      */
     public void setDropDownAnchor(int id) {
         mDropDownAnchorId = id;
-        mDropDownAnchorView = null;
+        mPopup.setAnchorView(null);
     }
     
     /**
@@ -358,7 +346,7 @@
      * @param offset the vertical offset
      */
     public void setDropDownVerticalOffset(int offset) {
-        mDropDownVerticalOffset = offset;
+        mPopup.setVerticalOffset(offset);
     }
     
     /**
@@ -367,7 +355,7 @@
      * @return the vertical offset
      */
     public int getDropDownVerticalOffset() {
-        return mDropDownVerticalOffset;
+        return mPopup.getVerticalOffset();
     }
     
     /**
@@ -376,7 +364,7 @@
      * @param offset the horizontal offset
      */
     public void setDropDownHorizontalOffset(int offset) {
-        mDropDownHorizontalOffset = offset;
+        mPopup.setHorizontalOffset(offset);
     }
     
     /**
@@ -385,7 +373,7 @@
      * @return the horizontal offset
      */
     public int getDropDownHorizontalOffset() {
-        return mDropDownHorizontalOffset;
+        return mPopup.getHorizontalOffset();
     }
 
      /**
@@ -422,7 +410,7 @@
      * @hide Pending API council approval
      */
     public boolean isDropDownAlwaysVisible() {
-        return mDropDownAlwaysVisible;
+        return mPopup.isDropDownAlwaysVisible();
     }
 
     /**
@@ -439,7 +427,7 @@
      * @hide Pending API council approval
      */
     public void setDropDownAlwaysVisible(boolean dropDownAlwaysVisible) {
-        mDropDownAlwaysVisible = dropDownAlwaysVisible;
+        mPopup.setDropDownAlwaysVisible(dropDownAlwaysVisible);
     }
    
     /**
@@ -606,15 +594,13 @@
             mFilter = null;
         }
 
-        if (mDropDownList != null) {
-            mDropDownList.setAdapter(mAdapter);
-        }
+        mPopup.setAdapter(mAdapter);
     }
 
     @Override
     public boolean onKeyPreIme(int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_BACK && isPopupShowing()
-                && !mDropDownAlwaysVisible) {
+                && !mPopup.isDropDownAlwaysVisible()) {
             // special case for the back key, we do not even try to send it
             // to the drop down list but instead, consume it immediately
             if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
@@ -633,18 +619,16 @@
 
     @Override
     public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (isPopupShowing() && mDropDownList.getSelectedItemPosition() >= 0) {
-            boolean consumed = mDropDownList.onKeyUp(keyCode, event);
-            if (consumed) {
-                switch (keyCode) {
-                    // if the list accepts the key events and the key event
-                    // was a click, the text view gets the selected item
-                    // from the drop down as its content
-                    case KeyEvent.KEYCODE_ENTER:
-                    case KeyEvent.KEYCODE_DPAD_CENTER:
-                        performCompletion();
-                        return true;
-                }
+        boolean consumed = mPopup.onKeyUp(keyCode, event);
+        if (consumed) {
+            switch (keyCode) {
+            // if the list accepts the key events and the key event
+            // was a click, the text view gets the selected item
+            // from the drop down as its content
+            case KeyEvent.KEYCODE_ENTER:
+            case KeyEvent.KEYCODE_DPAD_CENTER:
+                performCompletion();
+                return true;
             }
         }
         return super.onKeyUp(keyCode, event);
@@ -652,87 +636,11 @@
 
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
-        // when the drop down is shown, we drive it directly
-        if (isPopupShowing()) {
-            // the key events are forwarded to the list in the drop down view
-            // note that ListView handles space but we don't want that to happen
-            // also if selection is not currently in the drop down, then don't
-            // let center or enter presses go there since that would cause it
-            // to select one of its items
-            if (keyCode != KeyEvent.KEYCODE_SPACE
-                    && (mDropDownList.getSelectedItemPosition() >= 0
-                            || (keyCode != KeyEvent.KEYCODE_ENTER
-                                    && keyCode != KeyEvent.KEYCODE_DPAD_CENTER))) {
-                int curIndex = mDropDownList.getSelectedItemPosition();
-                boolean consumed;
-
-                final boolean below = !mPopup.isAboveAnchor();
-
-                final ListAdapter adapter = mAdapter;
-                
-                boolean allEnabled;
-                int firstItem = Integer.MAX_VALUE;
-                int lastItem = Integer.MIN_VALUE;
-
-                if (adapter != null) {
-                    allEnabled = adapter.areAllItemsEnabled();
-                    firstItem = allEnabled ? 0 :
-                            mDropDownList.lookForSelectablePosition(0, true);
-                    lastItem = allEnabled ? adapter.getCount() - 1 :
-                            mDropDownList.lookForSelectablePosition(adapter.getCount() - 1, false);                    
-                }
-                
-                if ((below && keyCode == KeyEvent.KEYCODE_DPAD_UP && curIndex <= firstItem) ||
-                        (!below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN && curIndex >= lastItem)) {
-                    // When the selection is at the top, we block the key
-                    // event to prevent focus from moving.
-                    clearListSelection();
-                    mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
-                    showDropDown();
-                    return true;
-                } else {
-                    // WARNING: Please read the comment where mListSelectionHidden
-                    //          is declared
-                    mDropDownList.mListSelectionHidden = false;
-                }
-
-                consumed = mDropDownList.onKeyDown(keyCode, event);
-                if (DEBUG) Log.v(TAG, "Key down: code=" + keyCode + " list consumed=" + consumed);
-
-                if (consumed) {
-                    // If it handled the key event, then the user is
-                    // navigating in the list, so we should put it in front.
-                    mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
-                    // Here's a little trick we need to do to make sure that
-                    // the list view is actually showing its focus indicator,
-                    // by ensuring it has focus and getting its window out
-                    // of touch mode.
-                    mDropDownList.requestFocusFromTouch();
-                    showDropDown();
-
-                    switch (keyCode) {
-                        // avoid passing the focus from the text view to the
-                        // next component
-                        case KeyEvent.KEYCODE_ENTER:
-                        case KeyEvent.KEYCODE_DPAD_CENTER:
-                        case KeyEvent.KEYCODE_DPAD_DOWN:
-                        case KeyEvent.KEYCODE_DPAD_UP:
-                            return true;
-                    }
-                } else {
-                    if (below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
-                        // when the selection is at the bottom, we block the
-                        // event to avoid going to the next focusable widget
-                        if (curIndex == lastItem) {
-                            return true;
-                        }
-                    } else if (!below && keyCode == KeyEvent.KEYCODE_DPAD_UP &&
-                            curIndex == firstItem) {
-                        return true;
-                    }
-                }
-            }
-        } else {
+        if (mPopup.onKeyDown(keyCode, event)) {
+            return true;
+        }
+        
+        if (!isPopupShowing()) {
             switch(keyCode) {
             case KeyEvent.KEYCODE_DPAD_DOWN:
                 performValidation();
@@ -743,7 +651,7 @@
         boolean handled = super.onKeyDown(keyCode, event);
         mLastKeyCode = KeyEvent.KEYCODE_UNKNOWN;
 
-        if (handled && isPopupShowing() && mDropDownList != null) {
+        if (handled && isPopupShowing()) {
             clearListSelection();
         }
 
@@ -804,11 +712,12 @@
         if (enoughToFilter()) {
             if (mFilter != null) {
                 performFiltering(getText(), mLastKeyCode);
+                buildImeCompletions();
             }
         } else {
             // drop down is automatically dismissed when enough characters
             // are deleted from the text view
-            if (!mDropDownAlwaysVisible) dismissDropDown();
+            if (!mPopup.isDropDownAlwaysVisible()) dismissDropDown();
             if (mFilter != null) {
                 mFilter.filter(null);
             }
@@ -841,13 +750,7 @@
      * it back.
      */
     public void clearListSelection() {
-        final DropDownListView list = mDropDownList;
-        if (list != null) {
-            // WARNING: Please read the comment where mListSelectionHidden is declared
-            list.mListSelectionHidden = true;
-            list.hideSelector();
-            list.requestLayout();
-        }
+        mPopup.clearListSelection();
     }
     
     /**
@@ -856,11 +759,7 @@
      * @param position The position to move the selector to.
      */
     public void setListSelection(int position) {
-        if (mPopup.isShowing() && (mDropDownList != null)) {
-            mDropDownList.mListSelectionHidden = false;
-            mDropDownList.setSelection(position);
-            // ListView.setSelection() will call requestLayout()
-        }
+        mPopup.setSelection(position);
     }
 
     /**
@@ -874,10 +773,7 @@
      * @see ListView#getSelectedItemPosition()
      */
     public int getListSelection() {
-        if (mPopup.isShowing() && (mDropDownList != null)) {
-            return mDropDownList.getSelectedItemPosition();
-        }
-        return ListView.INVALID_POSITION;
+        return mPopup.getSelectedItemPosition();
     }
 
     /**
@@ -911,13 +807,7 @@
             replaceText(completion.getText());
             mBlockCompletion = false;
 
-            if (mItemClickListener != null) {
-                final DropDownListView list = mDropDownList;
-                final int position = completion.getPosition();
-                mItemClickListener.onItemClick(list,
-                        list.getChildAt(position - list.getFirstVisiblePosition()),
-                        position, completion.getId());
-            }
+            mPopup.performItemClick(completion.getPosition());
         }
     }
 
@@ -925,7 +815,7 @@
         if (isPopupShowing()) {
             Object selectedItem;
             if (position < 0) {
-                selectedItem = mDropDownList.getSelectedItem();
+                selectedItem = mPopup.getSelectedItem();
             } else {
                 selectedItem = mAdapter.getItem(position);
             }
@@ -939,18 +829,18 @@
             mBlockCompletion = false;            
 
             if (mItemClickListener != null) {
-                final DropDownListView list = mDropDownList;
+                final ListPopupWindow list = mPopup;
 
                 if (selectedView == null || position < 0) {
                     selectedView = list.getSelectedView();
                     position = list.getSelectedItemPosition();
                     id = list.getSelectedItemId();
                 }
-                mItemClickListener.onItemClick(list, selectedView, position, id);
+                mItemClickListener.onItemClick(list.getListView(), selectedView, position, id);
             }
         }
 
-        if (mDropDownDismissedOnCompletion && !mDropDownAlwaysVisible) {
+        if (mDropDownDismissedOnCompletion && !mPopup.isDropDownAlwaysVisible()) {
             dismissDropDown();
         }
     }
@@ -1000,7 +890,6 @@
     /** {@inheritDoc} */
     public void onFilterComplete(int count) {
         updateDropDownForFilter(count);
-
     }
 
     private void updateDropDownForFilter(int count) {
@@ -1014,11 +903,12 @@
          * to filter.
          */
 
-        if ((count > 0 || mDropDownAlwaysVisible) && enoughToFilter()) {
+        final boolean dropDownAlwaysVisible = mPopup.isDropDownAlwaysVisible();
+        if ((count > 0 || dropDownAlwaysVisible) && enoughToFilter()) {
             if (hasFocus() && hasWindowFocus()) {
                 showDropDown();
             }
-        } else if (!mDropDownAlwaysVisible) {
+        } else if (!dropDownAlwaysVisible) {
             dismissDropDown();
         }
     }
@@ -1026,7 +916,7 @@
     @Override
     public void onWindowFocusChanged(boolean hasWindowFocus) {
         super.onWindowFocusChanged(hasWindowFocus);
-        if (!hasWindowFocus && !mDropDownAlwaysVisible) {
+        if (!hasWindowFocus && !mPopup.isDropDownAlwaysVisible()) {
             dismissDropDown();
         }
     }
@@ -1036,7 +926,7 @@
         super.onDisplayHint(hint);
         switch (hint) {
             case INVISIBLE:
-                if (!mDropDownAlwaysVisible) {
+                if (!mPopup.isDropDownAlwaysVisible()) {
                     dismissDropDown();
                 }
                 break;
@@ -1050,7 +940,7 @@
         if (!focused) {
             performValidation();
         }
-        if (!focused && !mDropDownAlwaysVisible) {
+        if (!focused && !mPopup.isDropDownAlwaysVisible()) {
             dismissDropDown();
         }
     }
@@ -1075,8 +965,6 @@
             imm.displayCompletions(this, null);
         }
         mPopup.dismiss();
-        mPopup.setContentView(null);
-        mDropDownList = null;
     }
 
     @Override
@@ -1089,18 +977,6 @@
 
         return result;
     }
-    
-    /**
-     * <p>Used for lazy instantiation of the anchor view from the id we have. If the value of
-     * the id is NO_ID or we can't find a view for the given id, we return this TextView as
-     * the default anchoring point.</p>
-     */
-    private View getDropDownAnchorView() {
-        if (mDropDownAnchorView == null && mDropDownAnchorId != View.NO_ID) {
-            mDropDownAnchorView = getRootView().findViewById(mDropDownAnchorId);
-        }
-        return mDropDownAnchorView == null ? this : mDropDownAnchorView;
-    }
 
     /**
      * Issues a runnable to show the dropdown as soon as possible.
@@ -1108,7 +984,7 @@
      * @hide internal used only by SearchDialog
      */
     public void showDropDownAfterLayout() {
-        post(mShowDropDownRunnable);
+        mPopup.postShow();
     }
     
     /**
@@ -1119,7 +995,7 @@
      */
     public void ensureImeVisible(boolean visible) {
         mPopup.setInputMethodMode(visible
-                ? PopupWindow.INPUT_METHOD_NEEDED : PopupWindow.INPUT_METHOD_NOT_NEEDED);
+                ? ListPopupWindow.INPUT_METHOD_NEEDED : ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
         showDropDown();
     }
 
@@ -1127,89 +1003,21 @@
      * @hide internal used only here and SearchDialog
      */
     public boolean isInputMethodNotNeeded() {
-        return mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
+        return mPopup.getInputMethodMode() == ListPopupWindow.INPUT_METHOD_NOT_NEEDED;
     }
 
     /**
      * <p>Displays the drop down on screen.</p>
      */
     public void showDropDown() {
-        int height = buildDropDown();
-
-        int widthSpec = 0;
-        int heightSpec = 0;
-
-        boolean noInputMethod = isInputMethodNotNeeded();
-
-        if (mPopup.isShowing()) {
-            if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
-                // The call to PopupWindow's update method below can accept -1 for any
-                // value you do not want to update.
-                widthSpec = -1;
-            } else if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
-                widthSpec = getDropDownAnchorView().getWidth();
+        if (mPopup.getAnchorView() == null) {
+            if (mDropDownAnchorId != View.NO_ID) {
+                mPopup.setAnchorView(getRootView().findViewById(mDropDownAnchorId));
             } else {
-                widthSpec = mDropDownWidth;
+                mPopup.setAnchorView(this);
             }
-
-            if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
-                // The call to PopupWindow's update method below can accept -1 for any
-                // value you do not want to update.
-                heightSpec = noInputMethod ? height : ViewGroup.LayoutParams.MATCH_PARENT;
-                if (noInputMethod) {
-                    mPopup.setWindowLayoutMode(
-                            mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
-                                    ViewGroup.LayoutParams.MATCH_PARENT : 0, 0);
-                } else {
-                    mPopup.setWindowLayoutMode(
-                            mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
-                                    ViewGroup.LayoutParams.MATCH_PARENT : 0,
-                            ViewGroup.LayoutParams.MATCH_PARENT);
-                }
-            } else if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
-                heightSpec = height;
-            } else {
-                heightSpec = mDropDownHeight;
-            }
-
-            mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
-
-            mPopup.update(getDropDownAnchorView(), mDropDownHorizontalOffset,
-                    mDropDownVerticalOffset, widthSpec, heightSpec);
-        } else {
-            if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
-                widthSpec = ViewGroup.LayoutParams.MATCH_PARENT;
-            } else {
-                if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
-                    mPopup.setWidth(getDropDownAnchorView().getWidth());
-                } else {
-                    mPopup.setWidth(mDropDownWidth);
-                }
-            }
-
-            if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
-                heightSpec = ViewGroup.LayoutParams.MATCH_PARENT;
-            } else {
-                if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
-                    mPopup.setHeight(height);
-                } else {
-                    mPopup.setHeight(mDropDownHeight);
-                }
-            }
-
-            mPopup.setWindowLayoutMode(widthSpec, heightSpec);
-            mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
-            
-            // use outside touchable to dismiss drop down when touching outside of it, so
-            // only set this if the dropdown is not always visible
-            mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
-            mPopup.setTouchInterceptor(new PopupTouchInterceptor());
-            mPopup.showAsDropDown(getDropDownAnchorView(),
-                    mDropDownHorizontalOffset, mDropDownVerticalOffset);
-            mDropDownList.setSelection(ListView.INVALID_POSITION);
-            clearListSelection();
-            post(mHideSelector);
         }
+        mPopup.show();
     }
     
     /**
@@ -1220,19 +1028,10 @@
      * @hide used only by SearchDialog
      */
     public void setForceIgnoreOutsideTouch(boolean forceIgnoreOutsideTouch) {
-        mForceIgnoreOutsideTouch = forceIgnoreOutsideTouch;
+        mPopup.setForceIgnoreOutsideTouch(forceIgnoreOutsideTouch);
     }
-
-    /**
-     * <p>Builds the popup window's content and returns the height the popup
-     * should have. Returns -1 when the content already exists.</p>
-     *
-     * @return the content's height or -1 if content already exists
-     */
-    private int buildDropDown() {
-        ViewGroup dropDownView;
-        int otherHeights = 0;
-
+    
+    private void buildImeCompletions() {
         final ListAdapter adapter = mAdapter;
         if (adapter != null) {
             InputMethodManager imm = InputMethodManager.peekInstance();
@@ -1260,135 +1059,6 @@
                 imm.displayCompletions(this, completions);
             }
         }
-
-        if (mDropDownList == null) {
-            Context context = getContext();
-
-            mHideSelector = new ListSelectorHider();
-
-            /**
-             * This Runnable exists for the sole purpose of checking if the view layout has got
-             * completed and if so call showDropDown to display the drop down. This is used to show
-             * the drop down as soon as possible after user opens up the search dialog, without
-             * waiting for the normal UI pipeline to do it's job which is slower than this method.
-             */
-            mShowDropDownRunnable = new Runnable() {
-                public void run() {
-                    // View layout should be all done before displaying the drop down.
-                    View view = getDropDownAnchorView();
-                    if (view != null && view.getWindowToken() != null) {
-                        showDropDown();
-                    }
-                }
-            };
-
-            mDropDownList = new DropDownListView(context);
-            mDropDownList.setSelector(mDropDownListHighlight);
-            mDropDownList.setAdapter(adapter);
-            mDropDownList.setVerticalFadingEdgeEnabled(true);
-            mDropDownList.setOnItemClickListener(mDropDownItemClickListener);
-            mDropDownList.setFocusable(true);
-            mDropDownList.setFocusableInTouchMode(true);
-            mDropDownList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
-                public void onItemSelected(AdapterView<?> parent, View view,
-                        int position, long id) {
-
-                    if (position != -1) {
-                        DropDownListView dropDownList = mDropDownList;
-
-                        if (dropDownList != null) {
-                            dropDownList.mListSelectionHidden = false;
-                        }
-                    }
-                }
-
-                public void onNothingSelected(AdapterView<?> parent) {
-                }
-            });
-            mDropDownList.setOnScrollListener(new PopupScrollListener());
-
-            if (mItemSelectedListener != null) {
-                mDropDownList.setOnItemSelectedListener(mItemSelectedListener);
-            }
-
-            dropDownView = mDropDownList;
-
-            View hintView = getHintView(context);
-            if (hintView != null) {
-                // if an hint has been specified, we accomodate more space for it and
-                // add a text view in the drop down menu, at the bottom of the list
-                LinearLayout hintContainer = new LinearLayout(context);
-                hintContainer.setOrientation(LinearLayout.VERTICAL);
-
-                LinearLayout.LayoutParams hintParams = new LinearLayout.LayoutParams(
-                        ViewGroup.LayoutParams.MATCH_PARENT, 0, 1.0f
-                );
-                hintContainer.addView(dropDownView, hintParams);
-                hintContainer.addView(hintView);
-
-                // measure the hint's height to find how much more vertical space
-                // we need to add to the drop down's height
-                int widthSpec = MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST);
-                int heightSpec = MeasureSpec.UNSPECIFIED;
-                hintView.measure(widthSpec, heightSpec);
-
-                hintParams = (LinearLayout.LayoutParams) hintView.getLayoutParams();
-                otherHeights = hintView.getMeasuredHeight() + hintParams.topMargin
-                        + hintParams.bottomMargin;
-
-                dropDownView = hintContainer;
-            }
-
-            mPopup.setContentView(dropDownView);
-        } else {
-            dropDownView = (ViewGroup) mPopup.getContentView();
-            final View view = dropDownView.findViewById(HINT_VIEW_ID);
-            if (view != null) {
-                LinearLayout.LayoutParams hintParams =
-                        (LinearLayout.LayoutParams) view.getLayoutParams();
-                otherHeights = view.getMeasuredHeight() + hintParams.topMargin
-                        + hintParams.bottomMargin;
-            }
-        }
-
-        // Max height available on the screen for a popup.
-        boolean ignoreBottomDecorations =
-                mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
-        final int maxHeight = mPopup.getMaxAvailableHeight(
-                getDropDownAnchorView(), mDropDownVerticalOffset, ignoreBottomDecorations);
-
-        // getMaxAvailableHeight() subtracts the padding, so we put it back,
-        // to get the available height for the whole window
-        int padding = 0;
-        Drawable background = mPopup.getBackground();
-        if (background != null) {
-            background.getPadding(mTempRect);
-            padding = mTempRect.top + mTempRect.bottom;
-        }
-
-        if (mDropDownAlwaysVisible || mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
-            return maxHeight + padding;
-        }
-
-        final int listContent = mDropDownList.measureHeightOfChildren(MeasureSpec.UNSPECIFIED,
-                0, ListView.NO_POSITION, maxHeight - otherHeights, 2);
-        // add padding only if the list has items in it, that way we don't show
-        // the popup if it is not needed
-        if (listContent > 0) otherHeights += padding;
-
-        return listContent + otherHeights;
-    }
-
-    private View getHintView(Context context) {
-        if (mHintText != null && mHintText.length() > 0) {
-            final TextView hintView = (TextView) LayoutInflater.from(context).inflate(
-                    mHintResource, null).findViewById(com.android.internal.R.id.text1);
-            hintView.setText(mHintText);
-            hintView.setId(HINT_VIEW_ID);
-            return hintView;
-        } else {
-            return null;
-        }
     }
 
     /**
@@ -1440,47 +1110,6 @@
         return mFilter;
     }
 
-    private class ListSelectorHider implements Runnable {
-        public void run() {
-            clearListSelection();
-        }
-    }
-
-    private class ResizePopupRunnable implements Runnable {
-        public void run() {
-            mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
-            showDropDown();
-        }
-    }
-
-    private class PopupTouchInterceptor implements OnTouchListener {
-        public boolean onTouch(View v, MotionEvent event) {
-            final int action = event.getAction();
-            if (action == MotionEvent.ACTION_DOWN &&
-                    mPopup != null && mPopup.isShowing()) {
-                postDelayed(mResizePopupRunnable, EXPAND_LIST_TIMEOUT);
-            } else if (action == MotionEvent.ACTION_UP) {
-                removeCallbacks(mResizePopupRunnable);
-            }
-            return false;
-        }
-    }
-    
-    private class PopupScrollListener implements ListView.OnScrollListener {
-        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
-                int totalItemCount) {
-
-        }
-
-        public void onScrollStateChanged(AbsListView view, int scrollState) {
-            if (scrollState == SCROLL_STATE_TOUCH_SCROLL &&
-                    !isInputMethodNotNeeded() && mPopup.getContentView() != null) {
-                removeCallbacks(mResizePopupRunnable);
-                mResizePopupRunnable.run();
-            }
-        }
-    }
-
     private class DropDownItemClickListener implements AdapterView.OnItemClickListener {
         public void onItemClick(AdapterView parent, View v, int position, long id) {
             performCompletion(v, position, id);
@@ -1488,123 +1117,6 @@
     }
 
     /**
-     * <p>Wrapper class for a ListView. This wrapper hijacks the focus to
-     * make sure the list uses the appropriate drawables and states when
-     * displayed on screen within a drop down. The focus is never actually
-     * passed to the drop down; the list only looks focused.</p>
-     */
-    private static class DropDownListView extends ListView {
-        /*
-         * WARNING: This is a workaround for a touch mode issue.
-         *
-         * Touch mode is propagated lazily to windows. This causes problems in
-         * the following scenario:
-         * - Type something in the AutoCompleteTextView and get some results
-         * - Move down with the d-pad to select an item in the list
-         * - Move up with the d-pad until the selection disappears
-         * - Type more text in the AutoCompleteTextView *using the soft keyboard*
-         *   and get new results; you are now in touch mode
-         * - The selection comes back on the first item in the list, even though
-         *   the list is supposed to be in touch mode
-         *
-         * Using the soft keyboard triggers the touch mode change but that change
-         * is propagated to our window only after the first list layout, therefore
-         * after the list attempts to resurrect the selection.
-         *
-         * The trick to work around this issue is to pretend the list is in touch
-         * mode when we know that the selection should not appear, that is when
-         * we know the user moved the selection away from the list.
-         *
-         * This boolean is set to true whenever we explicitely hide the list's
-         * selection and reset to false whenver we know the user moved the
-         * selection back to the list.
-         *
-         * When this boolean is true, isInTouchMode() returns true, otherwise it
-         * returns super.isInTouchMode().
-         */
-        private boolean mListSelectionHidden;
-
-        /**
-         * <p>Creates a new list view wrapper.</p>
-         *
-         * @param context this view's context
-         */
-        public DropDownListView(Context context) {
-            super(context, null, com.android.internal.R.attr.dropDownListViewStyle);
-        }
-
-        /**
-         * <p>Avoids jarring scrolling effect by ensuring that list elements
-         * made of a text view fit on a single line.</p>
-         *
-         * @param position the item index in the list to get a view for
-         * @return the view for the specified item
-         */
-        @Override
-        View obtainView(int position, boolean[] isScrap) {
-            View view = super.obtainView(position, isScrap);
-
-            if (view instanceof TextView) {
-                ((TextView) view).setHorizontallyScrolling(true);
-            }
-
-            return view;
-        }
-
-        @Override
-        public boolean isInTouchMode() {
-            // WARNING: Please read the comment where mListSelectionHidden is declared
-            return mListSelectionHidden || super.isInTouchMode();
-        }
-
-        /**
-         * <p>Returns the focus state in the drop down.</p>
-         *
-         * @return true always
-         */
-        @Override
-        public boolean hasWindowFocus() {
-            return true;
-        }
-
-        /**
-         * <p>Returns the focus state in the drop down.</p>
-         *
-         * @return true always
-         */
-        @Override
-        public boolean isFocused() {
-            return true;
-        }
-
-        /**
-         * <p>Returns the focus state in the drop down.</p>
-         *
-         * @return true always
-         */
-        @Override
-        public boolean hasFocus() {
-            return true;
-        }
-        
-        protected int[] onCreateDrawableState(int extraSpace) {
-            int[] res = super.onCreateDrawableState(extraSpace);
-            //noinspection ConstantIfStatement
-            if (false) {
-                StringBuilder sb = new StringBuilder("Created drawable state: [");
-                for (int i=0; i<res.length; i++) {
-                    if (i > 0) sb.append(", ");
-                    sb.append("0x");
-                    sb.append(Integer.toHexString(res[i]));
-                }
-                sb.append("]");
-                Log.i(TAG, sb.toString());
-            }
-            return res;
-        }
-    }
-
-    /**
      * This interface is used to make sure that the text entered in this TextView complies to
      * a certain format.  Since there is no foolproof way to prevent the user from leaving
      * this View with an incorrect value in it, all we can do is try to fix it ourselves
@@ -1652,10 +1164,7 @@
     private class PopupDataSetObserver extends DataSetObserver {
         @Override
         public void onChanged() {
-            if (isPopupShowing()) {
-                // This will resize the popup to fit the new adapter's content
-                showDropDown();
-            } else if (mAdapter != null) {
+            if (mAdapter != null) {
                 // If the popup is not showing already, showing it will cause
                 // the list of data set observers attached to the adapter to
                 // change. We can't do it from here, because we are in the middle
@@ -1670,14 +1179,5 @@
                 });
             }
         }
-
-        @Override
-        public void onInvalidated() {
-            if (!mDropDownAlwaysVisible) {
-                // There's no data to display so make sure we're not showing
-                // the drop down and its list
-                dismissDropDown();
-            }
-        }
     }
 }
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
new file mode 100644
index 0000000..eda3606
--- /dev/null
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -0,0 +1,1215 @@
+/*
+ * Copyright (C) 2010 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 android.widget;
+
+import android.content.Context;
+import android.database.DataSetObserver;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.View.MeasureSpec;
+import android.view.View.OnTouchListener;
+
+/**
+ * A ListPopupWindow anchors itself to a host view and displays a
+ * list of choices. When one is selected, the popup is dismissed.
+ * 
+ * <p>ListPopupWindow contains a number of tricky behaviors surrounding
+ * positioning, scrolling parents to fit the dropdown, interacting
+ * sanely with the IME if present, and others.
+ * 
+ * @see android.widget.AutoCompleteTextView
+ * @see android.widget.Spinner
+ */
+public class ListPopupWindow {
+    private static final String TAG = "ListPopupWindow";
+    private static final boolean DEBUG = false;
+
+    /**
+     * This value controls the length of time that the user
+     * must leave a pointer down without scrolling to expand
+     * the autocomplete dropdown list to cover the IME.
+     */
+    private static final int EXPAND_LIST_TIMEOUT = 250;
+
+    private Context mContext;
+    private PopupWindow mPopup;
+    private ListAdapter mAdapter;
+    private DropDownListView mDropDownList;
+
+    private int mDropDownHeight = ViewGroup.LayoutParams.WRAP_CONTENT;
+    private int mDropDownWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
+    private int mDropDownHorizontalOffset;
+    private int mDropDownVerticalOffset;
+
+    private boolean mDropDownAlwaysVisible = false;
+    private boolean mForceIgnoreOutsideTouch = false;
+
+    private View mPromptView;
+    private int mPromptPosition = POSITION_PROMPT_ABOVE;
+
+    private DataSetObserver mObserver;
+
+    private View mDropDownAnchorView;
+
+    private Drawable mDropDownListHighlight;
+
+    private AdapterView.OnItemClickListener mItemClickListener;
+    private AdapterView.OnItemSelectedListener mItemSelectedListener;
+
+    private final  ResizePopupRunnable mResizePopupRunnable = new ResizePopupRunnable();
+    private final PopupTouchInterceptor mTouchInterceptor = new PopupTouchInterceptor();
+    private final PopupScrollListener mScrollListener = new PopupScrollListener();
+    private final ListSelectorHider mHideSelector = new ListSelectorHider();
+    private Runnable mShowDropDownRunnable;
+
+    private Handler mHandler = new Handler();
+
+    private Rect mTempRect = new Rect();
+
+    private boolean mModal;
+
+    /**
+     * The provided prompt view should appear above list content.
+     * 
+     * @see #setPromptPosition(int)
+     * @see #getPromptPosition()
+     * @see #setPromptView(View)
+     */
+    public static final int POSITION_PROMPT_ABOVE = 0;
+
+    /**
+     * The provided prompt view should appear below list content.
+     * 
+     * @see #setPromptPosition(int)
+     * @see #getPromptPosition()
+     * @see #setPromptView(View)
+     */
+    public static final int POSITION_PROMPT_BELOW = 1;
+
+    /**
+     * Alias for {@link ViewGroup.LayoutParams#MATCH_PARENT}.
+     * If used to specify a popup width, the popup will match the width of the anchor view.
+     * If used to specify a popup height, the popup will fill available space.
+     */
+    public static final int MATCH_PARENT = ViewGroup.LayoutParams.MATCH_PARENT;
+    
+    /**
+     * Alias for {@link ViewGroup.LayoutParams#WRAP_CONTENT}.
+     * If used to specify a popup width, the popup will use the width of its content.
+     */
+    public static final int WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT;
+    
+    /**
+     * Mode for {@link #setInputMethodMode(int)}: the requirements for the
+     * input method should be based on the focusability of the popup.  That is
+     * if it is focusable than it needs to work with the input method, else
+     * it doesn't.
+     */
+    public static final int INPUT_METHOD_FROM_FOCUSABLE = PopupWindow.INPUT_METHOD_FROM_FOCUSABLE;
+    
+    /**
+     * Mode for {@link #setInputMethodMode(int)}: this popup always needs to
+     * work with an input method, regardless of whether it is focusable.  This
+     * means that it will always be displayed so that the user can also operate
+     * the input method while it is shown.
+     */
+    public static final int INPUT_METHOD_NEEDED = PopupWindow.INPUT_METHOD_NEEDED;
+    
+    /**
+     * Mode for {@link #setInputMethodMode(int)}: this popup never needs to
+     * work with an input method, regardless of whether it is focusable.  This
+     * means that it will always be displayed to use as much space on the
+     * screen as needed, regardless of whether this covers the input method.
+     */
+    public static final int INPUT_METHOD_NOT_NEEDED = PopupWindow.INPUT_METHOD_NOT_NEEDED;
+
+    /**
+     * Create a new, empty popup window capable of displaying items from a ListAdapter.
+     * Backgrounds should be set using {@link #setBackgroundDrawable(Drawable)}.
+     * 
+     * @param context Context used for contained views.
+     */
+    public ListPopupWindow(Context context) {
+        this(context, null, 0, 0);
+    }
+
+    /**
+     * Create a new, empty popup window capable of displaying items from a ListAdapter.
+     * Backgrounds should be set using {@link #setBackgroundDrawable(Drawable)}.
+     * 
+     * @param context Context used for contained views.
+     * @param attrs Attributes from inflating parent views used to style the popup.
+     */
+    public ListPopupWindow(Context context, AttributeSet attrs) {
+        this(context, attrs, 0, 0);
+    }
+
+    /**
+     * Create a new, empty popup window capable of displaying items from a ListAdapter.
+     * Backgrounds should be set using {@link #setBackgroundDrawable(Drawable)}.
+     * 
+     * @param context Context used for contained views.
+     * @param attrs Attributes from inflating parent views used to style the popup.
+     * @param defStyleAttr Default style attribute to use for popup content.
+     */
+    public ListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    /**
+     * Create a new, empty popup window capable of displaying items from a ListAdapter.
+     * Backgrounds should be set using {@link #setBackgroundDrawable(Drawable)}.
+     * 
+     * @param context Context used for contained views.
+     * @param attrs Attributes from inflating parent views used to style the popup.
+     * @param defStyleAttr Style attribute to read for default styling of popup content.
+     * @param defStyleRes Style resource ID to use for default styling of popup content.
+     */
+    public ListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        mContext = context;
+        mPopup = new PopupWindow(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    /**
+     * Sets the adapter that provides the data and the views to represent the data
+     * in this popup window.
+     *
+     * @param adapter The adapter to use to create this window's content.
+     */
+    public void setAdapter(ListAdapter adapter) {
+        if (mObserver == null) {
+            mObserver = new PopupDataSetObserver();
+        } else if (mAdapter != null) {
+            adapter.unregisterDataSetObserver(mObserver);
+        }
+        mAdapter = adapter;
+        if (mAdapter != null) {
+            adapter.registerDataSetObserver(mObserver);
+        }
+        
+        if (mDropDownList != null) {
+            mDropDownList.setAdapter(mAdapter);
+        }
+    }
+
+    /**
+     * Set where the optional prompt view should appear. The default is
+     * {@link #POSITION_PROMPT_ABOVE}.
+     * 
+     * @param position A position constant declaring where the prompt should be displayed.
+     * 
+     * @see #POSITION_PROMPT_ABOVE
+     * @see #POSITION_PROMPT_BELOW
+     */
+    public void setPromptPosition(int position) {
+        mPromptPosition = position;
+    }
+
+    /**
+     * @return Where the optional prompt view should appear.
+     * 
+     * @see #POSITION_PROMPT_ABOVE
+     * @see #POSITION_PROMPT_BELOW
+     */
+    public int getPromptPosition() {
+        return mPromptPosition;
+    }
+
+    /**
+     * Set whether this window should be modal when shown.
+     * 
+     * <p>If a popup window is modal, it will receive all touch and key input.
+     * If the user touches outside the popup window's content area the popup window
+     * will be dismissed.
+     * 
+     * @param modal {@code true} if the popup window should be modal, {@code false} otherwise.
+     */
+    public void setModal(boolean modal) {
+        mModal = true;
+        mPopup.setFocusable(modal);
+    }
+
+    /**
+     * Returns whether the popup window will be modal when shown.
+     * 
+     * @return {@code true} if the popup window will be modal, {@code false} otherwise.
+     */
+    public boolean isModal() {
+        return mModal;
+    }
+
+    /**
+     * Forces outside touches to be ignored. Normally if {@link #isDropDownAlwaysVisible()} is
+     * false, we allow outside touch to dismiss the dropdown. If this is set to true, then we
+     * ignore outside touch even when the drop down is not set to always visible.
+     * 
+     * @hide Used only by AutoCompleteTextView to handle some internal special cases.
+     */
+    public void setForceIgnoreOutsideTouch(boolean forceIgnoreOutsideTouch) {
+        mForceIgnoreOutsideTouch = forceIgnoreOutsideTouch;
+    }
+
+    /**
+     * Sets whether the drop-down should remain visible under certain conditions.
+     *
+     * The drop-down will occupy the entire screen below {@link #getAnchorView} regardless
+     * of the size or content of the list.  {@link #getBackground()} will fill any space
+     * that is not used by the list.
+     *
+     * @param dropDownAlwaysVisible Whether to keep the drop-down visible.
+     *
+     * @hide Only used by AutoCompleteTextView under special conditions.
+     */
+    public void setDropDownAlwaysVisible(boolean dropDownAlwaysVisible) {
+        mDropDownAlwaysVisible = dropDownAlwaysVisible;
+    }
+
+    /**
+     * @return Whether the drop-down is visible under special conditions.
+     *
+     * @hide Only used by AutoCompleteTextView under special conditions.
+     */
+    public boolean isDropDownAlwaysVisible() {
+        return mDropDownAlwaysVisible;
+    }
+
+    /**
+     * Sets the operating mode for the soft input area.
+     *
+     * @param mode The desired mode, see
+     *        {@link android.view.WindowManager.LayoutParams#softInputMode}
+     *        for the full list
+     *
+     * @see android.view.WindowManager.LayoutParams#softInputMode
+     * @see #getSoftInputMode()
+     */
+    public void setSoftInputMode(int mode) {
+        mPopup.setSoftInputMode(mode);
+    }
+
+    /**
+     * Returns the current value in {@link #setSoftInputMode(int)}.
+     *
+     * @see #setSoftInputMode(int)
+     * @see android.view.WindowManager.LayoutParams#softInputMode
+     */
+    public int getSoftInputMode() {
+        return mPopup.getSoftInputMode();
+    }
+
+    /**
+     * Sets a drawable to use as the list item selector.
+     * 
+     * @param selector List selector drawable to use in the popup.
+     */
+    public void setListSelector(Drawable selector) {
+        mDropDownListHighlight = selector;
+    }
+
+    /**
+     * @return The background drawable for the popup window.
+     */
+    public Drawable getBackground() {
+        return mPopup.getBackground();
+    }
+
+    /**
+     * Sets a drawable to be the background for the popup window.
+     * 
+     * @param d A drawable to set as the background.
+     */
+    public void setBackgroundDrawable(Drawable d) {
+        mPopup.setBackgroundDrawable(d);
+    }
+
+    /**
+     * Set an animation style to use when the popup window is shown or dismissed.
+     * 
+     * @param animationStyle Animation style to use.
+     */
+    public void setAnimationStyle(int animationStyle) {
+        mPopup.setAnimationStyle(animationStyle);
+    }
+
+    /**
+     * Returns the animation style that will be used when the popup window is
+     * shown or dismissed.
+     * 
+     * @return Animation style that will be used.
+     */
+    public int getAnimationStyle() {
+        return mPopup.getAnimationStyle();
+    }
+
+    /**
+     * Returns the view that will be used to anchor this popup.
+     * 
+     * @return The popup's anchor view
+     */
+    public View getAnchorView() {
+        return mDropDownAnchorView;
+    }
+
+    /**
+     * Sets the popup's anchor view. This popup will always be positioned relative to
+     * the anchor view when shown.
+     * 
+     * @param anchor The view to use as an anchor.
+     */
+    public void setAnchorView(View anchor) {
+        mDropDownAnchorView = anchor;
+    }
+
+    /**
+     * @return The horizontal offset of the popup from its anchor in pixels.
+     */
+    public int getHorizontalOffset() {
+        return mDropDownHorizontalOffset;
+    }
+
+    /**
+     * Set the horizontal offset of this popup from its anchor view in pixels.
+     * 
+     * @param The horizontal offset of the popup from its anchor. 
+     */
+    public void setHorizontalOffset(int offset) {
+        mDropDownHorizontalOffset = offset;
+    }
+
+    /**
+     * @return The vertical offset of the popup from its anchor in pixels.
+     */
+    public int getVerticalOffset() {
+        return mDropDownVerticalOffset;
+    }
+
+    /**
+     * Set the vertical offset of this popup from its anchor view in pixels.
+     * 
+     * @param The vertical offset of the popup from its anchor. 
+     */
+    public void setVerticalOffset(int offset) {
+        mDropDownVerticalOffset = offset;
+    }
+
+    /**
+     * @return The width of the popup window in pixels.
+     */
+    public int getWidth() {
+        return mDropDownWidth;
+    }
+
+    /**
+     * Sets the width of the popup window in pixels. Can also be {@link #MATCH_PARENT}
+     * or {@link #WRAP_CONTENT}.
+     * 
+     * @param width Width of the popup window.
+     */
+    public void setWidth(int width) {
+        mDropDownWidth = width;
+    }
+
+    /**
+     * @return The height of the popup window in pixels.
+     */
+    public int getHeight() {
+        return mDropDownHeight;
+    }
+
+    /**
+     * Sets the height of the popup window in pixels. Can also be {@link #MATCH_PARENT}.
+     * 
+     * @param height Height of the popup window.
+     */
+    public void setHeight(int height) {
+        mDropDownHeight = height;
+    }
+
+    /**
+     * Sets a listener to receive events when a list item is clicked.
+     * 
+     * @param clickListener Listener to register
+     * 
+     * @see ListView#setOnItemClickListener(android.widget.AdapterView.OnItemClickListener)
+     */
+    public void setOnItemClickListener(AdapterView.OnItemClickListener clickListener) {
+        mItemClickListener = clickListener;
+    }
+
+    /**
+     * Sets a listener to receive events when a list item is selected.
+     * 
+     * @param selectedListener Listener to register.
+     * 
+     * @see ListView#setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener)
+     */
+    public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener selectedListener) {
+        mItemSelectedListener = selectedListener;
+    }
+
+    /**
+     * Set a view to act as a user prompt for this popup window. Where the prompt view will appear
+     * is controlled by {@link #setPromptPosition(int)}.
+     * 
+     * @param prompt View to use as an informational prompt.
+     */
+    public void setPromptView(View prompt) {
+        boolean showing = isShowing();
+        if (showing) {
+            removePromptView();
+        }
+        mPromptView = prompt;
+        if (showing) {
+            show();
+        }
+    }
+
+    /**
+     * Post a {@link #show()} call to the UI thread.
+     */
+    public void postShow() {
+        mHandler.post(mShowDropDownRunnable);
+    }
+
+    /**
+     * Show the popup list. If the list is already showing, this method
+     * will recalculate the popup's size and position.
+     */
+    public void show() {
+        int height = buildDropDown();
+
+        int widthSpec = 0;
+        int heightSpec = 0;
+
+        boolean noInputMethod = isInputMethodNotNeeded();
+
+        if (mPopup.isShowing()) {
+            if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
+                // The call to PopupWindow's update method below can accept -1 for any
+                // value you do not want to update.
+                widthSpec = -1;
+            } else if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
+                widthSpec = getAnchorView().getWidth();
+            } else {
+                widthSpec = mDropDownWidth;
+            }
+
+            if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
+                // The call to PopupWindow's update method below can accept -1 for any
+                // value you do not want to update.
+                heightSpec = noInputMethod ? height : ViewGroup.LayoutParams.MATCH_PARENT;
+                if (noInputMethod) {
+                    mPopup.setWindowLayoutMode(
+                            mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
+                                    ViewGroup.LayoutParams.MATCH_PARENT : 0, 0);
+                } else {
+                    mPopup.setWindowLayoutMode(
+                            mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
+                                    ViewGroup.LayoutParams.MATCH_PARENT : 0,
+                            ViewGroup.LayoutParams.MATCH_PARENT);
+                }
+            } else if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
+                heightSpec = height;
+            } else {
+                heightSpec = mDropDownHeight;
+            }
+
+            mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
+
+            mPopup.update(getAnchorView(), mDropDownHorizontalOffset,
+                    mDropDownVerticalOffset, widthSpec, heightSpec);
+        } else {
+            if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
+                widthSpec = ViewGroup.LayoutParams.MATCH_PARENT;
+            } else {
+                if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
+                    mPopup.setWidth(getAnchorView().getWidth());
+                } else {
+                    mPopup.setWidth(mDropDownWidth);
+                }
+            }
+
+            if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
+                heightSpec = ViewGroup.LayoutParams.MATCH_PARENT;
+            } else {
+                if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
+                    mPopup.setHeight(height);
+                } else {
+                    mPopup.setHeight(mDropDownHeight);
+                }
+            }
+
+            mPopup.setWindowLayoutMode(widthSpec, heightSpec);
+            mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
+            
+            // use outside touchable to dismiss drop down when touching outside of it, so
+            // only set this if the dropdown is not always visible
+            mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
+            mPopup.setTouchInterceptor(mTouchInterceptor);
+            mPopup.showAsDropDown(getAnchorView(),
+                    mDropDownHorizontalOffset, mDropDownVerticalOffset);
+            mDropDownList.setSelection(ListView.INVALID_POSITION);
+            
+            if (!mModal || mDropDownList.isInTouchMode()) {
+                clearListSelection();
+            }
+            if (!mModal) {
+                mHandler.post(mHideSelector);
+            }
+        }
+    }
+
+    /**
+     * Dismiss the popup window.
+     */
+    public void dismiss() {
+        mPopup.dismiss();
+        removePromptView();
+        mPopup.setContentView(null);
+        mDropDownList = null;
+    }
+
+    private void removePromptView() {
+        if (mPromptView != null) {
+            final ViewParent parent = mPromptView.getParent();
+            if (parent instanceof ViewGroup) {
+                final ViewGroup group = (ViewGroup) parent;
+                group.removeView(mPromptView);
+            }
+        }
+    }
+
+    /**
+     * Control how the popup operates with an input method: one of
+     * {@link #INPUT_METHOD_FROM_FOCUSABLE}, {@link #INPUT_METHOD_NEEDED},
+     * or {@link #INPUT_METHOD_NOT_NEEDED}.
+     * 
+     * <p>If the popup is showing, calling this method will take effect only
+     * the next time the popup is shown or through a manual call to the {@link #show()}
+     * method.</p>
+     * 
+     * @see #getInputMethodMode()
+     * @see #show()
+     */
+    public void setInputMethodMode(int mode) {
+        mPopup.setInputMethodMode(mode);
+    }
+
+    /**
+     * Return the current value in {@link #setInputMethodMode(int)}.
+     * 
+     * @see #setInputMethodMode(int)
+     */
+    public int getInputMethodMode() {
+        return mPopup.getInputMethodMode();
+    }
+
+    /**
+     * Set the selected position of the list.
+     * Only valid when {@link #isShowing()} == {@code true}.
+     * 
+     * @param position List position to set as selected.
+     */
+    public void setSelection(int position) {
+        DropDownListView list = mDropDownList;
+        if (isShowing() && list != null) {
+            list.mListSelectionHidden = false;
+            list.setSelection(position);
+            if (list.getChoiceMode() != ListView.CHOICE_MODE_NONE) {
+                list.setItemChecked(position, true);
+            }
+        }
+    }
+
+    /**
+     * Clear any current list selection.
+     * Only valid when {@link #isShowing()} == {@code true}.
+     */
+    public void clearListSelection() {
+        final DropDownListView list = mDropDownList;
+        if (list != null) {
+            // WARNING: Please read the comment where mListSelectionHidden is declared
+            list.mListSelectionHidden = true;
+            list.hideSelector();
+            list.requestLayout();
+        }
+    }
+
+    /**
+     * @return {@code true} if the popup is currently showing, {@code false} otherwise.
+     */
+    public boolean isShowing() {
+        return mPopup.isShowing();
+    }
+
+    /**
+     * @return {@code true} if this popup is configured to assume the user does not need
+     * to interact with the IME while it is showing, {@code false} otherwise.
+     */
+    public boolean isInputMethodNotNeeded() {
+        return mPopup.getInputMethodMode() == INPUT_METHOD_NOT_NEEDED;
+    }
+
+    /**
+     * Perform an item click operation on the specified list adapter position.
+     * 
+     * @param position Adapter position for performing the click
+     * @return true if the click action could be performed, false if not.
+     *         (e.g. if the popup was not showing, this method would return false.)
+     */
+    public boolean performItemClick(int position) {
+        if (isShowing()) {
+            if (mItemClickListener != null) {
+                final DropDownListView list = mDropDownList;
+                final View child = list.getChildAt(position - list.getFirstVisiblePosition());
+                mItemClickListener.onItemClick(list, child, position, child.getId());
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * @return The currently selected item or null if the popup is not showing.
+     */
+    public Object getSelectedItem() {
+        if (!isShowing()) {
+            return null;
+        }
+        return mDropDownList.getSelectedItem();
+    }
+
+    /**
+     * @return The position of the currently selected item or {@link ListView#INVALID_POSITION}
+     * if {@link #isShowing()} == {@code false}.
+     * 
+     * @see ListView#getSelectedItemPosition()
+     */
+    public int getSelectedItemPosition() {
+        if (!isShowing()) {
+            return ListView.INVALID_POSITION;
+        }
+        return mDropDownList.getSelectedItemPosition();
+    }
+
+    /**
+     * @return The ID of the currently selected item or {@link ListView#INVALID_ROW_ID}
+     * if {@link #isShowing()} == {@code false}.
+     * 
+     * @see ListView#getSelectedItemId()
+     */
+    public long getSelectedItemId() {
+        if (!isShowing()) {
+            return ListView.INVALID_ROW_ID;
+        }
+        return mDropDownList.getSelectedItemId();
+    }
+
+    /**
+     * @return The View for the currently selected item or null if
+     * {@link #isShowing()} == {@code false}.
+     * 
+     * @see ListView#getSelectedView()
+     */
+    public View getSelectedView() {
+        if (!isShowing()) {
+            return null;
+        }
+        return mDropDownList.getSelectedView();
+    }
+
+    /**
+     * @return The {@link ListView} displayed within the popup window.
+     * Only valid when {@link #isShowing()} == {@code true}.
+     */
+    public ListView getListView() {
+        return mDropDownList;
+    }
+
+    /**
+     * Filter key down events. By forwarding key up events to this function,
+     * views using non-modal ListPopupWindow can have it handle key selection of items.
+     *  
+     * @param keyCode keyCode param passed to the host view's onKeyDown
+     * @param event event param passed to the host view's onKeyDown
+     * @return true if the event was handled, false if it was ignored.
+     * 
+     * @see #setModal(boolean)
+     */
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        // when the drop down is shown, we drive it directly
+        if (isShowing()) {
+            // the key events are forwarded to the list in the drop down view
+            // note that ListView handles space but we don't want that to happen
+            // also if selection is not currently in the drop down, then don't
+            // let center or enter presses go there since that would cause it
+            // to select one of its items
+            if (keyCode != KeyEvent.KEYCODE_SPACE
+                    && (mDropDownList.getSelectedItemPosition() >= 0
+                            || (keyCode != KeyEvent.KEYCODE_ENTER
+                                    && keyCode != KeyEvent.KEYCODE_DPAD_CENTER))) {
+                int curIndex = mDropDownList.getSelectedItemPosition();
+                boolean consumed;
+
+                final boolean below = !mPopup.isAboveAnchor();
+
+                final ListAdapter adapter = mAdapter;
+                
+                boolean allEnabled;
+                int firstItem = Integer.MAX_VALUE;
+                int lastItem = Integer.MIN_VALUE;
+
+                if (adapter != null) {
+                    allEnabled = adapter.areAllItemsEnabled();
+                    firstItem = allEnabled ? 0 :
+                            mDropDownList.lookForSelectablePosition(0, true);
+                    lastItem = allEnabled ? adapter.getCount() - 1 :
+                            mDropDownList.lookForSelectablePosition(adapter.getCount() - 1, false);                    
+                }
+                
+                if ((below && keyCode == KeyEvent.KEYCODE_DPAD_UP && curIndex <= firstItem) ||
+                        (!below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN && curIndex >= lastItem)) {
+                    // When the selection is at the top, we block the key
+                    // event to prevent focus from moving.
+                    clearListSelection();
+                    mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
+                    show();
+                    return true;
+                } else {
+                    // WARNING: Please read the comment where mListSelectionHidden
+                    //          is declared
+                    mDropDownList.mListSelectionHidden = false;
+                }
+
+                consumed = mDropDownList.onKeyDown(keyCode, event);
+                if (DEBUG) Log.v(TAG, "Key down: code=" + keyCode + " list consumed=" + consumed);
+
+                if (consumed) {
+                    // If it handled the key event, then the user is
+                    // navigating in the list, so we should put it in front.
+                    mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+                    // Here's a little trick we need to do to make sure that
+                    // the list view is actually showing its focus indicator,
+                    // by ensuring it has focus and getting its window out
+                    // of touch mode.
+                    mDropDownList.requestFocusFromTouch();
+                    show();
+
+                    switch (keyCode) {
+                        // avoid passing the focus from the text view to the
+                        // next component
+                        case KeyEvent.KEYCODE_ENTER:
+                        case KeyEvent.KEYCODE_DPAD_CENTER:
+                        case KeyEvent.KEYCODE_DPAD_DOWN:
+                        case KeyEvent.KEYCODE_DPAD_UP:
+                            return true;
+                    }
+                } else {
+                    if (below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
+                        // when the selection is at the bottom, we block the
+                        // event to avoid going to the next focusable widget
+                        if (curIndex == lastItem) {
+                            return true;
+                        }
+                    } else if (!below && keyCode == KeyEvent.KEYCODE_DPAD_UP &&
+                            curIndex == firstItem) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Filter key down events. By forwarding key up events to this function,
+     * views using non-modal ListPopupWindow can have it handle key selection of items.
+     *  
+     * @param keyCode keyCode param passed to the host view's onKeyUp
+     * @param event event param passed to the host view's onKeyUp
+     * @return true if the event was handled, false if it was ignored.
+     * 
+     * @see #setModal(boolean)
+     */
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (isShowing() && mDropDownList.getSelectedItemPosition() >= 0) {
+            boolean consumed = mDropDownList.onKeyUp(keyCode, event);
+            if (consumed) {
+                switch (keyCode) {
+                    // if the list accepts the key events and the key event
+                    // was a click, the text view gets the selected item
+                    // from the drop down as its content
+                    case KeyEvent.KEYCODE_ENTER:
+                    case KeyEvent.KEYCODE_DPAD_CENTER:
+                        dismiss();
+                        break;
+                }
+            }
+            return consumed;
+        }
+        return false;
+    }
+
+    /**
+     * Filter pre-IME key events. By forwarding {@link View#onKeyPreIme(int, KeyEvent)}
+     * events to this function, views using ListPopupWindow can have it dismiss the popup
+     * when the back key is pressed.
+     *  
+     * @param keyCode keyCode param passed to the host view's onKeyPreIme
+     * @param event event param passed to the host view's onKeyPreIme
+     * @return true if the event was handled, false if it was ignored.
+     * 
+     * @see #setModal(boolean)
+     */
+    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_BACK && isShowing()) {
+            // special case for the back key, we do not even try to send it
+            // to the drop down list but instead, consume it immediately
+            final View anchorView = mDropDownAnchorView;
+            if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
+                anchorView.getKeyDispatcherState().startTracking(event, this);
+                return true;
+            } else if (event.getAction() == KeyEvent.ACTION_UP) {
+                anchorView.getKeyDispatcherState().handleUpEvent(event);
+                if (event.isTracking() && !event.isCanceled()) {
+                    dismiss();
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * <p>Builds the popup window's content and returns the height the popup
+     * should have. Returns -1 when the content already exists.</p>
+     *
+     * @return the content's height or -1 if content already exists
+     */
+    private int buildDropDown() {
+        ViewGroup dropDownView;
+        int otherHeights = 0;
+
+        if (mDropDownList == null) {
+            Context context = mContext;
+
+            /**
+             * This Runnable exists for the sole purpose of checking if the view layout has got
+             * completed and if so call showDropDown to display the drop down. This is used to show
+             * the drop down as soon as possible after user opens up the search dialog, without
+             * waiting for the normal UI pipeline to do it's job which is slower than this method.
+             */
+            mShowDropDownRunnable = new Runnable() {
+                public void run() {
+                    // View layout should be all done before displaying the drop down.
+                    View view = getAnchorView();
+                    if (view != null && view.getWindowToken() != null) {
+                        show();
+                    }
+                }
+            };
+
+            mDropDownList = new DropDownListView(context, !mModal);
+            if (mDropDownListHighlight != null) {
+                mDropDownList.setSelector(mDropDownListHighlight);
+            }
+            mDropDownList.setAdapter(mAdapter);
+            mDropDownList.setVerticalFadingEdgeEnabled(true);
+            mDropDownList.setOnItemClickListener(mItemClickListener);
+            mDropDownList.setFocusable(true);
+            mDropDownList.setFocusableInTouchMode(true);
+            mDropDownList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+                public void onItemSelected(AdapterView<?> parent, View view,
+                        int position, long id) {
+
+                    if (position != -1) {
+                        DropDownListView dropDownList = mDropDownList;
+
+                        if (dropDownList != null) {
+                            dropDownList.mListSelectionHidden = false;
+                        }
+                    }
+                }
+
+                public void onNothingSelected(AdapterView<?> parent) {
+                }
+            });
+            mDropDownList.setOnScrollListener(mScrollListener);
+
+            if (mItemSelectedListener != null) {
+                mDropDownList.setOnItemSelectedListener(mItemSelectedListener);
+            }
+
+            dropDownView = mDropDownList;
+
+            View hintView = mPromptView;
+            if (hintView != null) {
+                // if an hint has been specified, we accomodate more space for it and
+                // add a text view in the drop down menu, at the bottom of the list
+                LinearLayout hintContainer = new LinearLayout(context);
+                hintContainer.setOrientation(LinearLayout.VERTICAL);
+
+                LinearLayout.LayoutParams hintParams = new LinearLayout.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT, 0, 1.0f
+                );
+                
+                switch (mPromptPosition) {
+                case POSITION_PROMPT_BELOW:
+                    hintContainer.addView(dropDownView, hintParams);
+                    hintContainer.addView(hintView);
+                    break;
+                    
+                case POSITION_PROMPT_ABOVE:
+                    hintContainer.addView(hintView);
+                    hintContainer.addView(dropDownView, hintParams);
+                    break;
+                    
+                default:
+                    Log.e(TAG, "Invalid hint position " + mPromptPosition);
+                    break;
+                }
+
+                // measure the hint's height to find how much more vertical space
+                // we need to add to the drop down's height
+                int widthSpec = MeasureSpec.makeMeasureSpec(mDropDownWidth, MeasureSpec.AT_MOST);
+                int heightSpec = MeasureSpec.UNSPECIFIED;
+                hintView.measure(widthSpec, heightSpec);
+
+                hintParams = (LinearLayout.LayoutParams) hintView.getLayoutParams();
+                otherHeights = hintView.getMeasuredHeight() + hintParams.topMargin
+                        + hintParams.bottomMargin;
+
+                dropDownView = hintContainer;
+            }
+
+            mPopup.setContentView(dropDownView);
+        } else {
+            dropDownView = (ViewGroup) mPopup.getContentView();
+            final View view = mPromptView;
+            if (view != null) {
+                LinearLayout.LayoutParams hintParams =
+                        (LinearLayout.LayoutParams) view.getLayoutParams();
+                otherHeights = view.getMeasuredHeight() + hintParams.topMargin
+                        + hintParams.bottomMargin;
+            }
+        }
+
+        // Max height available on the screen for a popup.
+        boolean ignoreBottomDecorations =
+                mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
+        final int maxHeight = mPopup.getMaxAvailableHeight(
+                getAnchorView(), mDropDownVerticalOffset, ignoreBottomDecorations);
+
+        // getMaxAvailableHeight() subtracts the padding, so we put it back,
+        // to get the available height for the whole window
+        int padding = 0;
+        Drawable background = mPopup.getBackground();
+        if (background != null) {
+            background.getPadding(mTempRect);
+            padding = mTempRect.top + mTempRect.bottom;
+        }
+
+        if (mDropDownAlwaysVisible || mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
+            return maxHeight + padding;
+        }
+
+        final int listContent = mDropDownList.measureHeightOfChildren(MeasureSpec.UNSPECIFIED,
+                0, ListView.NO_POSITION, maxHeight - otherHeights, 2);
+        // add padding only if the list has items in it, that way we don't show
+        // the popup if it is not needed
+        if (listContent > 0) otherHeights += padding;
+
+        return listContent + otherHeights;
+    }
+
+    /**
+     * <p>Wrapper class for a ListView. This wrapper can hijack the focus to
+     * make sure the list uses the appropriate drawables and states when
+     * displayed on screen within a drop down. The focus is never actually
+     * passed to the drop down in this mode; the list only looks focused.</p>
+     */
+    private static class DropDownListView extends ListView {
+        private static final String TAG = ListPopupWindow.TAG + ".DropDownListView";
+        /*
+         * WARNING: This is a workaround for a touch mode issue.
+         *
+         * Touch mode is propagated lazily to windows. This causes problems in
+         * the following scenario:
+         * - Type something in the AutoCompleteTextView and get some results
+         * - Move down with the d-pad to select an item in the list
+         * - Move up with the d-pad until the selection disappears
+         * - Type more text in the AutoCompleteTextView *using the soft keyboard*
+         *   and get new results; you are now in touch mode
+         * - The selection comes back on the first item in the list, even though
+         *   the list is supposed to be in touch mode
+         *
+         * Using the soft keyboard triggers the touch mode change but that change
+         * is propagated to our window only after the first list layout, therefore
+         * after the list attempts to resurrect the selection.
+         *
+         * The trick to work around this issue is to pretend the list is in touch
+         * mode when we know that the selection should not appear, that is when
+         * we know the user moved the selection away from the list.
+         *
+         * This boolean is set to true whenever we explicitly hide the list's
+         * selection and reset to false whenever we know the user moved the
+         * selection back to the list.
+         *
+         * When this boolean is true, isInTouchMode() returns true, otherwise it
+         * returns super.isInTouchMode().
+         */
+        private boolean mListSelectionHidden;
+        
+        /**
+         * True if this wrapper should fake focus.
+         */
+        private boolean mHijackFocus;
+
+        /**
+         * <p>Creates a new list view wrapper.</p>
+         *
+         * @param context this view's context
+         */
+        public DropDownListView(Context context, boolean hijackFocus) {
+            super(context, null, com.android.internal.R.attr.dropDownListViewStyle);
+            mHijackFocus = hijackFocus;
+        }
+
+        /**
+         * <p>Avoids jarring scrolling effect by ensuring that list elements
+         * made of a text view fit on a single line.</p>
+         *
+         * @param position the item index in the list to get a view for
+         * @return the view for the specified item
+         */
+        @Override
+        View obtainView(int position, boolean[] isScrap) {
+            View view = super.obtainView(position, isScrap);
+
+            if (view instanceof TextView) {
+                ((TextView) view).setHorizontallyScrolling(true);
+            }
+
+            return view;
+        }
+
+        @Override
+        public boolean isInTouchMode() {
+            // WARNING: Please read the comment where mListSelectionHidden is declared
+            return (mHijackFocus && mListSelectionHidden) || super.isInTouchMode();
+        }
+
+        /**
+         * <p>Returns the focus state in the drop down.</p>
+         *
+         * @return true always if hijacking focus
+         */
+        @Override
+        public boolean hasWindowFocus() {
+            return mHijackFocus || super.hasWindowFocus();
+        }
+
+        /**
+         * <p>Returns the focus state in the drop down.</p>
+         *
+         * @return true always if hijacking focus
+         */
+        @Override
+        public boolean isFocused() {
+            return mHijackFocus || super.isFocused();
+        }
+
+        /**
+         * <p>Returns the focus state in the drop down.</p>
+         *
+         * @return true always if hijacking focus
+         */
+        @Override
+        public boolean hasFocus() {
+            return mHijackFocus || super.hasFocus();
+        }
+    }
+
+    private class PopupDataSetObserver extends DataSetObserver {
+        @Override
+        public void onChanged() {
+            if (isShowing()) {
+                // Resize the popup to fit new content
+                show();
+            }
+        }
+        
+        @Override
+        public void onInvalidated() {
+            dismiss();
+        }
+    }
+
+    private class ListSelectorHider implements Runnable {
+        public void run() {
+            clearListSelection();
+        }
+    }
+
+    private class ResizePopupRunnable implements Runnable {
+        public void run() {
+            mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+            show();
+        }
+    }
+
+    private class PopupTouchInterceptor implements OnTouchListener {
+        public boolean onTouch(View v, MotionEvent event) {
+            final int action = event.getAction();
+            final int x = (int) event.getX();
+            final int y = (int) event.getY();
+            
+            if (action == MotionEvent.ACTION_DOWN &&
+                    mPopup != null && mPopup.isShowing() &&
+                    (x >= 0 && x < getWidth() && y >= 0 && y < getHeight())) {
+                mHandler.postDelayed(mResizePopupRunnable, EXPAND_LIST_TIMEOUT);
+            } else if (action == MotionEvent.ACTION_UP) {
+                mHandler.removeCallbacks(mResizePopupRunnable);
+            }
+            return false;
+        }
+    }
+
+    private class PopupScrollListener implements ListView.OnScrollListener {
+        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+                int totalItemCount) {
+
+        }
+
+        public void onScrollStateChanged(AbsListView view, int scrollState) {
+            if (scrollState == SCROLL_STATE_TOUCH_SCROLL &&
+                    !isInputMethodNotNeeded() && mPopup.getContentView() != null) {
+                mHandler.removeCallbacks(mResizePopupRunnable);
+                mResizePopupRunnable.run();
+            }
+        }
+    }
+}
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 0378328..d404ce7 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -16,27 +16,28 @@
 
 package android.widget;
 
-import com.android.internal.R;
+import java.lang.ref.WeakReference;
 
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.Gravity;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnScrollChangedListener;
-import android.view.View.OnTouchListener;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.StateListDrawable;
 import android.os.IBinder;
 import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+import android.view.View.OnTouchListener;
+import android.view.ViewTreeObserver.OnScrollChangedListener;
 
-import java.lang.ref.WeakReference;
+import com.android.internal.R;
 
 /**
  * <p>A popup window that can be used to display an arbitrary view. The popup
@@ -157,12 +158,21 @@
      * <p>The popup does provide a background.</p>
      */
     public PopupWindow(Context context, AttributeSet attrs, int defStyle) {
+        this(context, attrs, defStyle, 0);
+    }
+    
+    /**
+     * <p>Create a new, empty, non focusable popup window of dimension (0,0).</p>
+     * 
+     * <p>The popup does not provide a background.</p>
+     */
+    public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         mContext = context;
         mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
 
         TypedArray a =
             context.obtainStyledAttributes(
-                attrs, com.android.internal.R.styleable.PopupWindow, defStyle, 0);
+                attrs, com.android.internal.R.styleable.PopupWindow, defStyleAttr, defStyleRes);
 
         mBackground = a.getDrawable(R.styleable.PopupWindow_popupBackground);
 
@@ -1315,6 +1325,7 @@
     }
 
     private class PopupViewContainer extends FrameLayout {
+        private static final String TAG = "PopupWindow.PopupViewContainer";
 
         public PopupViewContainer(Context context) {
             super(context);
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 2f6dd1e..feb4211 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -24,6 +24,7 @@
 import android.content.res.TypedArray;
 import android.database.DataSetObserver;
 import android.util.AttributeSet;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -37,10 +38,20 @@
  */
 @Widget
 public class Spinner extends AbsSpinner implements OnClickListener {
+    private static final String TAG = "Spinner";
     
-    private CharSequence mPrompt;
-    private AlertDialog mPopup;
-
+    /**
+     * Use a dialog window for selecting spinner options.
+     */
+    public static final int MODE_DIALOG = 0;
+    
+    /**
+     * Use a dropdown anchored to the Spinner for selecting spinner options.
+     */
+    public static final int MODE_DROPDOWN = 1;
+    
+    private SpinnerPopup mPopup;
+    
     public Spinner(Context context) {
         this(context, null);
     }
@@ -55,10 +66,43 @@
         TypedArray a = context.obtainStyledAttributes(attrs,
                 com.android.internal.R.styleable.Spinner, defStyle, 0);
         
-        mPrompt = a.getString(com.android.internal.R.styleable.Spinner_prompt);
+        final int mode = a.getInt(com.android.internal.R.styleable.Spinner_spinnerMode,
+                MODE_DIALOG);
+        
+        switch (mode) {
+        case MODE_DIALOG: {
+            mPopup = new DialogPopup();
+            break;
+        }
+        
+        case MODE_DROPDOWN: {
+            final int hintResource = a.getResourceId(
+                    com.android.internal.R.styleable.Spinner_popupPromptView, 0);
+
+            DropdownPopup popup = new DropdownPopup(context, attrs, defStyle, hintResource);
+
+            popup.setBackgroundDrawable(a.getDrawable(
+                    com.android.internal.R.styleable.Spinner_popupBackground));
+            popup.setVerticalOffset(a.getDimensionPixelOffset(
+                    com.android.internal.R.styleable.Spinner_dropDownVerticalOffset, 0));
+            popup.setHorizontalOffset(a.getDimensionPixelOffset(
+                    com.android.internal.R.styleable.Spinner_dropDownHorizontalOffset, 0));
+
+            mPopup = popup;
+            break;
+        }
+        }
+        
+        mPopup.setPromptText(a.getString(com.android.internal.R.styleable.Spinner_prompt));
 
         a.recycle();
     }
+    
+    @Override
+    public void setAdapter(SpinnerAdapter adapter) {
+        super.setAdapter(adapter);
+        mPopup.setAdapter(new DropDownAdapter(adapter));
+    }
 
     @Override
     public int getBaseline() {
@@ -246,15 +290,10 @@
         
         if (!handled) {
             handled = true;
-            Context context = getContext();
-            
-            final DropDownAdapter adapter = new DropDownAdapter(getAdapter());
 
-            AlertDialog.Builder builder = new AlertDialog.Builder(context);
-            if (mPrompt != null) {
-                builder.setTitle(mPrompt);
+            if (!mPopup.isShowing()) {
+                mPopup.show();
             }
-            mPopup = builder.setSingleChoiceItems(adapter, getSelectedItemPosition(), this).show();
         }
 
         return handled;
@@ -271,7 +310,7 @@
      * @param prompt the prompt to set
      */
     public void setPrompt(CharSequence prompt) {
-        mPrompt = prompt;
+        mPopup.setPromptText(prompt);
     }
 
     /**
@@ -279,16 +318,42 @@
      * @param promptId the resource ID of the prompt to display when the dialog is shown
      */
     public void setPromptId(int promptId) {
-        mPrompt = getContext().getText(promptId);
+        setPrompt(getContext().getText(promptId));
     }
 
     /**
      * @return The prompt to display when the dialog is shown
      */
     public CharSequence getPrompt() {
-        return mPrompt;
+        return mPopup.getHintText();
     }
     
+    /*
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (mPopup.onKeyDown(keyCode, event)) {
+            return true;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+    
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (mPopup.onKeyUp(keyCode, event)) {
+            return true;
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+    
+    @Override
+    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+        if (mPopup.onKeyPreIme(keyCode, event)) {
+            return true;
+        }
+        return super.onKeyPreIme(keyCode, event);
+    }
+    */
+    
     /**
      * <p>Wrapper class for an Adapter. Transforms the embedded Adapter instance
      * into a ListAdapter.</p>
@@ -384,4 +449,123 @@
             return getCount() == 0;
         }
     }
+    
+    /**
+     * Implements some sort of popup selection interface for selecting a spinner option.
+     * Allows for different spinner modes.
+     */
+    private interface SpinnerPopup {
+        public void setAdapter(ListAdapter adapter);
+        
+        /**
+         * Show the popup
+         */
+        public void show();
+        
+        /**
+         * Dismiss the popup
+         */
+        public void dismiss();
+        
+        /**
+         * @return true if the popup is showing, false otherwise.
+         */
+        public boolean isShowing();
+        
+        /**
+         * Set hint text to be displayed to the user. This should provide
+         * a description of the choice being made.
+         * @param hintText Hint text to set.
+         */
+        public void setPromptText(CharSequence hintText);
+        public CharSequence getHintText();
+    }
+    
+    private class DialogPopup implements SpinnerPopup, DialogInterface.OnClickListener {
+        private AlertDialog mPopup;
+        private ListAdapter mListAdapter;
+        private CharSequence mPrompt;
+
+        public void dismiss() {
+            mPopup.dismiss();
+            mPopup = null;
+        }
+
+        public boolean isShowing() {
+            return mPopup != null ? mPopup.isShowing() : false;
+        }
+
+        public void setAdapter(ListAdapter adapter) {
+            mListAdapter = adapter;
+        }
+
+        public void setPromptText(CharSequence hintText) {
+            mPrompt = hintText;
+        }
+        
+        public CharSequence getHintText() {
+            return mPrompt;
+        }
+
+        public void show() {
+            AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+            if (mPrompt != null) {
+                builder.setTitle(mPrompt);
+            }
+            mPopup = builder.setSingleChoiceItems(mListAdapter,
+                    getSelectedItemPosition(), this).show();
+        }
+        
+        public void onClick(DialogInterface dialog, int which) {
+            setSelection(which);
+            dismiss();
+        }
+    }
+    
+    private class DropdownPopup extends ListPopupWindow implements SpinnerPopup {
+        private CharSequence mHintText;
+        private TextView mHintView;
+        private int mHintResource;
+        
+        public DropdownPopup(Context context, AttributeSet attrs,
+                int defStyleRes, int hintResource) {
+            super(context, attrs, 0, defStyleRes);
+            
+            mHintResource = hintResource;
+            
+            setAnchorView(Spinner.this);
+            setModal(true);
+            setPromptPosition(POSITION_PROMPT_BELOW);
+            setOnItemClickListener(new OnItemClickListener() {
+                public void onItemClick(AdapterView parent, View v, int position, long id) {
+                    Spinner.this.setSelection(position);
+                    dismiss();
+                }
+            });
+        }
+        
+        public CharSequence getHintText() {
+            return mHintText;
+        }
+        
+        public void setPromptText(CharSequence hintText) {
+            mHintText = hintText;
+            if (mHintView != null) {
+                mHintView.setText(hintText);
+            }
+        }
+        
+        public void show() {
+            if (mHintView == null) {
+                final TextView textView = (TextView) LayoutInflater.from(getContext()).inflate(
+                        mHintResource, null).findViewById(com.android.internal.R.id.text1);
+                textView.setText(mHintText);
+                setPromptView(textView);
+                mHintView = textView;
+            }
+            super.show();
+            getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+            setSelection(Spinner.this.getSelectedItemPosition());
+        }
+    }
 }
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e7b83c7..af8d91c 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2207,6 +2207,30 @@
     <declare-styleable name="Spinner">
         <!-- The prompt to display when the spinner's dialog is shown. -->
         <attr name="prompt" format="reference" />
+        <!-- Display mode for spinner options. -->
+        <attr name="spinnerMode" format="enum">
+            <!-- Spinner options will be presented to the user as a dialog window. -->
+            <enum name="dialog" value="0" />
+            <!-- Spinner options will be presented to the user as an inline dropdown
+                 anchored to the spinner widget itself. -->
+            <enum name="dropdown" value="1" />
+        </attr>
+        <!-- List selector to use for spinnerMode="dropdown" display. -->
+        <attr name="dropDownSelector" />
+        <!-- Background drawable to use for the dropdown in spinnerMode="dropdown". -->
+        <attr name="popupBackground" />
+        <!-- Vertical offset from the spinner widget for positioning the dropdown in
+             spinnerMode="dropdown". -->
+        <attr name="dropDownVerticalOffset" />
+        <!-- Horizontal offset from the spinner widget for positioning the dropdown
+             in spinnerMode="dropdown". -->
+        <attr name="dropDownHorizontalOffset" />
+        <!-- Width of the dropdown in spinnerMode="dropdown". -->
+        <attr name="dropDownWidth" />
+        <!-- Reference to a layout to use for displaying a prompt in the dropdown for
+             spinnerMode="dropdown". This layout must contain a TextView with the id
+             @android:id/text1 to be populated with the prompt text. -->
+        <attr name="popupPromptView" format="reference" />
     </declare-styleable>
     <declare-styleable name="DatePicker">
         <!-- The first year (inclusive), for example "1940". -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 1aa55e2..7ef842f5 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1286,5 +1286,6 @@
   <public type="id" name="home" />
 
   <public type="style" name="Theme.WithActionBar" />
+  <public type="style" name="Widget.Spinner.DropDown" />
 
 </resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index c90770c5..4f74413 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -451,6 +451,18 @@
     <style name="Widget.Spinner">
         <item name="android:background">@android:drawable/btn_dropdown</item>
         <item name="android:clickable">true</item>
+        <item name="android:spinnerMode">dialog</item>
+
+        <item name="android:dropDownSelector">@android:drawable/list_selector_background</item>
+        <item name="android:popupBackground">@android:drawable/spinner_dropdown_background</item>
+        <item name="android:dropDownVerticalOffset">-10dip</item>
+        <item name="android:dropDownHorizontalOffset">0dip</item>
+        <item name="android:dropDownWidth">wrap_content</item>
+        <item name="android:popupPromptView">@android:layout/simple_dropdown_hint</item>
+    </style>
+
+    <style name="Widget.Spinner.DropDown">
+        <item name="android:spinnerMode">dropdown</item>
     </style>
 
     <style name="Widget.TextView.PopupMenu">
diff --git a/preloaded-classes b/preloaded-classes
index 4098cbf..da5502d 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -563,8 +563,6 @@
 android.widget.AdapterView
 android.widget.ArrayAdapter
 android.widget.AutoCompleteTextView
-android.widget.AutoCompleteTextView$DropDownItemClickListener
-android.widget.AutoCompleteTextView$DropDownListView
 android.widget.BaseAdapter
 android.widget.BaseExpandableListAdapter
 android.widget.CheckBox
@@ -582,6 +580,7 @@
 android.widget.ImageView
 android.widget.ImageView$ScaleType
 android.widget.LinearLayout
+android.widget.ListPopupWindow
 android.widget.ListView
 android.widget.ListView$SavedState
 android.widget.MediaController