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