Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.widget;
     18 
     19 import com.android.internal.R;
     20 
     21 import android.content.Context;
     22 import android.content.res.Resources;
     23 import android.content.res.TypedArray;
     24 import android.graphics.PixelFormat;
     25 import android.graphics.Rect;
     26 import android.graphics.drawable.Drawable;
     27 import android.graphics.drawable.StateListDrawable;
     28 import android.os.Build;
     29 import android.os.IBinder;
     30 import android.util.AttributeSet;
     31 import android.view.Gravity;
     32 import android.view.KeyEvent;
     33 import android.view.MotionEvent;
     34 import android.view.View;
     35 import android.view.View.OnTouchListener;
     36 import android.view.ViewGroup;
     37 import android.view.ViewTreeObserver;
     38 import android.view.ViewTreeObserver.OnScrollChangedListener;
     39 import android.view.WindowManager;
     40 
     41 import java.lang.ref.WeakReference;
     42 
     43 /**
     44  * <p>A popup window that can be used to display an arbitrary view. The popup
     45  * window is a floating container that appears on top of the current
     46  * activity.</p>
     47  *
     48  * @see android.widget.AutoCompleteTextView
     49  * @see android.widget.Spinner
     50  */
     51 public class PopupWindow {
     52     /**
     53      * Mode for {@link #setInputMethodMode(int)}: the requirements for the
     54      * input method should be based on the focusability of the popup.  That is
     55      * if it is focusable than it needs to work with the input method, else
     56      * it doesn't.
     57      */
     58     public static final int INPUT_METHOD_FROM_FOCUSABLE = 0;
     59 
     60     /**
     61      * Mode for {@link #setInputMethodMode(int)}: this popup always needs to
     62      * work with an input method, regardless of whether it is focusable.  This
     63      * means that it will always be displayed so that the user can also operate
     64      * the input method while it is shown.
     65      */
     66     public static final int INPUT_METHOD_NEEDED = 1;
     67 
     68     /**
     69      * Mode for {@link #setInputMethodMode(int)}: this popup never needs to
     70      * work with an input method, regardless of whether it is focusable.  This
     71      * means that it will always be displayed to use as much space on the
     72      * screen as needed, regardless of whether this covers the input method.
     73      */
     74     public static final int INPUT_METHOD_NOT_NEEDED = 2;
     75 
     76     private static final int DEFAULT_ANCHORED_GRAVITY = Gravity.TOP | Gravity.START;
     77 
     78     private Context mContext;
     79     private WindowManager mWindowManager;
     80 
     81     private boolean mIsShowing;
     82     private boolean mIsDropdown;
     83 
     84     private View mContentView;
     85     private View mPopupView;
     86     private boolean mFocusable;
     87     private int mInputMethodMode = INPUT_METHOD_FROM_FOCUSABLE;
     88     private int mSoftInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
     89     private boolean mTouchable = true;
     90     private boolean mOutsideTouchable = false;
     91     private boolean mClippingEnabled = true;
     92     private int mSplitTouchEnabled = -1;
     93     private boolean mLayoutInScreen;
     94     private boolean mClipToScreen;
     95     private boolean mAllowScrollingAnchorParent = true;
     96     private boolean mLayoutInsetDecor = false;
     97     private boolean mNotTouchModal;
     98 
     99     private OnTouchListener mTouchInterceptor;
    100 
    101     private int mWidthMode;
    102     private int mWidth;
    103     private int mLastWidth;
    104     private int mHeightMode;
    105     private int mHeight;
    106     private int mLastHeight;
    107 
    108     private int mPopupWidth;
    109     private int mPopupHeight;
    110 
    111     private int[] mDrawingLocation = new int[2];
    112     private int[] mScreenLocation = new int[2];
    113     private Rect mTempRect = new Rect();
    114 
    115     private Drawable mBackground;
    116     private Drawable mAboveAnchorBackgroundDrawable;
    117     private Drawable mBelowAnchorBackgroundDrawable;
    118 
    119     private boolean mAboveAnchor;
    120     private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
    121 
    122     private OnDismissListener mOnDismissListener;
    123     private boolean mIgnoreCheekPress = false;
    124 
    125     private int mAnimationStyle = -1;
    126 
    127     private static final int[] ABOVE_ANCHOR_STATE_SET = new int[] {
    128         com.android.internal.R.attr.state_above_anchor
    129     };
    130 
    131     private WeakReference<View> mAnchor;
    132     private OnScrollChangedListener mOnScrollChangedListener =
    133         new OnScrollChangedListener() {
    134             public void onScrollChanged() {
    135                 View anchor = mAnchor != null ? mAnchor.get() : null;
    136                 if (anchor != null && mPopupView != null) {
    137                     WindowManager.LayoutParams p = (WindowManager.LayoutParams)
    138                             mPopupView.getLayoutParams();
    139 
    140                     updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
    141                             mAnchoredGravity));
    142                     update(p.x, p.y, -1, -1, true);
    143                 }
    144             }
    145         };
    146     private int mAnchorXoff, mAnchorYoff, mAnchoredGravity;
    147 
    148     private boolean mPopupViewInitialLayoutDirectionInherited;
    149 
    150     /**
    151      * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
    152      *
    153      * <p>The popup does provide a background.</p>
    154      */
    155     public PopupWindow(Context context) {
    156         this(context, null);
    157     }
    158 
    159     /**
    160      * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
    161      *
    162      * <p>The popup does provide a background.</p>
    163      */
    164     public PopupWindow(Context context, AttributeSet attrs) {
    165         this(context, attrs, com.android.internal.R.attr.popupWindowStyle);
    166     }
    167 
    168     /**
    169      * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
    170      *
    171      * <p>The popup does provide a background.</p>
    172      */
    173     public PopupWindow(Context context, AttributeSet attrs, int defStyle) {
    174         this(context, attrs, defStyle, 0);
    175     }
    176 
    177     /**
    178      * <p>Create a new, empty, non focusable popup window of dimension (0,0).</p>
    179      *
    180      * <p>The popup does not provide a background.</p>
    181      */
    182     public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    183         mContext = context;
    184         mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
    185 
    186         TypedArray a =
    187             context.obtainStyledAttributes(
    188                 attrs, com.android.internal.R.styleable.PopupWindow, defStyleAttr, defStyleRes);
    189 
    190         mBackground = a.getDrawable(R.styleable.PopupWindow_popupBackground);
    191 
    192         final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, -1);
    193         mAnimationStyle = animStyle == com.android.internal.R.style.Animation_PopupWindow ? -1 :
    194                 animStyle;
    195 
    196         // If this is a StateListDrawable, try to find and store the drawable to be
    197         // used when the drop-down is placed above its anchor view, and the one to be
    198         // used when the drop-down is placed below its anchor view. We extract
    199         // the drawables ourselves to work around a problem with using refreshDrawableState
    200         // that it will take into account the padding of all drawables specified in a
    201         // StateListDrawable, thus adding superfluous padding to drop-down views.
    202         //
    203         // We assume a StateListDrawable will have a drawable for ABOVE_ANCHOR_STATE_SET and
    204         // at least one other drawable, intended for the 'below-anchor state'.
    205         if (mBackground instanceof StateListDrawable) {
    206             StateListDrawable background = (StateListDrawable) mBackground;
    207 
    208             // Find the above-anchor view - this one's easy, it should be labeled as such.
    209             int aboveAnchorStateIndex = background.getStateDrawableIndex(ABOVE_ANCHOR_STATE_SET);
    210 
    211             // Now, for the below-anchor view, look for any other drawable specified in the
    212             // StateListDrawable which is not for the above-anchor state and use that.
    213             int count = background.getStateCount();
    214             int belowAnchorStateIndex = -1;
    215             for (int i = 0; i < count; i++) {
    216                 if (i != aboveAnchorStateIndex) {
    217                     belowAnchorStateIndex = i;
    218                     break;
    219                 }
    220             }
    221 
    222             // Store the drawables we found, if we found them. Otherwise, set them both
    223             // to null so that we'll just use refreshDrawableState.
    224             if (aboveAnchorStateIndex != -1 && belowAnchorStateIndex != -1) {
    225                 mAboveAnchorBackgroundDrawable = background.getStateDrawable(aboveAnchorStateIndex);
    226                 mBelowAnchorBackgroundDrawable = background.getStateDrawable(belowAnchorStateIndex);
    227             } else {
    228                 mBelowAnchorBackgroundDrawable = null;
    229                 mAboveAnchorBackgroundDrawable = null;
    230             }
    231         }
    232 
    233         a.recycle();
    234     }
    235 
    236     /**
    237      * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
    238      *
    239      * <p>The popup does not provide any background. This should be handled
    240      * by the content view.</p>
    241      */
    242     public PopupWindow() {
    243         this(null, 0, 0);
    244     }
    245 
    246     /**
    247      * <p>Create a new non focusable popup window which can display the
    248      * <tt>contentView</tt>. The dimension of the window are (0,0).</p>
    249      *
    250      * <p>The popup does not provide any background. This should be handled
    251      * by the content view.</p>
    252      *
    253      * @param contentView the popup's content
    254      */
    255     public PopupWindow(View contentView) {
    256         this(contentView, 0, 0);
    257     }
    258 
    259     /**
    260      * <p>Create a new empty, non focusable popup window. The dimension of the
    261      * window must be passed to this constructor.</p>
    262      *
    263      * <p>The popup does not provide any background. This should be handled
    264      * by the content view.</p>
    265      *
    266      * @param width the popup's width
    267      * @param height the popup's height
    268      */
    269     public PopupWindow(int width, int height) {
    270         this(null, width, height);
    271     }
    272 
    273     /**
    274      * <p>Create a new non focusable popup window which can display the
    275      * <tt>contentView</tt>. The dimension of the window must be passed to
    276      * this constructor.</p>
    277      *
    278      * <p>The popup does not provide any background. This should be handled
    279      * by the content view.</p>
    280      *
    281      * @param contentView the popup's content
    282      * @param width the popup's width
    283      * @param height the popup's height
    284      */
    285     public PopupWindow(View contentView, int width, int height) {
    286         this(contentView, width, height, false);
    287     }
    288 
    289     /**
    290      * <p>Create a new popup window which can display the <tt>contentView</tt>.
    291      * The dimension of the window must be passed to this constructor.</p>
    292      *
    293      * <p>The popup does not provide any background. This should be handled
    294      * by the content view.</p>
    295      *
    296      * @param contentView the popup's content
    297      * @param width the popup's width
    298      * @param height the popup's height
    299      * @param focusable true if the popup can be focused, false otherwise
    300      */
    301     public PopupWindow(View contentView, int width, int height, boolean focusable) {
    302         if (contentView != null) {
    303             mContext = contentView.getContext();
    304             mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
    305         }
    306         setContentView(contentView);
    307         setWidth(width);
    308         setHeight(height);
    309         setFocusable(focusable);
    310     }
    311 
    312     /**
    313      * <p>Return the drawable used as the popup window's background.</p>
    314      *
    315      * @return the background drawable or null
    316      */
    317     public Drawable getBackground() {
    318         return mBackground;
    319     }
    320 
    321     /**
    322      * <p>Change the background drawable for this popup window. The background
    323      * can be set to null.</p>
    324      *
    325      * @param background the popup's background
    326      */
    327     public void setBackgroundDrawable(Drawable background) {
    328         mBackground = background;
    329     }
    330 
    331     /**
    332      * <p>Return the animation style to use the popup appears and disappears</p>
    333      *
    334      * @return the animation style to use the popup appears and disappears
    335      */
    336     public int getAnimationStyle() {
    337         return mAnimationStyle;
    338     }
    339 
    340     /**
    341      * Set the flag on popup to ignore cheek press eventt; by default this flag
    342      * is set to false
    343      * which means the pop wont ignore cheek press dispatch events.
    344      *
    345      * <p>If the popup is showing, calling this method will take effect only
    346      * the next time the popup is shown or through a manual call to one of
    347      * the {@link #update()} methods.</p>
    348      *
    349      * @see #update()
    350      */
    351     public void setIgnoreCheekPress() {
    352         mIgnoreCheekPress = true;
    353     }
    354 
    355 
    356     /**
    357      * <p>Change the animation style resource for this popup.</p>
    358      *
    359      * <p>If the popup is showing, calling this method will take effect only
    360      * the next time the popup is shown or through a manual call to one of
    361      * the {@link #update()} methods.</p>
    362      *
    363      * @param animationStyle animation style to use when the popup appears
    364      *      and disappears.  Set to -1 for the default animation, 0 for no
    365      *      animation, or a resource identifier for an explicit animation.
    366      *
    367      * @see #update()
    368      */
    369     public void setAnimationStyle(int animationStyle) {
    370         mAnimationStyle = animationStyle;
    371     }
    372 
    373     /**
    374      * <p>Return the view used as the content of the popup window.</p>
    375      *
    376      * @return a {@link android.view.View} representing the popup's content
    377      *
    378      * @see #setContentView(android.view.View)
    379      */
    380     public View getContentView() {
    381         return mContentView;
    382     }
    383 
    384     /**
    385      * <p>Change the popup's content. The content is represented by an instance
    386      * of {@link android.view.View}.</p>
    387      *
    388      * <p>This method has no effect if called when the popup is showing.</p>
    389      *
    390      * @param contentView the new content for the popup
    391      *
    392      * @see #getContentView()
    393      * @see #isShowing()
    394      */
    395     public void setContentView(View contentView) {
    396         if (isShowing()) {
    397             return;
    398         }
    399 
    400         mContentView = contentView;
    401 
    402         if (mContext == null && mContentView != null) {
    403             mContext = mContentView.getContext();
    404         }
    405 
    406         if (mWindowManager == null && mContentView != null) {
    407             mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
    408         }
    409     }
    410 
    411     /**
    412      * Set a callback for all touch events being dispatched to the popup
    413      * window.
    414      */
    415     public void setTouchInterceptor(OnTouchListener l) {
    416         mTouchInterceptor = l;
    417     }
    418 
    419     /**
    420      * <p>Indicate whether the popup window can grab the focus.</p>
    421      *
    422      * @return true if the popup is focusable, false otherwise
    423      *
    424      * @see #setFocusable(boolean)
    425      */
    426     public boolean isFocusable() {
    427         return mFocusable;
    428     }
    429 
    430     /**
    431      * <p>Changes the focusability of the popup window. When focusable, the
    432      * window will grab the focus from the current focused widget if the popup
    433      * contains a focusable {@link android.view.View}.  By default a popup
    434      * window is not focusable.</p>
    435      *
    436      * <p>If the popup is showing, calling this method will take effect only
    437      * the next time the popup is shown or through a manual call to one of
    438      * the {@link #update()} methods.</p>
    439      *
    440      * @param focusable true if the popup should grab focus, false otherwise.
    441      *
    442      * @see #isFocusable()
    443      * @see #isShowing()
    444      * @see #update()
    445      */
    446     public void setFocusable(boolean focusable) {
    447         mFocusable = focusable;
    448     }
    449 
    450     /**
    451      * Return the current value in {@link #setInputMethodMode(int)}.
    452      *
    453      * @see #setInputMethodMode(int)
    454      */
    455     public int getInputMethodMode() {
    456         return mInputMethodMode;
    457 
    458     }
    459 
    460     /**
    461      * Control how the popup operates with an input method: one of
    462      * {@link #INPUT_METHOD_FROM_FOCUSABLE}, {@link #INPUT_METHOD_NEEDED},
    463      * or {@link #INPUT_METHOD_NOT_NEEDED}.
    464      *
    465      * <p>If the popup is showing, calling this method will take effect only
    466      * the next time the popup is shown or through a manual call to one of
    467      * the {@link #update()} methods.</p>
    468      *
    469      * @see #getInputMethodMode()
    470      * @see #update()
    471      */
    472     public void setInputMethodMode(int mode) {
    473         mInputMethodMode = mode;
    474     }
    475 
    476     /**
    477      * Sets the operating mode for the soft input area.
    478      *
    479      * @param mode The desired mode, see
    480      *        {@link android.view.WindowManager.LayoutParams#softInputMode}
    481      *        for the full list
    482      *
    483      * @see android.view.WindowManager.LayoutParams#softInputMode
    484      * @see #getSoftInputMode()
    485      */
    486     public void setSoftInputMode(int mode) {
    487         mSoftInputMode = mode;
    488     }
    489 
    490     /**
    491      * Returns the current value in {@link #setSoftInputMode(int)}.
    492      *
    493      * @see #setSoftInputMode(int)
    494      * @see android.view.WindowManager.LayoutParams#softInputMode
    495      */
    496     public int getSoftInputMode() {
    497         return mSoftInputMode;
    498     }
    499 
    500     /**
    501      * <p>Indicates whether the popup window receives touch events.</p>
    502      *
    503      * @return true if the popup is touchable, false otherwise
    504      *
    505      * @see #setTouchable(boolean)
    506      */
    507     public boolean isTouchable() {
    508         return mTouchable;
    509     }
    510 
    511     /**
    512      * <p>Changes the touchability of the popup window. When touchable, the
    513      * window will receive touch events, otherwise touch events will go to the
    514      * window below it. By default the window is touchable.</p>
    515      *
    516      * <p>If the popup is showing, calling this method will take effect only
    517      * the next time the popup is shown or through a manual call to one of
    518      * the {@link #update()} methods.</p>
    519      *
    520      * @param touchable true if the popup should receive touch events, false otherwise
    521      *
    522      * @see #isTouchable()
    523      * @see #isShowing()
    524      * @see #update()
    525      */
    526     public void setTouchable(boolean touchable) {
    527         mTouchable = touchable;
    528     }
    529 
    530     /**
    531      * <p>Indicates whether the popup window will be informed of touch events
    532      * outside of its window.</p>
    533      *
    534      * @return true if the popup is outside touchable, false otherwise
    535      *
    536      * @see #setOutsideTouchable(boolean)
    537      */
    538     public boolean isOutsideTouchable() {
    539         return mOutsideTouchable;
    540     }
    541 
    542     /**
    543      * <p>Controls whether the pop-up will be informed of touch events outside
    544      * of its window.  This only makes sense for pop-ups that are touchable
    545      * but not focusable, which means touches outside of the window will
    546      * be delivered to the window behind.  The default is false.</p>
    547      *
    548      * <p>If the popup is showing, calling this method will take effect only
    549      * the next time the popup is shown or through a manual call to one of
    550      * the {@link #update()} methods.</p>
    551      *
    552      * @param touchable true if the popup should receive outside
    553      * touch events, false otherwise
    554      *
    555      * @see #isOutsideTouchable()
    556      * @see #isShowing()
    557      * @see #update()
    558      */
    559     public void setOutsideTouchable(boolean touchable) {
    560         mOutsideTouchable = touchable;
    561     }
    562 
    563     /**
    564      * <p>Indicates whether clipping of the popup window is enabled.</p>
    565      *
    566      * @return true if the clipping is enabled, false otherwise
    567      *
    568      * @see #setClippingEnabled(boolean)
    569      */
    570     public boolean isClippingEnabled() {
    571         return mClippingEnabled;
    572     }
    573 
    574     /**
    575      * <p>Allows the popup window to extend beyond the bounds of the screen. By default the
    576      * window is clipped to the screen boundaries. Setting this to false will allow windows to be
    577      * accurately positioned.</p>
    578      *
    579      * <p>If the popup is showing, calling this method will take effect only
    580      * the next time the popup is shown or through a manual call to one of
    581      * the {@link #update()} methods.</p>
    582      *
    583      * @param enabled false if the window should be allowed to extend outside of the screen
    584      * @see #isShowing()
    585      * @see #isClippingEnabled()
    586      * @see #update()
    587      */
    588     public void setClippingEnabled(boolean enabled) {
    589         mClippingEnabled = enabled;
    590     }
    591 
    592     /**
    593      * Clip this popup window to the screen, but not to the containing window.
    594      *
    595      * @param enabled True to clip to the screen.
    596      * @hide
    597      */
    598     public void setClipToScreenEnabled(boolean enabled) {
    599         mClipToScreen = enabled;
    600         setClippingEnabled(!enabled);
    601     }
    602 
    603     /**
    604      * Allow PopupWindow to scroll the anchor's parent to provide more room
    605      * for the popup. Enabled by default.
    606      *
    607      * @param enabled True to scroll the anchor's parent when more room is desired by the popup.
    608      */
    609     void setAllowScrollingAnchorParent(boolean enabled) {
    610         mAllowScrollingAnchorParent = enabled;
    611     }
    612 
    613     /**
    614      * <p>Indicates whether the popup window supports splitting touches.</p>
    615      *
    616      * @return true if the touch splitting is enabled, false otherwise
    617      *
    618      * @see #setSplitTouchEnabled(boolean)
    619      */
    620     public boolean isSplitTouchEnabled() {
    621         if (mSplitTouchEnabled < 0 && mContext != null) {
    622             return mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB;
    623         }
    624         return mSplitTouchEnabled == 1;
    625     }
    626 
    627     /**
    628      * <p>Allows the popup window to split touches across other windows that also
    629      * support split touch.  When this flag is false, the first pointer
    630      * that goes down determines the window to which all subsequent touches
    631      * go until all pointers go up.  When this flag is true, each pointer
    632      * (not necessarily the first) that goes down determines the window
    633      * to which all subsequent touches of that pointer will go until that
    634      * pointer goes up thereby enabling touches with multiple pointers
    635      * to be split across multiple windows.</p>
    636      *
    637      * @param enabled true if the split touches should be enabled, false otherwise
    638      * @see #isSplitTouchEnabled()
    639      */
    640     public void setSplitTouchEnabled(boolean enabled) {
    641         mSplitTouchEnabled = enabled ? 1 : 0;
    642     }
    643 
    644     /**
    645      * <p>Indicates whether the popup window will be forced into using absolute screen coordinates
    646      * for positioning.</p>
    647      *
    648      * @return true if the window will always be positioned in screen coordinates.
    649      * @hide
    650      */
    651     public boolean isLayoutInScreenEnabled() {
    652         return mLayoutInScreen;
    653     }
    654 
    655     /**
    656      * <p>Allows the popup window to force the flag
    657      * {@link WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN}, overriding default behavior.
    658      * This will cause the popup to be positioned in absolute screen coordinates.</p>
    659      *
    660      * @param enabled true if the popup should always be positioned in screen coordinates
    661      * @hide
    662      */
    663     public void setLayoutInScreenEnabled(boolean enabled) {
    664         mLayoutInScreen = enabled;
    665     }
    666 
    667     /**
    668      * Allows the popup window to force the flag
    669      * {@link WindowManager.LayoutParams#FLAG_LAYOUT_INSET_DECOR}, overriding default behavior.
    670      * This will cause the popup to inset its content to account for system windows overlaying
    671      * the screen, such as the status bar.
    672      *
    673      * <p>This will often be combined with {@link #setLayoutInScreenEnabled(boolean)}.
    674      *
    675      * @param enabled true if the popup's views should inset content to account for system windows,
    676      *                the way that decor views behave for full-screen windows.
    677      * @hide
    678      */
    679     public void setLayoutInsetDecor(boolean enabled) {
    680         mLayoutInsetDecor = enabled;
    681     }
    682 
    683     /**
    684      * Set the layout type for this window. Should be one of the TYPE constants defined in
    685      * {@link WindowManager.LayoutParams}.
    686      *
    687      * @param layoutType Layout type for this window.
    688      * @hide
    689      */
    690     public void setWindowLayoutType(int layoutType) {
    691         mWindowLayoutType = layoutType;
    692     }
    693 
    694     /**
    695      * @return The layout type for this window.
    696      * @hide
    697      */
    698     public int getWindowLayoutType() {
    699         return mWindowLayoutType;
    700     }
    701 
    702     /**
    703      * Set whether this window is touch modal or if outside touches will be sent to
    704      * other windows behind it.
    705      * @hide
    706      */
    707     public void setTouchModal(boolean touchModal) {
    708         mNotTouchModal = !touchModal;
    709     }
    710 
    711     /**
    712      * <p>Change the width and height measure specs that are given to the
    713      * window manager by the popup.  By default these are 0, meaning that
    714      * the current width or height is requested as an explicit size from
    715      * the window manager.  You can supply
    716      * {@link ViewGroup.LayoutParams#WRAP_CONTENT} or
    717      * {@link ViewGroup.LayoutParams#MATCH_PARENT} to have that measure
    718      * spec supplied instead, replacing the absolute width and height that
    719      * has been set in the popup.</p>
    720      *
    721      * <p>If the popup is showing, calling this method will take effect only
    722      * the next time the popup is shown.</p>
    723      *
    724      * @param widthSpec an explicit width measure spec mode, either
    725      * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
    726      * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
    727      * width.
    728      * @param heightSpec an explicit height measure spec mode, either
    729      * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
    730      * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
    731      * height.
    732      */
    733     public void setWindowLayoutMode(int widthSpec, int heightSpec) {
    734         mWidthMode = widthSpec;
    735         mHeightMode = heightSpec;
    736     }
    737 
    738     /**
    739      * <p>Return this popup's height MeasureSpec</p>
    740      *
    741      * @return the height MeasureSpec of the popup
    742      *
    743      * @see #setHeight(int)
    744      */
    745     public int getHeight() {
    746         return mHeight;
    747     }
    748 
    749     /**
    750      * <p>Change the popup's height MeasureSpec</p>
    751      *
    752      * <p>If the popup is showing, calling this method will take effect only
    753      * the next time the popup is shown.</p>
    754      *
    755      * @param height the height MeasureSpec of the popup
    756      *
    757      * @see #getHeight()
    758      * @see #isShowing()
    759      */
    760     public void setHeight(int height) {
    761         mHeight = height;
    762     }
    763 
    764     /**
    765      * <p>Return this popup's width MeasureSpec</p>
    766      *
    767      * @return the width MeasureSpec of the popup
    768      *
    769      * @see #setWidth(int)
    770      */
    771     public int getWidth() {
    772         return mWidth;
    773     }
    774 
    775     /**
    776      * <p>Change the popup's width MeasureSpec</p>
    777      *
    778      * <p>If the popup is showing, calling this method will take effect only
    779      * the next time the popup is shown.</p>
    780      *
    781      * @param width the width MeasureSpec of the popup
    782      *
    783      * @see #getWidth()
    784      * @see #isShowing()
    785      */
    786     public void setWidth(int width) {
    787         mWidth = width;
    788     }
    789 
    790     /**
    791      * <p>Indicate whether this popup window is showing on screen.</p>
    792      *
    793      * @return true if the popup is showing, false otherwise
    794      */
    795     public boolean isShowing() {
    796         return mIsShowing;
    797     }
    798 
    799     /**
    800      * <p>
    801      * Display the content view in a popup window at the specified location. If the popup window
    802      * cannot fit on screen, it will be clipped. See {@link android.view.WindowManager.LayoutParams}
    803      * for more information on how gravity and the x and y parameters are related. Specifying
    804      * a gravity of {@link android.view.Gravity#NO_GRAVITY} is similar to specifying
    805      * <code>Gravity.LEFT | Gravity.TOP</code>.
    806      * </p>
    807      *
    808      * @param parent a parent view to get the {@link android.view.View#getWindowToken()} token from
    809      * @param gravity the gravity which controls the placement of the popup window
    810      * @param x the popup's x location offset
    811      * @param y the popup's y location offset
    812      */
    813     public void showAtLocation(View parent, int gravity, int x, int y) {
    814         showAtLocation(parent.getWindowToken(), gravity, x, y);
    815     }
    816 
    817     /**
    818      * Display the content view in a popup window at the specified location.
    819      *
    820      * @param token Window token to use for creating the new window
    821      * @param gravity the gravity which controls the placement of the popup window
    822      * @param x the popup's x location offset
    823      * @param y the popup's y location offset
    824      *
    825      * @hide Internal use only. Applications should use
    826      *       {@link #showAtLocation(View, int, int, int)} instead.
    827      */
    828     public void showAtLocation(IBinder token, int gravity, int x, int y) {
    829         if (isShowing() || mContentView == null) {
    830             return;
    831         }
    832 
    833         unregisterForScrollChanged();
    834 
    835         mIsShowing = true;
    836         mIsDropdown = false;
    837 
    838         WindowManager.LayoutParams p = createPopupLayout(token);
    839         p.windowAnimations = computeAnimationResource();
    840 
    841         preparePopup(p);
    842         if (gravity == Gravity.NO_GRAVITY) {
    843             gravity = Gravity.TOP | Gravity.START;
    844         }
    845         p.gravity = gravity;
    846         p.x = x;
    847         p.y = y;
    848         if (mHeightMode < 0) p.height = mLastHeight = mHeightMode;
    849         if (mWidthMode < 0) p.width = mLastWidth = mWidthMode;
    850         invokePopup(p);
    851     }
    852 
    853     /**
    854      * <p>Display the content view in a popup window anchored to the bottom-left
    855      * corner of the anchor view. If there is not enough room on screen to show
    856      * the popup in its entirety, this method tries to find a parent scroll
    857      * view to scroll. If no parent scroll view can be scrolled, the bottom-left
    858      * corner of the popup is pinned at the top left corner of the anchor view.</p>
    859      *
    860      * @param anchor the view on which to pin the popup window
    861      *
    862      * @see #dismiss()
    863      */
    864     public void showAsDropDown(View anchor) {
    865         showAsDropDown(anchor, 0, 0);
    866     }
    867 
    868     /**
    869      * <p>Display the content view in a popup window anchored to the bottom-left
    870      * corner of the anchor view offset by the specified x and y coordinates.
    871      * If there is not enough room on screen to show
    872      * the popup in its entirety, this method tries to find a parent scroll
    873      * view to scroll. If no parent scroll view can be scrolled, the bottom-left
    874      * corner of the popup is pinned at the top left corner of the anchor view.</p>
    875      * <p>If the view later scrolls to move <code>anchor</code> to a different
    876      * location, the popup will be moved correspondingly.</p>
    877      *
    878      * @param anchor the view on which to pin the popup window
    879      * @param xoff A horizontal offset from the anchor in pixels
    880      * @param yoff A vertical offset from the anchor in pixels
    881      *
    882      * @see #dismiss()
    883      */
    884     public void showAsDropDown(View anchor, int xoff, int yoff) {
    885         showAsDropDown(anchor, xoff, yoff, DEFAULT_ANCHORED_GRAVITY);
    886     }
    887 
    888     /**
    889      * <p>Display the content view in a popup window anchored to the bottom-left
    890      * corner of the anchor view offset by the specified x and y coordinates.
    891      * If there is not enough room on screen to show
    892      * the popup in its entirety, this method tries to find a parent scroll
    893      * view to scroll. If no parent scroll view can be scrolled, the bottom-left
    894      * corner of the popup is pinned at the top left corner of the anchor view.</p>
    895      * <p>If the view later scrolls to move <code>anchor</code> to a different
    896      * location, the popup will be moved correspondingly.</p>
    897      *
    898      * @param anchor the view on which to pin the popup window
    899      * @param xoff A horizontal offset from the anchor in pixels
    900      * @param yoff A vertical offset from the anchor in pixels
    901      * @param gravity Alignment of the popup relative to the anchor
    902      *
    903      * @see #dismiss()
    904      */
    905     public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
    906         if (isShowing() || mContentView == null) {
    907             return;
    908         }
    909 
    910         registerForScrollChanged(anchor, xoff, yoff, gravity);
    911 
    912         mIsShowing = true;
    913         mIsDropdown = true;
    914 
    915         WindowManager.LayoutParams p = createPopupLayout(anchor.getWindowToken());
    916         preparePopup(p);
    917 
    918         updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff, gravity));
    919 
    920         if (mHeightMode < 0) p.height = mLastHeight = mHeightMode;
    921         if (mWidthMode < 0) p.width = mLastWidth = mWidthMode;
    922 
    923         p.windowAnimations = computeAnimationResource();
    924 
    925         invokePopup(p);
    926     }
    927 
    928     private void updateAboveAnchor(boolean aboveAnchor) {
    929         if (aboveAnchor != mAboveAnchor) {
    930             mAboveAnchor = aboveAnchor;
    931 
    932             if (mBackground != null) {
    933                 // If the background drawable provided was a StateListDrawable with above-anchor
    934                 // and below-anchor states, use those. Otherwise rely on refreshDrawableState to
    935                 // do the job.
    936                 if (mAboveAnchorBackgroundDrawable != null) {
    937                     if (mAboveAnchor) {
    938                         mPopupView.setBackgroundDrawable(mAboveAnchorBackgroundDrawable);
    939                     } else {
    940                         mPopupView.setBackgroundDrawable(mBelowAnchorBackgroundDrawable);
    941                     }
    942                 } else {
    943                     mPopupView.refreshDrawableState();
    944                 }
    945             }
    946         }
    947     }
    948 
    949     /**
    950      * Indicates whether the popup is showing above (the y coordinate of the popup's bottom
    951      * is less than the y coordinate of the anchor) or below the anchor view (the y coordinate
    952      * of the popup is greater than y coordinate of the anchor's bottom).
    953      *
    954      * The value returned
    955      * by this method is meaningful only after {@link #showAsDropDown(android.view.View)}
    956      * or {@link #showAsDropDown(android.view.View, int, int)} was invoked.
    957      *
    958      * @return True if this popup is showing above the anchor view, false otherwise.
    959      */
    960     public boolean isAboveAnchor() {
    961         return mAboveAnchor;
    962     }
    963 
    964     /**
    965      * <p>Prepare the popup by embedding in into a new ViewGroup if the
    966      * background drawable is not null. If embedding is required, the layout
    967      * parameters' height is mnodified to take into account the background's
    968      * padding.</p>
    969      *
    970      * @param p the layout parameters of the popup's content view
    971      */
    972     private void preparePopup(WindowManager.LayoutParams p) {
    973         if (mContentView == null || mContext == null || mWindowManager == null) {
    974             throw new IllegalStateException("You must specify a valid content view by "
    975                     + "calling setContentView() before attempting to show the popup.");
    976         }
    977 
    978         if (mBackground != null) {
    979             final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
    980             int height = ViewGroup.LayoutParams.MATCH_PARENT;
    981             if (layoutParams != null &&
    982                     layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
    983                 height = ViewGroup.LayoutParams.WRAP_CONTENT;
    984             }
    985 
    986             // when a background is available, we embed the content view
    987             // within another view that owns the background drawable
    988             PopupViewContainer popupViewContainer = new PopupViewContainer(mContext);
    989             PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(
    990                     ViewGroup.LayoutParams.MATCH_PARENT, height
    991             );
    992             popupViewContainer.setBackgroundDrawable(mBackground);
    993             popupViewContainer.addView(mContentView, listParams);
    994 
    995             mPopupView = popupViewContainer;
    996         } else {
    997             mPopupView = mContentView;
    998         }
    999         mPopupViewInitialLayoutDirectionInherited =
   1000                 (mPopupView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
   1001         mPopupWidth = p.width;
   1002         mPopupHeight = p.height;
   1003     }
   1004 
   1005     /**
   1006      * <p>Invoke the popup window by adding the content view to the window
   1007      * manager.</p>
   1008      *
   1009      * <p>The content view must be non-null when this method is invoked.</p>
   1010      *
   1011      * @param p the layout parameters of the popup's content view
   1012      */
   1013     private void invokePopup(WindowManager.LayoutParams p) {
   1014         if (mContext != null) {
   1015             p.packageName = mContext.getPackageName();
   1016         }
   1017         mPopupView.setFitsSystemWindows(mLayoutInsetDecor);
   1018         setLayoutDirectionFromAnchor();
   1019         mWindowManager.addView(mPopupView, p);
   1020     }
   1021 
   1022     private void setLayoutDirectionFromAnchor() {
   1023         if (mAnchor != null) {
   1024             View anchor = mAnchor.get();
   1025             if (anchor != null && mPopupViewInitialLayoutDirectionInherited) {
   1026                 mPopupView.setLayoutDirection(anchor.getLayoutDirection());
   1027             }
   1028         }
   1029     }
   1030 
   1031     /**
   1032      * <p>Generate the layout parameters for the popup window.</p>
   1033      *
   1034      * @param token the window token used to bind the popup's window
   1035      *
   1036      * @return the layout parameters to pass to the window manager
   1037      */
   1038     private WindowManager.LayoutParams createPopupLayout(IBinder token) {
   1039         // generates the layout parameters for the drop down
   1040         // we want a fixed size view located at the bottom left of the anchor
   1041         WindowManager.LayoutParams p = new WindowManager.LayoutParams();
   1042         // these gravity settings put the view at the top left corner of the
   1043         // screen. The view is then positioned to the appropriate location
   1044         // by setting the x and y offsets to match the anchor's bottom
   1045         // left corner
   1046         p.gravity = Gravity.START | Gravity.TOP;
   1047         p.width = mLastWidth = mWidth;
   1048         p.height = mLastHeight = mHeight;
   1049         if (mBackground != null) {
   1050             p.format = mBackground.getOpacity();
   1051         } else {
   1052             p.format = PixelFormat.TRANSLUCENT;
   1053         }
   1054         p.flags = computeFlags(p.flags);
   1055         p.type = mWindowLayoutType;
   1056         p.token = token;
   1057         p.softInputMode = mSoftInputMode;
   1058         p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
   1059 
   1060         return p;
   1061     }
   1062 
   1063     private int computeFlags(int curFlags) {
   1064         curFlags &= ~(
   1065                 WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES |
   1066                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
   1067                 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
   1068                 WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
   1069                 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
   1070                 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
   1071                 WindowManager.LayoutParams.FLAG_SPLIT_TOUCH);
   1072         if(mIgnoreCheekPress) {
   1073             curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
   1074         }
   1075         if (!mFocusable) {
   1076             curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
   1077             if (mInputMethodMode == INPUT_METHOD_NEEDED) {
   1078                 curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
   1079             }
   1080         } else if (mInputMethodMode == INPUT_METHOD_NOT_NEEDED) {
   1081             curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
   1082         }
   1083         if (!mTouchable) {
   1084             curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
   1085         }
   1086         if (mOutsideTouchable) {
   1087             curFlags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
   1088         }
   1089         if (!mClippingEnabled) {
   1090             curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
   1091         }
   1092         if (isSplitTouchEnabled()) {
   1093             curFlags |= WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
   1094         }
   1095         if (mLayoutInScreen) {
   1096             curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
   1097         }
   1098         if (mLayoutInsetDecor) {
   1099             curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
   1100         }
   1101         if (mNotTouchModal) {
   1102             curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
   1103         }
   1104         return curFlags;
   1105     }
   1106 
   1107     private int computeAnimationResource() {
   1108         if (mAnimationStyle == -1) {
   1109             if (mIsDropdown) {
   1110                 return mAboveAnchor
   1111                         ? com.android.internal.R.style.Animation_DropDownUp
   1112                         : com.android.internal.R.style.Animation_DropDownDown;
   1113             }
   1114             return 0;
   1115         }
   1116         return mAnimationStyle;
   1117     }
   1118 
   1119     /**
   1120      * <p>Positions the popup window on screen. When the popup window is too
   1121      * tall to fit under the anchor, a parent scroll view is seeked and scrolled
   1122      * up to reclaim space. If scrolling is not possible or not enough, the
   1123      * popup window gets moved on top of the anchor.</p>
   1124      *
   1125      * <p>The height must have been set on the layout parameters prior to
   1126      * calling this method.</p>
   1127      *
   1128      * @param anchor the view on which the popup window must be anchored
   1129      * @param p the layout parameters used to display the drop down
   1130      *
   1131      * @return true if the popup is translated upwards to fit on screen
   1132      */
   1133     private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p,
   1134             int xoff, int yoff, int gravity) {
   1135 
   1136         final int anchorHeight = anchor.getHeight();
   1137         anchor.getLocationInWindow(mDrawingLocation);
   1138         p.x = mDrawingLocation[0] + xoff;
   1139         p.y = mDrawingLocation[1] + anchorHeight + yoff;
   1140 
   1141         final int hgrav = Gravity.getAbsoluteGravity(gravity, anchor.getLayoutDirection()) &
   1142                 Gravity.HORIZONTAL_GRAVITY_MASK;
   1143         if (hgrav == Gravity.RIGHT) {
   1144             // Flip the location to align the right sides of the popup and anchor instead of left
   1145             p.x -= mPopupWidth - anchor.getWidth();
   1146         }
   1147 
   1148         boolean onTop = false;
   1149 
   1150         p.gravity = Gravity.LEFT | Gravity.TOP;
   1151 
   1152         anchor.getLocationOnScreen(mScreenLocation);
   1153         final Rect displayFrame = new Rect();
   1154         anchor.getWindowVisibleDisplayFrame(displayFrame);
   1155 
   1156         int screenY = mScreenLocation[1] + anchorHeight + yoff;
   1157 
   1158         final View root = anchor.getRootView();
   1159         if (screenY + mPopupHeight > displayFrame.bottom ||
   1160                 p.x + mPopupWidth - root.getWidth() > 0) {
   1161             // if the drop down disappears at the bottom of the screen. we try to
   1162             // scroll a parent scrollview or move the drop down back up on top of
   1163             // the edit box
   1164             if (mAllowScrollingAnchorParent) {
   1165                 int scrollX = anchor.getScrollX();
   1166                 int scrollY = anchor.getScrollY();
   1167                 Rect r = new Rect(scrollX, scrollY,  scrollX + mPopupWidth + xoff,
   1168                         scrollY + mPopupHeight + anchor.getHeight() + yoff);
   1169                 anchor.requestRectangleOnScreen(r, true);
   1170             }
   1171 
   1172             // now we re-evaluate the space available, and decide from that
   1173             // whether the pop-up will go above or below the anchor.
   1174             anchor.getLocationInWindow(mDrawingLocation);
   1175             p.x = mDrawingLocation[0] + xoff;
   1176             p.y = mDrawingLocation[1] + anchor.getHeight() + yoff;
   1177 
   1178             // Preserve the gravity adjustment
   1179             if (hgrav == Gravity.RIGHT) {
   1180                 p.x -= mPopupWidth - anchor.getWidth();
   1181             }
   1182 
   1183             // determine whether there is more space above or below the anchor
   1184             anchor.getLocationOnScreen(mScreenLocation);
   1185 
   1186             onTop = (displayFrame.bottom - mScreenLocation[1] - anchor.getHeight() - yoff) <
   1187                     (mScreenLocation[1] - yoff - displayFrame.top);
   1188             if (onTop) {
   1189                 p.gravity = Gravity.LEFT | Gravity.BOTTOM;
   1190                 p.y = root.getHeight() - mDrawingLocation[1] + yoff;
   1191             } else {
   1192                 p.y = mDrawingLocation[1] + anchor.getHeight() + yoff;
   1193             }
   1194         }
   1195 
   1196         if (mClipToScreen) {
   1197             final int displayFrameWidth = displayFrame.right - displayFrame.left;
   1198 
   1199             int right = p.x + p.width;
   1200             if (right > displayFrameWidth) {
   1201                 p.x -= right - displayFrameWidth;
   1202             }
   1203             if (p.x < displayFrame.left) {
   1204                 p.x = displayFrame.left;
   1205                 p.width = Math.min(p.width, displayFrameWidth);
   1206             }
   1207 
   1208             if (onTop) {
   1209                 int popupTop = mScreenLocation[1] + yoff - mPopupHeight;
   1210                 if (popupTop < 0) {
   1211                     p.y += popupTop;
   1212                 }
   1213             } else {
   1214                 p.y = Math.max(p.y, displayFrame.top);
   1215             }
   1216         }
   1217 
   1218         p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
   1219 
   1220         return onTop;
   1221     }
   1222 
   1223     /**
   1224      * Returns the maximum height that is available for the popup to be
   1225      * completely shown. It is recommended that this height be the maximum for
   1226      * the popup's height, otherwise it is possible that the popup will be
   1227      * clipped.
   1228      *
   1229      * @param anchor The view on which the popup window must be anchored.
   1230      * @return The maximum available height for the popup to be completely
   1231      *         shown.
   1232      */
   1233     public int getMaxAvailableHeight(View anchor) {
   1234         return getMaxAvailableHeight(anchor, 0);
   1235     }
   1236 
   1237     /**
   1238      * Returns the maximum height that is available for the popup to be
   1239      * completely shown. It is recommended that this height be the maximum for
   1240      * the popup's height, otherwise it is possible that the popup will be
   1241      * clipped.
   1242      *
   1243      * @param anchor The view on which the popup window must be anchored.
   1244      * @param yOffset y offset from the view's bottom edge
   1245      * @return The maximum available height for the popup to be completely
   1246      *         shown.
   1247      */
   1248     public int getMaxAvailableHeight(View anchor, int yOffset) {
   1249         return getMaxAvailableHeight(anchor, yOffset, false);
   1250     }
   1251 
   1252     /**
   1253      * Returns the maximum height that is available for the popup to be
   1254      * completely shown, optionally ignoring any bottom decorations such as
   1255      * the input method. It is recommended that this height be the maximum for
   1256      * the popup's height, otherwise it is possible that the popup will be
   1257      * clipped.
   1258      *
   1259      * @param anchor The view on which the popup window must be anchored.
   1260      * @param yOffset y offset from the view's bottom edge
   1261      * @param ignoreBottomDecorations if true, the height returned will be
   1262      *        all the way to the bottom of the display, ignoring any
   1263      *        bottom decorations
   1264      * @return The maximum available height for the popup to be completely
   1265      *         shown.
   1266      *
   1267      * @hide Pending API council approval.
   1268      */
   1269     public int getMaxAvailableHeight(View anchor, int yOffset, boolean ignoreBottomDecorations) {
   1270         final Rect displayFrame = new Rect();
   1271         anchor.getWindowVisibleDisplayFrame(displayFrame);
   1272 
   1273         final int[] anchorPos = mDrawingLocation;
   1274         anchor.getLocationOnScreen(anchorPos);
   1275 
   1276         int bottomEdge = displayFrame.bottom;
   1277         if (ignoreBottomDecorations) {
   1278             Resources res = anchor.getContext().getResources();
   1279             bottomEdge = res.getDisplayMetrics().heightPixels;
   1280         }
   1281         final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
   1282         final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
   1283 
   1284         // anchorPos[1] is distance from anchor to top of screen
   1285         int returnedHeight = Math.max(distanceToBottom, distanceToTop);
   1286         if (mBackground != null) {
   1287             mBackground.getPadding(mTempRect);
   1288             returnedHeight -= mTempRect.top + mTempRect.bottom;
   1289         }
   1290 
   1291         return returnedHeight;
   1292     }
   1293 
   1294     /**
   1295      * <p>Dispose of the popup window. This method can be invoked only after
   1296      * {@link #showAsDropDown(android.view.View)} has been executed. Failing that, calling
   1297      * this method will have no effect.</p>
   1298      *
   1299      * @see #showAsDropDown(android.view.View)
   1300      */
   1301     public void dismiss() {
   1302         if (isShowing() && mPopupView != null) {
   1303             mIsShowing = false;
   1304 
   1305             unregisterForScrollChanged();
   1306 
   1307             try {
   1308                 mWindowManager.removeViewImmediate(mPopupView);
   1309             } finally {
   1310                 if (mPopupView != mContentView && mPopupView instanceof ViewGroup) {
   1311                     ((ViewGroup) mPopupView).removeView(mContentView);
   1312                 }
   1313                 mPopupView = null;
   1314 
   1315                 if (mOnDismissListener != null) {
   1316                     mOnDismissListener.onDismiss();
   1317                 }
   1318             }
   1319         }
   1320     }
   1321 
   1322     /**
   1323      * Sets the listener to be called when the window is dismissed.
   1324      *
   1325      * @param onDismissListener The listener.
   1326      */
   1327     public void setOnDismissListener(OnDismissListener onDismissListener) {
   1328         mOnDismissListener = onDismissListener;
   1329     }
   1330 
   1331     /**
   1332      * Updates the state of the popup window, if it is currently being displayed,
   1333      * from the currently set state.  This include:
   1334      * {@link #setClippingEnabled(boolean)}, {@link #setFocusable(boolean)},
   1335      * {@link #setIgnoreCheekPress()}, {@link #setInputMethodMode(int)},
   1336      * {@link #setTouchable(boolean)}, and {@link #setAnimationStyle(int)}.
   1337      */
   1338     public void update() {
   1339         if (!isShowing() || mContentView == null) {
   1340             return;
   1341         }
   1342 
   1343         WindowManager.LayoutParams p = (WindowManager.LayoutParams)
   1344                 mPopupView.getLayoutParams();
   1345 
   1346         boolean update = false;
   1347 
   1348         final int newAnim = computeAnimationResource();
   1349         if (newAnim != p.windowAnimations) {
   1350             p.windowAnimations = newAnim;
   1351             update = true;
   1352         }
   1353 
   1354         final int newFlags = computeFlags(p.flags);
   1355         if (newFlags != p.flags) {
   1356             p.flags = newFlags;
   1357             update = true;
   1358         }
   1359 
   1360         if (update) {
   1361             setLayoutDirectionFromAnchor();
   1362             mWindowManager.updateViewLayout(mPopupView, p);
   1363         }
   1364     }
   1365 
   1366     /**
   1367      * <p>Updates the dimension of the popup window. Calling this function
   1368      * also updates the window with the current popup state as described
   1369      * for {@link #update()}.</p>
   1370      *
   1371      * @param width the new width
   1372      * @param height the new height
   1373      */
   1374     public void update(int width, int height) {
   1375         WindowManager.LayoutParams p = (WindowManager.LayoutParams)
   1376                 mPopupView.getLayoutParams();
   1377         update(p.x, p.y, width, height, false);
   1378     }
   1379 
   1380     /**
   1381      * <p>Updates the position and the dimension of the popup window. Width and
   1382      * height can be set to -1 to update location only.  Calling this function
   1383      * also updates the window with the current popup state as
   1384      * described for {@link #update()}.</p>
   1385      *
   1386      * @param x the new x location
   1387      * @param y the new y location
   1388      * @param width the new width, can be -1 to ignore
   1389      * @param height the new height, can be -1 to ignore
   1390      */
   1391     public void update(int x, int y, int width, int height) {
   1392         update(x, y, width, height, false);
   1393     }
   1394 
   1395     /**
   1396      * <p>Updates the position and the dimension of the popup window. Width and
   1397      * height can be set to -1 to update location only.  Calling this function
   1398      * also updates the window with the current popup state as
   1399      * described for {@link #update()}.</p>
   1400      *
   1401      * @param x the new x location
   1402      * @param y the new y location
   1403      * @param width the new width, can be -1 to ignore
   1404      * @param height the new height, can be -1 to ignore
   1405      * @param force reposition the window even if the specified position
   1406      *              already seems to correspond to the LayoutParams
   1407      */
   1408     public void update(int x, int y, int width, int height, boolean force) {
   1409         if (width != -1) {
   1410             mLastWidth = width;
   1411             setWidth(width);
   1412         }
   1413 
   1414         if (height != -1) {
   1415             mLastHeight = height;
   1416             setHeight(height);
   1417         }
   1418 
   1419         if (!isShowing() || mContentView == null) {
   1420             return;
   1421         }
   1422 
   1423         WindowManager.LayoutParams p = (WindowManager.LayoutParams) mPopupView.getLayoutParams();
   1424 
   1425         boolean update = force;
   1426 
   1427         final int finalWidth = mWidthMode < 0 ? mWidthMode : mLastWidth;
   1428         if (width != -1 && p.width != finalWidth) {
   1429             p.width = mLastWidth = finalWidth;
   1430             update = true;
   1431         }
   1432 
   1433         final int finalHeight = mHeightMode < 0 ? mHeightMode : mLastHeight;
   1434         if (height != -1 && p.height != finalHeight) {
   1435             p.height = mLastHeight = finalHeight;
   1436             update = true;
   1437         }
   1438 
   1439         if (p.x != x) {
   1440             p.x = x;
   1441             update = true;
   1442         }
   1443 
   1444         if (p.y != y) {
   1445             p.y = y;
   1446             update = true;
   1447         }
   1448 
   1449         final int newAnim = computeAnimationResource();
   1450         if (newAnim != p.windowAnimations) {
   1451             p.windowAnimations = newAnim;
   1452             update = true;
   1453         }
   1454 
   1455         final int newFlags = computeFlags(p.flags);
   1456         if (newFlags != p.flags) {
   1457             p.flags = newFlags;
   1458             update = true;
   1459         }
   1460 
   1461         if (update) {
   1462             setLayoutDirectionFromAnchor();
   1463             mWindowManager.updateViewLayout(mPopupView, p);
   1464         }
   1465     }
   1466 
   1467     /**
   1468      * <p>Updates the position and the dimension of the popup window. Calling this
   1469      * function also updates the window with the current popup state as described
   1470      * for {@link #update()}.</p>
   1471      *
   1472      * @param anchor the popup's anchor view
   1473      * @param width the new width, can be -1 to ignore
   1474      * @param height the new height, can be -1 to ignore
   1475      */
   1476     public void update(View anchor, int width, int height) {
   1477         update(anchor, false, 0, 0, true, width, height, mAnchoredGravity);
   1478     }
   1479 
   1480     /**
   1481      * <p>Updates the position and the dimension of the popup window. Width and
   1482      * height can be set to -1 to update location only.  Calling this function
   1483      * also updates the window with the current popup state as
   1484      * described for {@link #update()}.</p>
   1485      *
   1486      * <p>If the view later scrolls to move <code>anchor</code> to a different
   1487      * location, the popup will be moved correspondingly.</p>
   1488      *
   1489      * @param anchor the popup's anchor view
   1490      * @param xoff x offset from the view's left edge
   1491      * @param yoff y offset from the view's bottom edge
   1492      * @param width the new width, can be -1 to ignore
   1493      * @param height the new height, can be -1 to ignore
   1494      */
   1495     public void update(View anchor, int xoff, int yoff, int width, int height) {
   1496         update(anchor, true, xoff, yoff, true, width, height, mAnchoredGravity);
   1497     }
   1498 
   1499     private void update(View anchor, boolean updateLocation, int xoff, int yoff,
   1500             boolean updateDimension, int width, int height, int gravity) {
   1501 
   1502         if (!isShowing() || mContentView == null) {
   1503             return;
   1504         }
   1505 
   1506         WeakReference<View> oldAnchor = mAnchor;
   1507         final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff);
   1508         if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) {
   1509             registerForScrollChanged(anchor, xoff, yoff, gravity);
   1510         } else if (needsUpdate) {
   1511             // No need to register again if this is a DropDown, showAsDropDown already did.
   1512             mAnchorXoff = xoff;
   1513             mAnchorYoff = yoff;
   1514             mAnchoredGravity = gravity;
   1515         }
   1516 
   1517         WindowManager.LayoutParams p = (WindowManager.LayoutParams) mPopupView.getLayoutParams();
   1518 
   1519         if (updateDimension) {
   1520             if (width == -1) {
   1521                 width = mPopupWidth;
   1522             } else {
   1523                 mPopupWidth = width;
   1524             }
   1525             if (height == -1) {
   1526                 height = mPopupHeight;
   1527             } else {
   1528                 mPopupHeight = height;
   1529             }
   1530         }
   1531 
   1532         int x = p.x;
   1533         int y = p.y;
   1534 
   1535         if (updateLocation) {
   1536             updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff, gravity));
   1537         } else {
   1538             updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
   1539                     mAnchoredGravity));
   1540         }
   1541 
   1542         update(p.x, p.y, width, height, x != p.x || y != p.y);
   1543     }
   1544 
   1545     /**
   1546      * Listener that is called when this popup window is dismissed.
   1547      */
   1548     public interface OnDismissListener {
   1549         /**
   1550          * Called when this popup window is dismissed.
   1551          */
   1552         public void onDismiss();
   1553     }
   1554 
   1555     private void unregisterForScrollChanged() {
   1556         WeakReference<View> anchorRef = mAnchor;
   1557         View anchor = null;
   1558         if (anchorRef != null) {
   1559             anchor = anchorRef.get();
   1560         }
   1561         if (anchor != null) {
   1562             ViewTreeObserver vto = anchor.getViewTreeObserver();
   1563             vto.removeOnScrollChangedListener(mOnScrollChangedListener);
   1564         }
   1565         mAnchor = null;
   1566     }
   1567 
   1568     private void registerForScrollChanged(View anchor, int xoff, int yoff, int gravity) {
   1569         unregisterForScrollChanged();
   1570 
   1571         mAnchor = new WeakReference<View>(anchor);
   1572         ViewTreeObserver vto = anchor.getViewTreeObserver();
   1573         if (vto != null) {
   1574             vto.addOnScrollChangedListener(mOnScrollChangedListener);
   1575         }
   1576 
   1577         mAnchorXoff = xoff;
   1578         mAnchorYoff = yoff;
   1579         mAnchoredGravity = gravity;
   1580     }
   1581 
   1582     private class PopupViewContainer extends FrameLayout {
   1583         private static final String TAG = "PopupWindow.PopupViewContainer";
   1584 
   1585         public PopupViewContainer(Context context) {
   1586             super(context);
   1587         }
   1588 
   1589         @Override
   1590         protected int[] onCreateDrawableState(int extraSpace) {
   1591             if (mAboveAnchor) {
   1592                 // 1 more needed for the above anchor state
   1593                 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
   1594                 View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET);
   1595                 return drawableState;
   1596             } else {
   1597                 return super.onCreateDrawableState(extraSpace);
   1598             }
   1599         }
   1600 
   1601         @Override
   1602         public boolean dispatchKeyEvent(KeyEvent event) {
   1603             if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
   1604                 if (getKeyDispatcherState() == null) {
   1605                     return super.dispatchKeyEvent(event);
   1606                 }
   1607 
   1608                 if (event.getAction() == KeyEvent.ACTION_DOWN
   1609                         && event.getRepeatCount() == 0) {
   1610                     KeyEvent.DispatcherState state = getKeyDispatcherState();
   1611                     if (state != null) {
   1612                         state.startTracking(event, this);
   1613                     }
   1614                     return true;
   1615                 } else if (event.getAction() == KeyEvent.ACTION_UP) {
   1616                     KeyEvent.DispatcherState state = getKeyDispatcherState();
   1617                     if (state != null && state.isTracking(event) && !event.isCanceled()) {
   1618                         dismiss();
   1619                         return true;
   1620                     }
   1621                 }
   1622                 return super.dispatchKeyEvent(event);
   1623             } else {
   1624                 return super.dispatchKeyEvent(event);
   1625             }
   1626         }
   1627 
   1628         @Override
   1629         public boolean dispatchTouchEvent(MotionEvent ev) {
   1630             if (mTouchInterceptor != null && mTouchInterceptor.onTouch(this, ev)) {
   1631                 return true;
   1632             }
   1633             return super.dispatchTouchEvent(ev);
   1634         }
   1635 
   1636         @Override
   1637         public boolean onTouchEvent(MotionEvent event) {
   1638             final int x = (int) event.getX();
   1639             final int y = (int) event.getY();
   1640 
   1641             if ((event.getAction() == MotionEvent.ACTION_DOWN)
   1642                     && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
   1643                 dismiss();
   1644                 return true;
   1645             } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
   1646                 dismiss();
   1647                 return true;
   1648             } else {
   1649                 return super.onTouchEvent(event);
   1650             }
   1651         }
   1652 
   1653         @Override
   1654         public void sendAccessibilityEvent(int eventType) {
   1655             // clinets are interested in the content not the container, make it event source
   1656             if (mContentView != null) {
   1657                 mContentView.sendAccessibilityEvent(eventType);
   1658             } else {
   1659                 super.sendAccessibilityEvent(eventType);
   1660             }
   1661         }
   1662     }
   1663 
   1664 }
   1665