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