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.annotation.NonNull;
     22 import android.annotation.Nullable;
     23 import android.content.Context;
     24 import android.content.res.TypedArray;
     25 import android.graphics.PixelFormat;
     26 import android.graphics.Rect;
     27 import android.graphics.drawable.Drawable;
     28 import android.graphics.drawable.StateListDrawable;
     29 import android.os.Build;
     30 import android.os.IBinder;
     31 import android.transition.Transition;
     32 import android.transition.Transition.EpicenterCallback;
     33 import android.transition.Transition.TransitionListener;
     34 import android.transition.Transition.TransitionListenerAdapter;
     35 import android.transition.TransitionInflater;
     36 import android.transition.TransitionManager;
     37 import android.transition.TransitionSet;
     38 import android.util.AttributeSet;
     39 import android.view.Gravity;
     40 import android.view.KeyEvent;
     41 import android.view.MotionEvent;
     42 import android.view.View;
     43 import android.view.View.OnAttachStateChangeListener;
     44 import android.view.View.OnTouchListener;
     45 import android.view.ViewGroup;
     46 import android.view.ViewParent;
     47 import android.view.ViewTreeObserver;
     48 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
     49 import android.view.ViewTreeObserver.OnScrollChangedListener;
     50 import android.view.WindowManager;
     51 import android.view.WindowManager.LayoutParams;
     52 
     53 import java.lang.ref.WeakReference;
     54 
     55 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
     56 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
     57 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
     58 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
     59 
     60 /**
     61  * <p>
     62  * This class represents a popup window that can be used to display an
     63  * arbitrary view. The popup window is a floating container that appears on top
     64  * of the current activity.
     65  * </p>
     66  * <a name="Animation"></a>
     67  * <h3>Animation</h3>
     68  * <p>
     69  * On all versions of Android, popup window enter and exit animations may be
     70  * specified by calling {@link #setAnimationStyle(int)} and passing the
     71  * resource ID for an animation style that defines {@code windowEnterAnimation}
     72  * and {@code windowExitAnimation}. For example, passing
     73  * {@link android.R.style#Animation_Dialog} will give a scale and alpha
     74  * animation.
     75  * </br>
     76  * A window animation style may also be specified in the popup window's style
     77  * XML via the {@link android.R.styleable#PopupWindow_popupAnimationStyle popupAnimationStyle}
     78  * attribute.
     79  * </p>
     80  * <p>
     81  * Starting with API 23, more complex popup window enter and exit transitions
     82  * may be specified by calling either {@link #setEnterTransition(Transition)}
     83  * or {@link #setExitTransition(Transition)} and passing a  {@link Transition}.
     84  * </br>
     85  * Popup enter and exit transitions may also be specified in the popup window's
     86  * style XML via the {@link android.R.styleable#PopupWindow_popupEnterTransition popupEnterTransition}
     87  * and {@link android.R.styleable#PopupWindow_popupExitTransition popupExitTransition}
     88  * attributes, respectively.
     89  * </p>
     90  *
     91  * @attr ref android.R.styleable#PopupWindow_overlapAnchor
     92  * @attr ref android.R.styleable#PopupWindow_popupAnimationStyle
     93  * @attr ref android.R.styleable#PopupWindow_popupBackground
     94  * @attr ref android.R.styleable#PopupWindow_popupElevation
     95  * @attr ref android.R.styleable#PopupWindow_popupEnterTransition
     96  * @attr ref android.R.styleable#PopupWindow_popupExitTransition
     97  *
     98  * @see android.widget.AutoCompleteTextView
     99  * @see android.widget.Spinner
    100  */
    101 public class PopupWindow {
    102     /**
    103      * Mode for {@link #setInputMethodMode(int)}: the requirements for the
    104      * input method should be based on the focusability of the popup.  That is
    105      * if it is focusable than it needs to work with the input method, else
    106      * it doesn't.
    107      */
    108     public static final int INPUT_METHOD_FROM_FOCUSABLE = 0;
    109 
    110     /**
    111      * Mode for {@link #setInputMethodMode(int)}: this popup always needs to
    112      * work with an input method, regardless of whether it is focusable.  This
    113      * means that it will always be displayed so that the user can also operate
    114      * the input method while it is shown.
    115      */
    116     public static final int INPUT_METHOD_NEEDED = 1;
    117 
    118     /**
    119      * Mode for {@link #setInputMethodMode(int)}: this popup never needs to
    120      * work with an input method, regardless of whether it is focusable.  This
    121      * means that it will always be displayed to use as much space on the
    122      * screen as needed, regardless of whether this covers the input method.
    123      */
    124     public static final int INPUT_METHOD_NOT_NEEDED = 2;
    125 
    126     private static final int DEFAULT_ANCHORED_GRAVITY = Gravity.TOP | Gravity.START;
    127 
    128     /**
    129      * Default animation style indicating that separate animations should be
    130      * used for top/bottom anchoring states.
    131      */
    132     private static final int ANIMATION_STYLE_DEFAULT = -1;
    133 
    134     private final int[] mTmpDrawingLocation = new int[2];
    135     private final int[] mTmpScreenLocation = new int[2];
    136     private final Rect mTempRect = new Rect();
    137 
    138     private Context mContext;
    139     private WindowManager mWindowManager;
    140 
    141     private boolean mIsShowing;
    142     private boolean mIsTransitioningToDismiss;
    143     private boolean mIsDropdown;
    144 
    145     /** View that handles event dispatch and content transitions. */
    146     private PopupDecorView mDecorView;
    147 
    148     /** View that holds the background and may animate during a transition. */
    149     private View mBackgroundView;
    150 
    151     /** The contents of the popup. May be identical to the background view. */
    152     private View mContentView;
    153 
    154     private boolean mFocusable;
    155     private int mInputMethodMode = INPUT_METHOD_FROM_FOCUSABLE;
    156     private int mSoftInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
    157     private boolean mTouchable = true;
    158     private boolean mOutsideTouchable = false;
    159     private boolean mClippingEnabled = true;
    160     private int mSplitTouchEnabled = -1;
    161     private boolean mLayoutInScreen;
    162     private boolean mClipToScreen;
    163     private boolean mAllowScrollingAnchorParent = true;
    164     private boolean mLayoutInsetDecor = false;
    165     private boolean mNotTouchModal;
    166     private boolean mAttachedInDecor = true;
    167     private boolean mAttachedInDecorSet = false;
    168 
    169     private OnTouchListener mTouchInterceptor;
    170 
    171     private int mWidthMode;
    172     private int mWidth = LayoutParams.WRAP_CONTENT;
    173     private int mLastWidth;
    174     private int mHeightMode;
    175     private int mHeight = LayoutParams.WRAP_CONTENT;
    176     private int mLastHeight;
    177 
    178     private float mElevation;
    179 
    180     private Drawable mBackground;
    181     private Drawable mAboveAnchorBackgroundDrawable;
    182     private Drawable mBelowAnchorBackgroundDrawable;
    183 
    184     private Transition mEnterTransition;
    185     private Transition mExitTransition;
    186     private Rect mEpicenterBounds;
    187 
    188     private boolean mAboveAnchor;
    189     private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
    190 
    191     private OnDismissListener mOnDismissListener;
    192     private boolean mIgnoreCheekPress = false;
    193 
    194     private int mAnimationStyle = ANIMATION_STYLE_DEFAULT;
    195 
    196     private static final int[] ABOVE_ANCHOR_STATE_SET = new int[] {
    197         com.android.internal.R.attr.state_above_anchor
    198     };
    199 
    200     private final OnAttachStateChangeListener mOnAnchorRootDetachedListener =
    201             new OnAttachStateChangeListener() {
    202                 @Override
    203                 public void onViewAttachedToWindow(View v) {}
    204 
    205                 @Override
    206                 public void onViewDetachedFromWindow(View v) {
    207                     mIsAnchorRootAttached = false;
    208                 }
    209             };
    210 
    211     private WeakReference<View> mAnchor;
    212     private WeakReference<View> mAnchorRoot;
    213     private boolean mIsAnchorRootAttached;
    214 
    215     private final OnScrollChangedListener mOnScrollChangedListener = new OnScrollChangedListener() {
    216         @Override
    217         public void onScrollChanged() {
    218             final View anchor = mAnchor != null ? mAnchor.get() : null;
    219             if (anchor != null && mDecorView != null) {
    220                 final WindowManager.LayoutParams p = (WindowManager.LayoutParams)
    221                         mDecorView.getLayoutParams();
    222 
    223                 updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
    224                         p.width, p.height, mAnchoredGravity));
    225                 update(p.x, p.y, -1, -1, true);
    226             }
    227         }
    228     };
    229 
    230     private int mAnchorXoff;
    231     private int mAnchorYoff;
    232     private int mAnchoredGravity;
    233     private boolean mOverlapAnchor;
    234 
    235     private boolean mPopupViewInitialLayoutDirectionInherited;
    236 
    237     /**
    238      * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
    239      *
    240      * <p>The popup does provide a background.</p>
    241      */
    242     public PopupWindow(Context context) {
    243         this(context, null);
    244     }
    245 
    246     /**
    247      * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
    248      *
    249      * <p>The popup does provide a background.</p>
    250      */
    251     public PopupWindow(Context context, AttributeSet attrs) {
    252         this(context, attrs, com.android.internal.R.attr.popupWindowStyle);
    253     }
    254 
    255     /**
    256      * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
    257      *
    258      * <p>The popup does provide a background.</p>
    259      */
    260     public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
    261         this(context, attrs, defStyleAttr, 0);
    262     }
    263 
    264     /**
    265      * <p>Create a new, empty, non focusable popup window of dimension (0,0).</p>
    266      *
    267      * <p>The popup does not provide a background.</p>
    268      */
    269     public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    270         mContext = context;
    271         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    272 
    273         final TypedArray a = context.obtainStyledAttributes(
    274                 attrs, R.styleable.PopupWindow, defStyleAttr, defStyleRes);
    275         final Drawable bg = a.getDrawable(R.styleable.PopupWindow_popupBackground);
    276         mElevation = a.getDimension(R.styleable.PopupWindow_popupElevation, 0);
    277         mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false);
    278 
    279         // Preserve default behavior from Gingerbread. If the animation is
    280         // undefined or explicitly specifies the Gingerbread animation style,
    281         // use a sentinel value.
    282         if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupAnimationStyle)) {
    283             final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, 0);
    284             if (animStyle == R.style.Animation_PopupWindow) {
    285                 mAnimationStyle = ANIMATION_STYLE_DEFAULT;
    286             } else {
    287                 mAnimationStyle = animStyle;
    288             }
    289         } else {
    290             mAnimationStyle = ANIMATION_STYLE_DEFAULT;
    291         }
    292 
    293         final Transition enterTransition = getTransition(a.getResourceId(
    294                 R.styleable.PopupWindow_popupEnterTransition, 0));
    295         final Transition exitTransition;
    296         if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupExitTransition)) {
    297             exitTransition = getTransition(a.getResourceId(
    298                     R.styleable.PopupWindow_popupExitTransition, 0));
    299         } else {
    300             exitTransition = enterTransition == null ? null : enterTransition.clone();
    301         }
    302 
    303         a.recycle();
    304 
    305         setEnterTransition(enterTransition);
    306         setExitTransition(exitTransition);
    307         setBackgroundDrawable(bg);
    308     }
    309 
    310     /**
    311      * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
    312      *
    313      * <p>The popup does not provide any background. This should be handled
    314      * by the content view.</p>
    315      */
    316     public PopupWindow() {
    317         this(null, 0, 0);
    318     }
    319 
    320     /**
    321      * <p>Create a new non focusable popup window which can display the
    322      * <tt>contentView</tt>. The dimension of the window are (0,0).</p>
    323      *
    324      * <p>The popup does not provide any background. This should be handled
    325      * by the content view.</p>
    326      *
    327      * @param contentView the popup's content
    328      */
    329     public PopupWindow(View contentView) {
    330         this(contentView, 0, 0);
    331     }
    332 
    333     /**
    334      * <p>Create a new empty, non focusable popup window. The dimension of the
    335      * window must be passed to this constructor.</p>
    336      *
    337      * <p>The popup does not provide any background. This should be handled
    338      * by the content view.</p>
    339      *
    340      * @param width the popup's width
    341      * @param height the popup's height
    342      */
    343     public PopupWindow(int width, int height) {
    344         this(null, width, height);
    345     }
    346 
    347     /**
    348      * <p>Create a new non focusable popup window which can display the
    349      * <tt>contentView</tt>. The dimension of the window must be passed to
    350      * this constructor.</p>
    351      *
    352      * <p>The popup does not provide any background. This should be handled
    353      * by the content view.</p>
    354      *
    355      * @param contentView the popup's content
    356      * @param width the popup's width
    357      * @param height the popup's height
    358      */
    359     public PopupWindow(View contentView, int width, int height) {
    360         this(contentView, width, height, false);
    361     }
    362 
    363     /**
    364      * <p>Create a new popup window which can display the <tt>contentView</tt>.
    365      * The dimension of the window must be passed to this constructor.</p>
    366      *
    367      * <p>The popup does not provide any background. This should be handled
    368      * by the content view.</p>
    369      *
    370      * @param contentView the popup's content
    371      * @param width the popup's width
    372      * @param height the popup's height
    373      * @param focusable true if the popup can be focused, false otherwise
    374      */
    375     public PopupWindow(View contentView, int width, int height, boolean focusable) {
    376         if (contentView != null) {
    377             mContext = contentView.getContext();
    378             mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
    379         }
    380 
    381         setContentView(contentView);
    382         setWidth(width);
    383         setHeight(height);
    384         setFocusable(focusable);
    385     }
    386 
    387     /**
    388      * Sets the enter transition to be used when the popup window is shown.
    389      *
    390      * @param enterTransition the enter transition, or {@code null} to clear
    391      * @see #getEnterTransition()
    392      * @attr ref android.R.styleable#PopupWindow_popupEnterTransition
    393      */
    394     public void setEnterTransition(@Nullable Transition enterTransition) {
    395         mEnterTransition = enterTransition;
    396     }
    397 
    398     /**
    399      * Returns the enter transition to be used when the popup window is shown.
    400      *
    401      * @return the enter transition, or {@code null} if not set
    402      * @see #setEnterTransition(Transition)
    403      * @attr ref android.R.styleable#PopupWindow_popupEnterTransition
    404      */
    405     @Nullable
    406     public Transition getEnterTransition() {
    407         return mEnterTransition;
    408     }
    409 
    410     /**
    411      * Sets the exit transition to be used when the popup window is dismissed.
    412      *
    413      * @param exitTransition the exit transition, or {@code null} to clear
    414      * @see #getExitTransition()
    415      * @attr ref android.R.styleable#PopupWindow_popupExitTransition
    416      */
    417     public void setExitTransition(@Nullable Transition exitTransition) {
    418         mExitTransition = exitTransition;
    419     }
    420 
    421     /**
    422      * Returns the exit transition to be used when the popup window is
    423      * dismissed.
    424      *
    425      * @return the exit transition, or {@code null} if not set
    426      * @see #setExitTransition(Transition)
    427      * @attr ref android.R.styleable#PopupWindow_popupExitTransition
    428      */
    429     @Nullable
    430     public Transition getExitTransition() {
    431         return mExitTransition;
    432     }
    433 
    434     /**
    435      * Sets the bounds used as the epicenter of the enter and exit transitions.
    436      * <p>
    437      * Transitions use a point or Rect, referred to as the epicenter, to orient
    438      * the direction of travel. For popup windows, the anchor view bounds are
    439      * used as the default epicenter.
    440      * <p>
    441      * See {@link Transition#setEpicenterCallback(EpicenterCallback)} for more
    442      * information about how transition epicenters.
    443      *
    444      * @param bounds the epicenter bounds relative to the anchor view, or
    445      *               {@code null} to use the default epicenter
    446      * @see #getTransitionEpicenter()
    447      * @hide
    448      */
    449     public void setEpicenterBounds(Rect bounds) {
    450         mEpicenterBounds = bounds;
    451     }
    452 
    453     private Transition getTransition(int resId) {
    454         if (resId != 0 && resId != R.transition.no_transition) {
    455             final TransitionInflater inflater = TransitionInflater.from(mContext);
    456             final Transition transition = inflater.inflateTransition(resId);
    457             if (transition != null) {
    458                 final boolean isEmpty = transition instanceof TransitionSet
    459                         && ((TransitionSet) transition).getTransitionCount() == 0;
    460                 if (!isEmpty) {
    461                     return transition;
    462                 }
    463             }
    464         }
    465         return null;
    466     }
    467 
    468     /**
    469      * Return the drawable used as the popup window's background.
    470      *
    471      * @return the background drawable or {@code null} if not set
    472      * @see #setBackgroundDrawable(Drawable)
    473      * @attr ref android.R.styleable#PopupWindow_popupBackground
    474      */
    475     public Drawable getBackground() {
    476         return mBackground;
    477     }
    478 
    479     /**
    480      * Specifies the background drawable for this popup window. The background
    481      * can be set to {@code null}.
    482      *
    483      * @param background the popup's background
    484      * @see #getBackground()
    485      * @attr ref android.R.styleable#PopupWindow_popupBackground
    486      */
    487     public void setBackgroundDrawable(Drawable background) {
    488         mBackground = background;
    489 
    490         // If this is a StateListDrawable, try to find and store the drawable to be
    491         // used when the drop-down is placed above its anchor view, and the one to be
    492         // used when the drop-down is placed below its anchor view. We extract
    493         // the drawables ourselves to work around a problem with using refreshDrawableState
    494         // that it will take into account the padding of all drawables specified in a
    495         // StateListDrawable, thus adding superfluous padding to drop-down views.
    496         //
    497         // We assume a StateListDrawable will have a drawable for ABOVE_ANCHOR_STATE_SET and
    498         // at least one other drawable, intended for the 'below-anchor state'.
    499         if (mBackground instanceof StateListDrawable) {
    500             StateListDrawable stateList = (StateListDrawable) mBackground;
    501 
    502             // Find the above-anchor view - this one's easy, it should be labeled as such.
    503             int aboveAnchorStateIndex = stateList.getStateDrawableIndex(ABOVE_ANCHOR_STATE_SET);
    504 
    505             // Now, for the below-anchor view, look for any other drawable specified in the
    506             // StateListDrawable which is not for the above-anchor state and use that.
    507             int count = stateList.getStateCount();
    508             int belowAnchorStateIndex = -1;
    509             for (int i = 0; i < count; i++) {
    510                 if (i != aboveAnchorStateIndex) {
    511                     belowAnchorStateIndex = i;
    512                     break;
    513                 }
    514             }
    515 
    516             // Store the drawables we found, if we found them. Otherwise, set them both
    517             // to null so that we'll just use refreshDrawableState.
    518             if (aboveAnchorStateIndex != -1 && belowAnchorStateIndex != -1) {
    519                 mAboveAnchorBackgroundDrawable = stateList.getStateDrawable(aboveAnchorStateIndex);
    520                 mBelowAnchorBackgroundDrawable = stateList.getStateDrawable(belowAnchorStateIndex);
    521             } else {
    522                 mBelowAnchorBackgroundDrawable = null;
    523                 mAboveAnchorBackgroundDrawable = null;
    524             }
    525         }
    526     }
    527 
    528     /**
    529      * @return the elevation for this popup window in pixels
    530      * @see #setElevation(float)
    531      * @attr ref android.R.styleable#PopupWindow_popupElevation
    532      */
    533     public float getElevation() {
    534         return mElevation;
    535     }
    536 
    537     /**
    538      * Specifies the elevation for this popup window.
    539      *
    540      * @param elevation the popup's elevation in pixels
    541      * @see #getElevation()
    542      * @attr ref android.R.styleable#PopupWindow_popupElevation
    543      */
    544     public void setElevation(float elevation) {
    545         mElevation = elevation;
    546     }
    547 
    548     /**
    549      * <p>Return the animation style to use the popup appears and disappears</p>
    550      *
    551      * @return the animation style to use the popup appears and disappears
    552      */
    553     public int getAnimationStyle() {
    554         return mAnimationStyle;
    555     }
    556 
    557     /**
    558      * Set the flag on popup to ignore cheek press events; by default this flag
    559      * is set to false
    560      * which means the popup will not ignore cheek press dispatch events.
    561      *
    562      * <p>If the popup is showing, calling this method will take effect only
    563      * the next time the popup is shown or through a manual call to one of
    564      * the {@link #update()} methods.</p>
    565      *
    566      * @see #update()
    567      */
    568     public void setIgnoreCheekPress() {
    569         mIgnoreCheekPress = true;
    570     }
    571 
    572 
    573     /**
    574      * <p>Change the animation style resource for this popup.</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 animationStyle animation style to use when the popup appears
    581      *      and disappears.  Set to -1 for the default animation, 0 for no
    582      *      animation, or a resource identifier for an explicit animation.
    583      *
    584      * @see #update()
    585      */
    586     public void setAnimationStyle(int animationStyle) {
    587         mAnimationStyle = animationStyle;
    588     }
    589 
    590     /**
    591      * <p>Return the view used as the content of the popup window.</p>
    592      *
    593      * @return a {@link android.view.View} representing the popup's content
    594      *
    595      * @see #setContentView(android.view.View)
    596      */
    597     public View getContentView() {
    598         return mContentView;
    599     }
    600 
    601     /**
    602      * <p>Change the popup's content. The content is represented by an instance
    603      * of {@link android.view.View}.</p>
    604      *
    605      * <p>This method has no effect if called when the popup is showing.</p>
    606      *
    607      * @param contentView the new content for the popup
    608      *
    609      * @see #getContentView()
    610      * @see #isShowing()
    611      */
    612     public void setContentView(View contentView) {
    613         if (isShowing()) {
    614             return;
    615         }
    616 
    617         mContentView = contentView;
    618 
    619         if (mContext == null && mContentView != null) {
    620             mContext = mContentView.getContext();
    621         }
    622 
    623         if (mWindowManager == null && mContentView != null) {
    624             mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
    625         }
    626 
    627         // Setting the default for attachedInDecor based on SDK version here
    628         // instead of in the constructor since we might not have the context
    629         // object in the constructor. We only want to set default here if the
    630         // app hasn't already set the attachedInDecor.
    631         if (mContext != null && !mAttachedInDecorSet) {
    632             // Attach popup window in decor frame of parent window by default for
    633             // {@link Build.VERSION_CODES.LOLLIPOP_MR1} or greater. Keep current
    634             // behavior of not attaching to decor frame for older SDKs.
    635             setAttachedInDecor(mContext.getApplicationInfo().targetSdkVersion
    636                     >= Build.VERSION_CODES.LOLLIPOP_MR1);
    637         }
    638 
    639     }
    640 
    641     /**
    642      * Set a callback for all touch events being dispatched to the popup
    643      * window.
    644      */
    645     public void setTouchInterceptor(OnTouchListener l) {
    646         mTouchInterceptor = l;
    647     }
    648 
    649     /**
    650      * <p>Indicate whether the popup window can grab the focus.</p>
    651      *
    652      * @return true if the popup is focusable, false otherwise
    653      *
    654      * @see #setFocusable(boolean)
    655      */
    656     public boolean isFocusable() {
    657         return mFocusable;
    658     }
    659 
    660     /**
    661      * <p>Changes the focusability of the popup window. When focusable, the
    662      * window will grab the focus from the current focused widget if the popup
    663      * contains a focusable {@link android.view.View}.  By default a popup
    664      * window is not focusable.</p>
    665      *
    666      * <p>If the popup is showing, calling this method will take effect only
    667      * the next time the popup is shown or through a manual call to one of
    668      * the {@link #update()} methods.</p>
    669      *
    670      * @param focusable true if the popup should grab focus, false otherwise.
    671      *
    672      * @see #isFocusable()
    673      * @see #isShowing()
    674      * @see #update()
    675      */
    676     public void setFocusable(boolean focusable) {
    677         mFocusable = focusable;
    678     }
    679 
    680     /**
    681      * Return the current value in {@link #setInputMethodMode(int)}.
    682      *
    683      * @see #setInputMethodMode(int)
    684      */
    685     public int getInputMethodMode() {
    686         return mInputMethodMode;
    687 
    688     }
    689 
    690     /**
    691      * Control how the popup operates with an input method: one of
    692      * {@link #INPUT_METHOD_FROM_FOCUSABLE}, {@link #INPUT_METHOD_NEEDED},
    693      * or {@link #INPUT_METHOD_NOT_NEEDED}.
    694      *
    695      * <p>If the popup is showing, calling this method will take effect only
    696      * the next time the popup is shown or through a manual call to one of
    697      * the {@link #update()} methods.</p>
    698      *
    699      * @see #getInputMethodMode()
    700      * @see #update()
    701      */
    702     public void setInputMethodMode(int mode) {
    703         mInputMethodMode = mode;
    704     }
    705 
    706     /**
    707      * Sets the operating mode for the soft input area.
    708      *
    709      * @param mode The desired mode, see
    710      *        {@link android.view.WindowManager.LayoutParams#softInputMode}
    711      *        for the full list
    712      *
    713      * @see android.view.WindowManager.LayoutParams#softInputMode
    714      * @see #getSoftInputMode()
    715      */
    716     public void setSoftInputMode(int mode) {
    717         mSoftInputMode = mode;
    718     }
    719 
    720     /**
    721      * Returns the current value in {@link #setSoftInputMode(int)}.
    722      *
    723      * @see #setSoftInputMode(int)
    724      * @see android.view.WindowManager.LayoutParams#softInputMode
    725      */
    726     public int getSoftInputMode() {
    727         return mSoftInputMode;
    728     }
    729 
    730     /**
    731      * <p>Indicates whether the popup window receives touch events.</p>
    732      *
    733      * @return true if the popup is touchable, false otherwise
    734      *
    735      * @see #setTouchable(boolean)
    736      */
    737     public boolean isTouchable() {
    738         return mTouchable;
    739     }
    740 
    741     /**
    742      * <p>Changes the touchability of the popup window. When touchable, the
    743      * window will receive touch events, otherwise touch events will go to the
    744      * window below it. By default the window is touchable.</p>
    745      *
    746      * <p>If the popup is showing, calling this method will take effect only
    747      * the next time the popup is shown or through a manual call to one of
    748      * the {@link #update()} methods.</p>
    749      *
    750      * @param touchable true if the popup should receive touch events, false otherwise
    751      *
    752      * @see #isTouchable()
    753      * @see #isShowing()
    754      * @see #update()
    755      */
    756     public void setTouchable(boolean touchable) {
    757         mTouchable = touchable;
    758     }
    759 
    760     /**
    761      * <p>Indicates whether the popup window will be informed of touch events
    762      * outside of its window.</p>
    763      *
    764      * @return true if the popup is outside touchable, false otherwise
    765      *
    766      * @see #setOutsideTouchable(boolean)
    767      */
    768     public boolean isOutsideTouchable() {
    769         return mOutsideTouchable;
    770     }
    771 
    772     /**
    773      * <p>Controls whether the pop-up will be informed of touch events outside
    774      * of its window.  This only makes sense for pop-ups that are touchable
    775      * but not focusable, which means touches outside of the window will
    776      * be delivered to the window behind.  The default is false.</p>
    777      *
    778      * <p>If the popup is showing, calling this method will take effect only
    779      * the next time the popup is shown or through a manual call to one of
    780      * the {@link #update()} methods.</p>
    781      *
    782      * @param touchable true if the popup should receive outside
    783      * touch events, false otherwise
    784      *
    785      * @see #isOutsideTouchable()
    786      * @see #isShowing()
    787      * @see #update()
    788      */
    789     public void setOutsideTouchable(boolean touchable) {
    790         mOutsideTouchable = touchable;
    791     }
    792 
    793     /**
    794      * <p>Indicates whether clipping of the popup window is enabled.</p>
    795      *
    796      * @return true if the clipping is enabled, false otherwise
    797      *
    798      * @see #setClippingEnabled(boolean)
    799      */
    800     public boolean isClippingEnabled() {
    801         return mClippingEnabled;
    802     }
    803 
    804     /**
    805      * <p>Allows the popup window to extend beyond the bounds of the screen. By default the
    806      * window is clipped to the screen boundaries. Setting this to false will allow windows to be
    807      * accurately positioned.</p>
    808      *
    809      * <p>If the popup is showing, calling this method will take effect only
    810      * the next time the popup is shown or through a manual call to one of
    811      * the {@link #update()} methods.</p>
    812      *
    813      * @param enabled false if the window should be allowed to extend outside of the screen
    814      * @see #isShowing()
    815      * @see #isClippingEnabled()
    816      * @see #update()
    817      */
    818     public void setClippingEnabled(boolean enabled) {
    819         mClippingEnabled = enabled;
    820     }
    821 
    822     /**
    823      * Clip this popup window to the screen, but not to the containing window.
    824      *
    825      * @param enabled True to clip to the screen.
    826      * @hide
    827      */
    828     public void setClipToScreenEnabled(boolean enabled) {
    829         mClipToScreen = enabled;
    830     }
    831 
    832     /**
    833      * Allow PopupWindow to scroll the anchor's parent to provide more room
    834      * for the popup. Enabled by default.
    835      *
    836      * @param enabled True to scroll the anchor's parent when more room is desired by the popup.
    837      */
    838     void setAllowScrollingAnchorParent(boolean enabled) {
    839         mAllowScrollingAnchorParent = enabled;
    840     }
    841 
    842     /**
    843      * <p>Indicates whether the popup window supports splitting touches.</p>
    844      *
    845      * @return true if the touch splitting is enabled, false otherwise
    846      *
    847      * @see #setSplitTouchEnabled(boolean)
    848      */
    849     public boolean isSplitTouchEnabled() {
    850         if (mSplitTouchEnabled < 0 && mContext != null) {
    851             return mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB;
    852         }
    853         return mSplitTouchEnabled == 1;
    854     }
    855 
    856     /**
    857      * <p>Allows the popup window to split touches across other windows that also
    858      * support split touch.  When this flag is false, the first pointer
    859      * that goes down determines the window to which all subsequent touches
    860      * go until all pointers go up.  When this flag is true, each pointer
    861      * (not necessarily the first) that goes down determines the window
    862      * to which all subsequent touches of that pointer will go until that
    863      * pointer goes up thereby enabling touches with multiple pointers
    864      * to be split across multiple windows.</p>
    865      *
    866      * @param enabled true if the split touches should be enabled, false otherwise
    867      * @see #isSplitTouchEnabled()
    868      */
    869     public void setSplitTouchEnabled(boolean enabled) {
    870         mSplitTouchEnabled = enabled ? 1 : 0;
    871     }
    872 
    873     /**
    874      * <p>Indicates whether the popup window will be forced into using absolute screen coordinates
    875      * for positioning.</p>
    876      *
    877      * @return true if the window will always be positioned in screen coordinates.
    878      * @hide
    879      */
    880     public boolean isLayoutInScreenEnabled() {
    881         return mLayoutInScreen;
    882     }
    883 
    884     /**
    885      * <p>Allows the popup window to force the flag
    886      * {@link WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN}, overriding default behavior.
    887      * This will cause the popup to be positioned in absolute screen coordinates.</p>
    888      *
    889      * @param enabled true if the popup should always be positioned in screen coordinates
    890      * @hide
    891      */
    892     public void setLayoutInScreenEnabled(boolean enabled) {
    893         mLayoutInScreen = enabled;
    894     }
    895 
    896     /**
    897      * <p>Indicates whether the popup window will be attached in the decor frame of its parent
    898      * window.
    899      *
    900      * @return true if the window will be attached to the decor frame of its parent window.
    901      *
    902      * @see #setAttachedInDecor(boolean)
    903      * @see WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR
    904      */
    905     public boolean isAttachedInDecor() {
    906         return mAttachedInDecor;
    907     }
    908 
    909     /**
    910      * <p>This will attach the popup window to the decor frame of the parent window to avoid
    911      * overlaping with screen decorations like the navigation bar. Overrides the default behavior of
    912      * the flag {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR}.
    913      *
    914      * <p>By default the flag is set on SDK version {@link Build.VERSION_CODES#LOLLIPOP_MR1} or
    915      * greater and cleared on lesser SDK versions.
    916      *
    917      * @param enabled true if the popup should be attached to the decor frame of its parent window.
    918      *
    919      * @see WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR
    920      */
    921     public void setAttachedInDecor(boolean enabled) {
    922         mAttachedInDecor = enabled;
    923         mAttachedInDecorSet = true;
    924     }
    925 
    926     /**
    927      * Allows the popup window to force the flag
    928      * {@link WindowManager.LayoutParams#FLAG_LAYOUT_INSET_DECOR}, overriding default behavior.
    929      * This will cause the popup to inset its content to account for system windows overlaying
    930      * the screen, such as the status bar.
    931      *
    932      * <p>This will often be combined with {@link #setLayoutInScreenEnabled(boolean)}.
    933      *
    934      * @param enabled true if the popup's views should inset content to account for system windows,
    935      *                the way that decor views behave for full-screen windows.
    936      * @hide
    937      */
    938     public void setLayoutInsetDecor(boolean enabled) {
    939         mLayoutInsetDecor = enabled;
    940     }
    941 
    942     /**
    943      * Set the layout type for this window.
    944      * <p>
    945      * See {@link WindowManager.LayoutParams#type} for possible values.
    946      *
    947      * @param layoutType Layout type for this window.
    948      *
    949      * @see WindowManager.LayoutParams#type
    950      */
    951     public void setWindowLayoutType(int layoutType) {
    952         mWindowLayoutType = layoutType;
    953     }
    954 
    955     /**
    956      * Returns the layout type for this window.
    957      *
    958      * @see #setWindowLayoutType(int)
    959      */
    960     public int getWindowLayoutType() {
    961         return mWindowLayoutType;
    962     }
    963 
    964     /**
    965      * Set whether this window is touch modal or if outside touches will be sent to
    966      * other windows behind it.
    967      * @hide
    968      */
    969     public void setTouchModal(boolean touchModal) {
    970         mNotTouchModal = !touchModal;
    971     }
    972 
    973     /**
    974      * <p>Change the width and height measure specs that are given to the
    975      * window manager by the popup.  By default these are 0, meaning that
    976      * the current width or height is requested as an explicit size from
    977      * the window manager.  You can supply
    978      * {@link ViewGroup.LayoutParams#WRAP_CONTENT} or
    979      * {@link ViewGroup.LayoutParams#MATCH_PARENT} to have that measure
    980      * spec supplied instead, replacing the absolute width and height that
    981      * has been set in the popup.</p>
    982      *
    983      * <p>If the popup is showing, calling this method will take effect only
    984      * the next time the popup is shown.</p>
    985      *
    986      * @param widthSpec an explicit width measure spec mode, either
    987      * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
    988      * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
    989      * width.
    990      * @param heightSpec an explicit height measure spec mode, either
    991      * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
    992      * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
    993      * height.
    994      *
    995      * @deprecated Use {@link #setWidth(int)} and {@link #setHeight(int)}.
    996      */
    997     @Deprecated
    998     public void setWindowLayoutMode(int widthSpec, int heightSpec) {
    999         mWidthMode = widthSpec;
   1000         mHeightMode = heightSpec;
   1001     }
   1002 
   1003     /**
   1004      * Returns the popup's requested height. May be a layout constant such as
   1005      * {@link LayoutParams#WRAP_CONTENT} or {@link LayoutParams#MATCH_PARENT}.
   1006      * <p>
   1007      * The actual size of the popup may depend on other factors such as
   1008      * clipping and window layout.
   1009      *
   1010      * @return the popup height in pixels or a layout constant
   1011      * @see #setHeight(int)
   1012      */
   1013     public int getHeight() {
   1014         return mHeight;
   1015     }
   1016 
   1017     /**
   1018      * Sets the popup's requested height. May be a layout constant such as
   1019      * {@link LayoutParams#WRAP_CONTENT} or {@link LayoutParams#MATCH_PARENT}.
   1020      * <p>
   1021      * The actual size of the popup may depend on other factors such as
   1022      * clipping and window layout.
   1023      * <p>
   1024      * If the popup is showing, calling this method will take effect the next
   1025      * time the popup is shown.
   1026      *
   1027      * @param height the popup height in pixels or a layout constant
   1028      * @see #getHeight()
   1029      * @see #isShowing()
   1030      */
   1031     public void setHeight(int height) {
   1032         mHeight = height;
   1033     }
   1034 
   1035     /**
   1036      * Returns the popup's requested width. May be a layout constant such as
   1037      * {@link LayoutParams#WRAP_CONTENT} or {@link LayoutParams#MATCH_PARENT}.
   1038      * <p>
   1039      * The actual size of the popup may depend on other factors such as
   1040      * clipping and window layout.
   1041      *
   1042      * @return the popup width in pixels or a layout constant
   1043      * @see #setWidth(int)
   1044      */
   1045     public int getWidth() {
   1046         return mWidth;
   1047     }
   1048 
   1049     /**
   1050      * Sets the popup's requested width. May be a layout constant such as
   1051      * {@link LayoutParams#WRAP_CONTENT} or {@link LayoutParams#MATCH_PARENT}.
   1052      * <p>
   1053      * The actual size of the popup may depend on other factors such as
   1054      * clipping and window layout.
   1055      * <p>
   1056      * If the popup is showing, calling this method will take effect the next
   1057      * time the popup is shown.
   1058      *
   1059      * @param width the popup width in pixels or a layout constant
   1060      * @see #getWidth()
   1061      * @see #isShowing()
   1062      */
   1063     public void setWidth(int width) {
   1064         mWidth = width;
   1065     }
   1066 
   1067     /**
   1068      * Sets whether the popup window should overlap its anchor view when
   1069      * displayed as a drop-down.
   1070      * <p>
   1071      * If the popup is showing, calling this method will take effect only
   1072      * the next time the popup is shown.
   1073      *
   1074      * @param overlapAnchor Whether the popup should overlap its anchor.
   1075      *
   1076      * @see #getOverlapAnchor()
   1077      * @see #isShowing()
   1078      */
   1079     public void setOverlapAnchor(boolean overlapAnchor) {
   1080         mOverlapAnchor = overlapAnchor;
   1081     }
   1082 
   1083     /**
   1084      * Returns whether the popup window should overlap its anchor view when
   1085      * displayed as a drop-down.
   1086      *
   1087      * @return Whether the popup should overlap its anchor.
   1088      *
   1089      * @see #setOverlapAnchor(boolean)
   1090      */
   1091     public boolean getOverlapAnchor() {
   1092         return mOverlapAnchor;
   1093     }
   1094 
   1095     /**
   1096      * <p>Indicate whether this popup window is showing on screen.</p>
   1097      *
   1098      * @return true if the popup is showing, false otherwise
   1099      */
   1100     public boolean isShowing() {
   1101         return mIsShowing;
   1102     }
   1103 
   1104     /**
   1105      * <p>
   1106      * Display the content view in a popup window at the specified location. If the popup window
   1107      * cannot fit on screen, it will be clipped. See {@link android.view.WindowManager.LayoutParams}
   1108      * for more information on how gravity and the x and y parameters are related. Specifying
   1109      * a gravity of {@link android.view.Gravity#NO_GRAVITY} is similar to specifying
   1110      * <code>Gravity.LEFT | Gravity.TOP</code>.
   1111      * </p>
   1112      *
   1113      * @param parent a parent view to get the {@link android.view.View#getWindowToken()} token from
   1114      * @param gravity the gravity which controls the placement of the popup window
   1115      * @param x the popup's x location offset
   1116      * @param y the popup's y location offset
   1117      */
   1118     public void showAtLocation(View parent, int gravity, int x, int y) {
   1119         showAtLocation(parent.getWindowToken(), gravity, x, y);
   1120     }
   1121 
   1122     /**
   1123      * Display the content view in a popup window at the specified location.
   1124      *
   1125      * @param token Window token to use for creating the new window
   1126      * @param gravity the gravity which controls the placement of the popup window
   1127      * @param x the popup's x location offset
   1128      * @param y the popup's y location offset
   1129      *
   1130      * @hide Internal use only. Applications should use
   1131      *       {@link #showAtLocation(View, int, int, int)} instead.
   1132      */
   1133     public void showAtLocation(IBinder token, int gravity, int x, int y) {
   1134         if (isShowing() || mContentView == null) {
   1135             return;
   1136         }
   1137 
   1138         TransitionManager.endTransitions(mDecorView);
   1139 
   1140         detachFromAnchor();
   1141 
   1142         mIsShowing = true;
   1143         mIsDropdown = false;
   1144 
   1145         final WindowManager.LayoutParams p = createPopupLayoutParams(token);
   1146         preparePopup(p);
   1147 
   1148         // Only override the default if some gravity was specified.
   1149         if (gravity != Gravity.NO_GRAVITY) {
   1150             p.gravity = gravity;
   1151         }
   1152 
   1153         p.x = x;
   1154         p.y = y;
   1155 
   1156         invokePopup(p);
   1157     }
   1158 
   1159     /**
   1160      * Display the content view in a popup window anchored to the bottom-left
   1161      * corner of the anchor view. If there is not enough room on screen to show
   1162      * the popup in its entirety, this method tries to find a parent scroll
   1163      * view to scroll. If no parent scroll view can be scrolled, the
   1164      * bottom-left corner of the popup is pinned at the top left corner of the
   1165      * anchor view.
   1166      *
   1167      * @param anchor the view on which to pin the popup window
   1168      *
   1169      * @see #dismiss()
   1170      */
   1171     public void showAsDropDown(View anchor) {
   1172         showAsDropDown(anchor, 0, 0);
   1173     }
   1174 
   1175     /**
   1176      * Display the content view in a popup window anchored to the bottom-left
   1177      * corner of the anchor view offset by the specified x and y coordinates.
   1178      * If there is not enough room on screen to show the popup in its entirety,
   1179      * this method tries to find a parent scroll view to scroll. If no parent
   1180      * scroll view can be scrolled, the bottom-left corner of the popup is
   1181      * pinned at the top left corner of the anchor view.
   1182      * <p>
   1183      * If the view later scrolls to move <code>anchor</code> to a different
   1184      * location, the popup will be moved correspondingly.
   1185      *
   1186      * @param anchor the view on which to pin the popup window
   1187      * @param xoff A horizontal offset from the anchor in pixels
   1188      * @param yoff A vertical offset from the anchor in pixels
   1189      *
   1190      * @see #dismiss()
   1191      */
   1192     public void showAsDropDown(View anchor, int xoff, int yoff) {
   1193         showAsDropDown(anchor, xoff, yoff, DEFAULT_ANCHORED_GRAVITY);
   1194     }
   1195 
   1196     /**
   1197      * Displays the content view in a popup window anchored to the corner of
   1198      * another view. The window is positioned according to the specified
   1199      * gravity and offset by the specified x and y coordinates.
   1200      * <p>
   1201      * If there is not enough room on screen to show the popup in its entirety,
   1202      * this method tries to find a parent scroll view to scroll. If no parent
   1203      * view can be scrolled, the specified vertical gravity will be ignored and
   1204      * the popup will anchor itself such that it is visible.
   1205      * <p>
   1206      * If the view later scrolls to move <code>anchor</code> to a different
   1207      * location, the popup will be moved correspondingly.
   1208      *
   1209      * @param anchor the view on which to pin the popup window
   1210      * @param xoff A horizontal offset from the anchor in pixels
   1211      * @param yoff A vertical offset from the anchor in pixels
   1212      * @param gravity Alignment of the popup relative to the anchor
   1213      *
   1214      * @see #dismiss()
   1215      */
   1216     public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
   1217         if (isShowing() || mContentView == null) {
   1218             return;
   1219         }
   1220 
   1221         TransitionManager.endTransitions(mDecorView);
   1222 
   1223         attachToAnchor(anchor, xoff, yoff, gravity);
   1224 
   1225         mIsShowing = true;
   1226         mIsDropdown = true;
   1227 
   1228         final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken());
   1229         preparePopup(p);
   1230 
   1231         final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff,
   1232                 p.width, p.height, gravity);
   1233         updateAboveAnchor(aboveAnchor);
   1234         p.accessibilityIdOfAnchor = (anchor != null) ? anchor.getAccessibilityViewId() : -1;
   1235 
   1236         invokePopup(p);
   1237     }
   1238 
   1239     private void updateAboveAnchor(boolean aboveAnchor) {
   1240         if (aboveAnchor != mAboveAnchor) {
   1241             mAboveAnchor = aboveAnchor;
   1242 
   1243             if (mBackground != null && mBackgroundView != null) {
   1244                 // If the background drawable provided was a StateListDrawable
   1245                 // with above-anchor and below-anchor states, use those.
   1246                 // Otherwise, rely on refreshDrawableState to do the job.
   1247                 if (mAboveAnchorBackgroundDrawable != null) {
   1248                     if (mAboveAnchor) {
   1249                         mBackgroundView.setBackground(mAboveAnchorBackgroundDrawable);
   1250                     } else {
   1251                         mBackgroundView.setBackground(mBelowAnchorBackgroundDrawable);
   1252                     }
   1253                 } else {
   1254                     mBackgroundView.refreshDrawableState();
   1255                 }
   1256             }
   1257         }
   1258     }
   1259 
   1260     /**
   1261      * Indicates whether the popup is showing above (the y coordinate of the popup's bottom
   1262      * is less than the y coordinate of the anchor) or below the anchor view (the y coordinate
   1263      * of the popup is greater than y coordinate of the anchor's bottom).
   1264      *
   1265      * The value returned
   1266      * by this method is meaningful only after {@link #showAsDropDown(android.view.View)}
   1267      * or {@link #showAsDropDown(android.view.View, int, int)} was invoked.
   1268      *
   1269      * @return True if this popup is showing above the anchor view, false otherwise.
   1270      */
   1271     public boolean isAboveAnchor() {
   1272         return mAboveAnchor;
   1273     }
   1274 
   1275     /**
   1276      * Prepare the popup by embedding it into a new ViewGroup if the background
   1277      * drawable is not null. If embedding is required, the layout parameters'
   1278      * height is modified to take into account the background's padding.
   1279      *
   1280      * @param p the layout parameters of the popup's content view
   1281      */
   1282     private void preparePopup(WindowManager.LayoutParams p) {
   1283         if (mContentView == null || mContext == null || mWindowManager == null) {
   1284             throw new IllegalStateException("You must specify a valid content view by "
   1285                     + "calling setContentView() before attempting to show the popup.");
   1286         }
   1287 
   1288         // The old decor view may be transitioning out. Make sure it finishes
   1289         // and cleans up before we try to create another one.
   1290         if (mDecorView != null) {
   1291             mDecorView.cancelTransitions();
   1292         }
   1293 
   1294         // When a background is available, we embed the content view within
   1295         // another view that owns the background drawable.
   1296         if (mBackground != null) {
   1297             mBackgroundView = createBackgroundView(mContentView);
   1298             mBackgroundView.setBackground(mBackground);
   1299         } else {
   1300             mBackgroundView = mContentView;
   1301         }
   1302 
   1303         mDecorView = createDecorView(mBackgroundView);
   1304 
   1305         // The background owner should be elevated so that it casts a shadow.
   1306         mBackgroundView.setElevation(mElevation);
   1307 
   1308         // We may wrap that in another view, so we'll need to manually specify
   1309         // the surface insets.
   1310         p.setSurfaceInsets(mBackgroundView, true /*manual*/, true /*preservePrevious*/);
   1311 
   1312         mPopupViewInitialLayoutDirectionInherited =
   1313                 (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
   1314     }
   1315 
   1316     /**
   1317      * Wraps a content view in a PopupViewContainer.
   1318      *
   1319      * @param contentView the content view to wrap
   1320      * @return a PopupViewContainer that wraps the content view
   1321      */
   1322     private PopupBackgroundView createBackgroundView(View contentView) {
   1323         final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
   1324         final int height;
   1325         if (layoutParams != null && layoutParams.height == WRAP_CONTENT) {
   1326             height = WRAP_CONTENT;
   1327         } else {
   1328             height = MATCH_PARENT;
   1329         }
   1330 
   1331         final PopupBackgroundView backgroundView = new PopupBackgroundView(mContext);
   1332         final PopupBackgroundView.LayoutParams listParams = new PopupBackgroundView.LayoutParams(
   1333                 MATCH_PARENT, height);
   1334         backgroundView.addView(contentView, listParams);
   1335 
   1336         return backgroundView;
   1337     }
   1338 
   1339     /**
   1340      * Wraps a content view in a FrameLayout.
   1341      *
   1342      * @param contentView the content view to wrap
   1343      * @return a FrameLayout that wraps the content view
   1344      */
   1345     private PopupDecorView createDecorView(View contentView) {
   1346         final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
   1347         final int height;
   1348         if (layoutParams != null && layoutParams.height == WRAP_CONTENT) {
   1349             height = WRAP_CONTENT;
   1350         } else {
   1351             height = MATCH_PARENT;
   1352         }
   1353 
   1354         final PopupDecorView decorView = new PopupDecorView(mContext);
   1355         decorView.addView(contentView, MATCH_PARENT, height);
   1356         decorView.setClipChildren(false);
   1357         decorView.setClipToPadding(false);
   1358 
   1359         return decorView;
   1360     }
   1361 
   1362     /**
   1363      * <p>Invoke the popup window by adding the content view to the window
   1364      * manager.</p>
   1365      *
   1366      * <p>The content view must be non-null when this method is invoked.</p>
   1367      *
   1368      * @param p the layout parameters of the popup's content view
   1369      */
   1370     private void invokePopup(WindowManager.LayoutParams p) {
   1371         if (mContext != null) {
   1372             p.packageName = mContext.getPackageName();
   1373         }
   1374 
   1375         final PopupDecorView decorView = mDecorView;
   1376         decorView.setFitsSystemWindows(mLayoutInsetDecor);
   1377 
   1378         setLayoutDirectionFromAnchor();
   1379 
   1380         mWindowManager.addView(decorView, p);
   1381 
   1382         if (mEnterTransition != null) {
   1383             decorView.requestEnterTransition(mEnterTransition);
   1384         }
   1385     }
   1386 
   1387     private void setLayoutDirectionFromAnchor() {
   1388         if (mAnchor != null) {
   1389             View anchor = mAnchor.get();
   1390             if (anchor != null && mPopupViewInitialLayoutDirectionInherited) {
   1391                 mDecorView.setLayoutDirection(anchor.getLayoutDirection());
   1392             }
   1393         }
   1394     }
   1395 
   1396     private int computeGravity() {
   1397         int gravity = Gravity.START | Gravity.TOP;
   1398         if (mClipToScreen || mClippingEnabled) {
   1399             gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
   1400         }
   1401         return gravity;
   1402     }
   1403 
   1404     /**
   1405      * <p>Generate the layout parameters for the popup window.</p>
   1406      *
   1407      * @param token the window token used to bind the popup's window
   1408      *
   1409      * @return the layout parameters to pass to the window manager
   1410      */
   1411     private WindowManager.LayoutParams createPopupLayoutParams(IBinder token) {
   1412         final WindowManager.LayoutParams p = new WindowManager.LayoutParams();
   1413 
   1414         // These gravity settings put the view at the top left corner of the
   1415         // screen. The view is then positioned to the appropriate location by
   1416         // setting the x and y offsets to match the anchor's bottom-left
   1417         // corner.
   1418         p.gravity = computeGravity();
   1419         p.flags = computeFlags(p.flags);
   1420         p.type = mWindowLayoutType;
   1421         p.token = token;
   1422         p.softInputMode = mSoftInputMode;
   1423         p.windowAnimations = computeAnimationResource();
   1424 
   1425         if (mBackground != null) {
   1426             p.format = mBackground.getOpacity();
   1427         } else {
   1428             p.format = PixelFormat.TRANSLUCENT;
   1429         }
   1430 
   1431         if (mHeightMode < 0) {
   1432             p.height = mLastHeight = mHeightMode;
   1433         } else {
   1434             p.height = mLastHeight = mHeight;
   1435         }
   1436 
   1437         if (mWidthMode < 0) {
   1438             p.width = mLastWidth = mWidthMode;
   1439         } else {
   1440             p.width = mLastWidth = mWidth;
   1441         }
   1442 
   1443         p.privateFlags = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH
   1444                 | PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
   1445 
   1446         // Used for debugging.
   1447         p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
   1448 
   1449         return p;
   1450     }
   1451 
   1452     private int computeFlags(int curFlags) {
   1453         curFlags &= ~(
   1454                 WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES |
   1455                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
   1456                 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
   1457                 WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
   1458                 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
   1459                 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
   1460                 WindowManager.LayoutParams.FLAG_SPLIT_TOUCH);
   1461         if(mIgnoreCheekPress) {
   1462             curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
   1463         }
   1464         if (!mFocusable) {
   1465             curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
   1466             if (mInputMethodMode == INPUT_METHOD_NEEDED) {
   1467                 curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
   1468             }
   1469         } else if (mInputMethodMode == INPUT_METHOD_NOT_NEEDED) {
   1470             curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
   1471         }
   1472         if (!mTouchable) {
   1473             curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
   1474         }
   1475         if (mOutsideTouchable) {
   1476             curFlags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
   1477         }
   1478         if (!mClippingEnabled || mClipToScreen) {
   1479             curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
   1480         }
   1481         if (isSplitTouchEnabled()) {
   1482             curFlags |= WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
   1483         }
   1484         if (mLayoutInScreen) {
   1485             curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
   1486         }
   1487         if (mLayoutInsetDecor) {
   1488             curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
   1489         }
   1490         if (mNotTouchModal) {
   1491             curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
   1492         }
   1493         if (mAttachedInDecor) {
   1494           curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR;
   1495         }
   1496         return curFlags;
   1497     }
   1498 
   1499     private int computeAnimationResource() {
   1500         if (mAnimationStyle == ANIMATION_STYLE_DEFAULT) {
   1501             if (mIsDropdown) {
   1502                 return mAboveAnchor
   1503                         ? com.android.internal.R.style.Animation_DropDownUp
   1504                         : com.android.internal.R.style.Animation_DropDownDown;
   1505             }
   1506             return 0;
   1507         }
   1508         return mAnimationStyle;
   1509     }
   1510 
   1511     /**
   1512      * Positions the popup window on screen. When the popup window is too tall
   1513      * to fit under the anchor, a parent scroll view is seeked and scrolled up
   1514      * to reclaim space. If scrolling is not possible or not enough, the popup
   1515      * window gets moved on top of the anchor.
   1516      * <p>
   1517      * The results of positioning are placed in {@code outParams}.
   1518      *
   1519      * @param anchor the view on which the popup window must be anchored
   1520      * @param outParams the layout parameters used to display the drop down
   1521      * @param xOffset absolute horizontal offset from the left of the anchor
   1522      * @param yOffset absolute vertical offset from the top of the anchor
   1523      * @param gravity horizontal gravity specifying popup alignment
   1524      * @return true if the popup is translated upwards to fit on screen
   1525      */
   1526     private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams,
   1527             int xOffset, int yOffset, int width, int height, int gravity) {
   1528         final int anchorHeight = anchor.getHeight();
   1529         final int anchorWidth = anchor.getWidth();
   1530         if (mOverlapAnchor) {
   1531             yOffset -= anchorHeight;
   1532         }
   1533 
   1534         // Initially, align to the bottom-left corner of the anchor plus offsets.
   1535         final int[] drawingLocation = mTmpDrawingLocation;
   1536         anchor.getLocationInWindow(drawingLocation);
   1537         outParams.x = drawingLocation[0] + xOffset;
   1538         outParams.y = drawingLocation[1] + anchorHeight + yOffset;
   1539 
   1540         final Rect displayFrame = new Rect();
   1541         anchor.getWindowVisibleDisplayFrame(displayFrame);
   1542         if (width == MATCH_PARENT) {
   1543             width = displayFrame.right - displayFrame.left;
   1544         }
   1545         if (height == MATCH_PARENT) {
   1546             height = displayFrame.bottom - displayFrame.top;
   1547         }
   1548 
   1549         // Let the window manager know to align the top to y.
   1550         outParams.gravity = Gravity.LEFT | Gravity.TOP;
   1551         outParams.width = width;
   1552         outParams.height = height;
   1553 
   1554         // If we need to adjust for gravity RIGHT, align to the bottom-right
   1555         // corner of the anchor (still accounting for offsets).
   1556         final int hgrav = Gravity.getAbsoluteGravity(gravity, anchor.getLayoutDirection())
   1557                 & Gravity.HORIZONTAL_GRAVITY_MASK;
   1558         if (hgrav == Gravity.RIGHT) {
   1559             outParams.x -= width - anchorWidth;
   1560         }
   1561 
   1562         final int[] screenLocation = mTmpScreenLocation;
   1563         anchor.getLocationOnScreen(screenLocation);
   1564 
   1565         // First, attempt to fit the popup vertically without resizing.
   1566         final boolean fitsVertical = tryFitVertical(outParams, yOffset, height,
   1567                 anchorHeight, drawingLocation[1], screenLocation[1], displayFrame.top,
   1568                 displayFrame.bottom, false);
   1569 
   1570         // Next, attempt to fit the popup horizontally without resizing.
   1571         final boolean fitsHorizontal = tryFitHorizontal(outParams, xOffset, width,
   1572                 anchorWidth, drawingLocation[0], screenLocation[0], displayFrame.left,
   1573                 displayFrame.right, false);
   1574 
   1575         // If the popup still doesn't fit, attempt to scroll the parent.
   1576         if (!fitsVertical || !fitsHorizontal) {
   1577             final int scrollX = anchor.getScrollX();
   1578             final int scrollY = anchor.getScrollY();
   1579             final Rect r = new Rect(scrollX, scrollY, scrollX + width + xOffset,
   1580                     scrollY + height + anchorHeight + yOffset);
   1581             if (mAllowScrollingAnchorParent && anchor.requestRectangleOnScreen(r, true)) {
   1582                 // Reset for the new anchor position.
   1583                 anchor.getLocationInWindow(drawingLocation);
   1584                 outParams.x = drawingLocation[0] + xOffset;
   1585                 outParams.y = drawingLocation[1] + anchorHeight + yOffset;
   1586 
   1587                 // Preserve the gravity adjustment.
   1588                 if (hgrav == Gravity.RIGHT) {
   1589                     outParams.x -= width - anchorWidth;
   1590                 }
   1591             }
   1592 
   1593             // Try to fit the popup again and allowing resizing.
   1594             tryFitVertical(outParams, yOffset, height, anchorHeight, drawingLocation[1],
   1595                     screenLocation[1], displayFrame.top, displayFrame.bottom, mClipToScreen);
   1596             tryFitHorizontal(outParams, xOffset, width, anchorWidth, drawingLocation[0],
   1597                     screenLocation[0], displayFrame.left, displayFrame.right, mClipToScreen);
   1598         }
   1599 
   1600         // Return whether the popup's top edge is above the anchor's top edge.
   1601         return outParams.y < drawingLocation[1];
   1602     }
   1603 
   1604     private boolean tryFitVertical(@NonNull LayoutParams outParams, int yOffset, int height,
   1605             int anchorHeight, int drawingLocationY, int screenLocationY, int displayFrameTop,
   1606             int displayFrameBottom, boolean allowResize) {
   1607         final int winOffsetY = screenLocationY - drawingLocationY;
   1608         final int anchorTopInScreen = outParams.y + winOffsetY;
   1609         final int spaceBelow = displayFrameBottom - anchorTopInScreen;
   1610         if (anchorTopInScreen >= 0 && height <= spaceBelow) {
   1611             return true;
   1612         }
   1613 
   1614         final int spaceAbove = anchorTopInScreen - anchorHeight - displayFrameTop;
   1615         if (height <= spaceAbove) {
   1616             // Move everything up.
   1617             if (mOverlapAnchor) {
   1618                 yOffset += anchorHeight;
   1619             }
   1620             outParams.y = drawingLocationY - height + yOffset;
   1621 
   1622             return true;
   1623         }
   1624 
   1625         if (positionInDisplayVertical(outParams, height, drawingLocationY, screenLocationY,
   1626                 displayFrameTop, displayFrameBottom, allowResize)) {
   1627             return true;
   1628         }
   1629 
   1630         return false;
   1631     }
   1632 
   1633     private boolean positionInDisplayVertical(@NonNull LayoutParams outParams, int height,
   1634             int drawingLocationY, int screenLocationY, int displayFrameTop, int displayFrameBottom,
   1635             boolean canResize) {
   1636         boolean fitsInDisplay = true;
   1637 
   1638         final int winOffsetY = screenLocationY - drawingLocationY;
   1639         outParams.y += winOffsetY;
   1640         outParams.height = height;
   1641 
   1642         final int bottom = outParams.y + height;
   1643         if (bottom > displayFrameBottom) {
   1644             // The popup is too far down, move it back in.
   1645             outParams.y -= bottom - displayFrameBottom;
   1646         }
   1647 
   1648         if (outParams.y < displayFrameTop) {
   1649             // The popup is too far up, move it back in and clip if
   1650             // it's still too large.
   1651             outParams.y = displayFrameTop;
   1652 
   1653             final int displayFrameHeight = displayFrameBottom - displayFrameTop;
   1654             if (canResize && height > displayFrameHeight) {
   1655                 outParams.height = displayFrameHeight;
   1656             } else {
   1657                 fitsInDisplay = false;
   1658             }
   1659         }
   1660 
   1661         outParams.y -= winOffsetY;
   1662 
   1663         return fitsInDisplay;
   1664     }
   1665 
   1666     private boolean tryFitHorizontal(@NonNull LayoutParams outParams, int xOffset, int width,
   1667             int anchorWidth, int drawingLocationX, int screenLocationX, int displayFrameLeft,
   1668             int displayFrameRight, boolean allowResize) {
   1669         final int winOffsetX = screenLocationX - drawingLocationX;
   1670         final int anchorLeftInScreen = outParams.x + winOffsetX;
   1671         final int spaceRight = displayFrameRight - anchorLeftInScreen;
   1672         if (anchorLeftInScreen >= 0 && width <= spaceRight) {
   1673             return true;
   1674         }
   1675 
   1676         if (positionInDisplayHorizontal(outParams, width, drawingLocationX, screenLocationX,
   1677                 displayFrameLeft, displayFrameRight, allowResize)) {
   1678             return true;
   1679         }
   1680 
   1681         return false;
   1682     }
   1683 
   1684     private boolean positionInDisplayHorizontal(@NonNull LayoutParams outParams, int width,
   1685             int drawingLocationX, int screenLocationX, int displayFrameLeft, int displayFrameRight,
   1686             boolean canResize) {
   1687         boolean fitsInDisplay = true;
   1688 
   1689         // Use screen coordinates for comparison against display frame.
   1690         final int winOffsetX = screenLocationX - drawingLocationX;
   1691         outParams.x += winOffsetX;
   1692 
   1693         final int right = outParams.x + width;
   1694         if (right > displayFrameRight) {
   1695             // The popup is too far right, move it back in.
   1696             outParams.x -= right - displayFrameRight;
   1697         }
   1698 
   1699         if (outParams.x < displayFrameLeft) {
   1700             // The popup is too far left, move it back in and clip if it's
   1701             // still too large.
   1702             outParams.x = displayFrameLeft;
   1703 
   1704             final int displayFrameWidth = displayFrameRight - displayFrameLeft;
   1705             if (canResize && width > displayFrameWidth) {
   1706                 outParams.width = displayFrameWidth;
   1707             } else {
   1708                 fitsInDisplay = false;
   1709             }
   1710         }
   1711 
   1712         outParams.x -= winOffsetX;
   1713 
   1714         return fitsInDisplay;
   1715     }
   1716 
   1717     /**
   1718      * Returns the maximum height that is available for the popup to be
   1719      * completely shown. It is recommended that this height be the maximum for
   1720      * the popup's height, otherwise it is possible that the popup will be
   1721      * clipped.
   1722      *
   1723      * @param anchor The view on which the popup window must be anchored.
   1724      * @return The maximum available height for the popup to be completely
   1725      *         shown.
   1726      */
   1727     public int getMaxAvailableHeight(@NonNull View anchor) {
   1728         return getMaxAvailableHeight(anchor, 0);
   1729     }
   1730 
   1731     /**
   1732      * Returns the maximum height that is available for the popup to be
   1733      * completely shown. It is recommended that this height be the maximum for
   1734      * the popup's height, otherwise it is possible that the popup will be
   1735      * clipped.
   1736      *
   1737      * @param anchor The view on which the popup window must be anchored.
   1738      * @param yOffset y offset from the view's bottom edge
   1739      * @return The maximum available height for the popup to be completely
   1740      *         shown.
   1741      */
   1742     public int getMaxAvailableHeight(@NonNull View anchor, int yOffset) {
   1743         return getMaxAvailableHeight(anchor, yOffset, false);
   1744     }
   1745 
   1746     /**
   1747      * Returns the maximum height that is available for the popup to be
   1748      * completely shown, optionally ignoring any bottom decorations such as
   1749      * the input method. It is recommended that this height be the maximum for
   1750      * the popup's height, otherwise it is possible that the popup will be
   1751      * clipped.
   1752      *
   1753      * @param anchor The view on which the popup window must be anchored.
   1754      * @param yOffset y offset from the view's bottom edge
   1755      * @param ignoreBottomDecorations if true, the height returned will be
   1756      *        all the way to the bottom of the display, ignoring any
   1757      *        bottom decorations
   1758      * @return The maximum available height for the popup to be completely
   1759      *         shown.
   1760      */
   1761     public int getMaxAvailableHeight(
   1762             @NonNull View anchor, int yOffset, boolean ignoreBottomDecorations) {
   1763         final Rect displayFrame = new Rect();
   1764         if (ignoreBottomDecorations) {
   1765             anchor.getWindowDisplayFrame(displayFrame);
   1766         } else {
   1767             anchor.getWindowVisibleDisplayFrame(displayFrame);
   1768         }
   1769 
   1770         final int[] anchorPos = mTmpDrawingLocation;
   1771         anchor.getLocationOnScreen(anchorPos);
   1772 
   1773         final int bottomEdge = displayFrame.bottom;
   1774 
   1775         final int distanceToBottom;
   1776         if (mOverlapAnchor) {
   1777             distanceToBottom = bottomEdge - anchorPos[1] - yOffset;
   1778         } else {
   1779             distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
   1780         }
   1781         final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
   1782 
   1783         // anchorPos[1] is distance from anchor to top of screen
   1784         int returnedHeight = Math.max(distanceToBottom, distanceToTop);
   1785         if (mBackground != null) {
   1786             mBackground.getPadding(mTempRect);
   1787             returnedHeight -= mTempRect.top + mTempRect.bottom;
   1788         }
   1789 
   1790         return returnedHeight;
   1791     }
   1792 
   1793     /**
   1794      * Disposes of the popup window. This method can be invoked only after
   1795      * {@link #showAsDropDown(android.view.View)} has been executed. Failing
   1796      * that, calling this method will have no effect.
   1797      *
   1798      * @see #showAsDropDown(android.view.View)
   1799      */
   1800     public void dismiss() {
   1801         if (!isShowing() || mIsTransitioningToDismiss) {
   1802             return;
   1803         }
   1804 
   1805         final PopupDecorView decorView = mDecorView;
   1806         final View contentView = mContentView;
   1807 
   1808         final ViewGroup contentHolder;
   1809         final ViewParent contentParent = contentView.getParent();
   1810         if (contentParent instanceof ViewGroup) {
   1811             contentHolder = ((ViewGroup) contentParent);
   1812         } else {
   1813             contentHolder = null;
   1814         }
   1815 
   1816         // Ensure any ongoing or pending transitions are canceled.
   1817         decorView.cancelTransitions();
   1818 
   1819         mIsShowing = false;
   1820         mIsTransitioningToDismiss = true;
   1821 
   1822         // This method may be called as part of window detachment, in which
   1823         // case the anchor view (and its root) will still return true from
   1824         // isAttachedToWindow() during execution of this method; however, we
   1825         // can expect the OnAttachStateChangeListener to have been called prior
   1826         // to executing this method, so we can rely on that instead.
   1827         final Transition exitTransition = mExitTransition;
   1828         if (mIsAnchorRootAttached && exitTransition != null && decorView.isLaidOut()) {
   1829             // The decor view is non-interactive and non-IME-focusable during exit transitions.
   1830             final LayoutParams p = (LayoutParams) decorView.getLayoutParams();
   1831             p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
   1832             p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
   1833             p.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
   1834             mWindowManager.updateViewLayout(decorView, p);
   1835 
   1836             // Once we start dismissing the decor view, all state (including
   1837             // the anchor root) needs to be moved to the decor view since we
   1838             // may open another popup while it's busy exiting.
   1839             final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
   1840             final Rect epicenter = getTransitionEpicenter();
   1841             exitTransition.setEpicenterCallback(new EpicenterCallback() {
   1842                 @Override
   1843                 public Rect onGetEpicenter(Transition transition) {
   1844                     return epicenter;
   1845                 }
   1846             });
   1847             decorView.startExitTransition(exitTransition, anchorRoot,
   1848                     new TransitionListenerAdapter() {
   1849                         @Override
   1850                         public void onTransitionEnd(Transition transition) {
   1851                             dismissImmediate(decorView, contentHolder, contentView);
   1852                         }
   1853                     });
   1854         } else {
   1855             dismissImmediate(decorView, contentHolder, contentView);
   1856         }
   1857 
   1858         // Clears the anchor view.
   1859         detachFromAnchor();
   1860 
   1861         if (mOnDismissListener != null) {
   1862             mOnDismissListener.onDismiss();
   1863         }
   1864     }
   1865 
   1866     /**
   1867      * Returns the window-relative epicenter bounds to be used by enter and
   1868      * exit transitions.
   1869      * <p>
   1870      * <strong>Note:</strong> This is distinct from the rect passed to
   1871      * {@link #setEpicenterBounds(Rect)}, which is anchor-relative.
   1872      *
   1873      * @return the window-relative epicenter bounds to be used by enter and
   1874      *         exit transitions
   1875      */
   1876     private Rect getTransitionEpicenter() {
   1877         final View anchor = mAnchor != null ? mAnchor.get() : null;
   1878         final View decor = mDecorView;
   1879         if (anchor == null || decor == null) {
   1880             return null;
   1881         }
   1882 
   1883         final int[] anchorLocation = anchor.getLocationOnScreen();
   1884         final int[] popupLocation = mDecorView.getLocationOnScreen();
   1885 
   1886         // Compute the position of the anchor relative to the popup.
   1887         final Rect bounds = new Rect(0, 0, anchor.getWidth(), anchor.getHeight());
   1888         bounds.offset(anchorLocation[0] - popupLocation[0], anchorLocation[1] - popupLocation[1]);
   1889 
   1890         // Use anchor-relative epicenter, if specified.
   1891         if (mEpicenterBounds != null) {
   1892             final int offsetX = bounds.left;
   1893             final int offsetY = bounds.top;
   1894             bounds.set(mEpicenterBounds);
   1895             bounds.offset(offsetX, offsetY);
   1896         }
   1897 
   1898         return bounds;
   1899     }
   1900 
   1901     /**
   1902      * Removes the popup from the window manager and tears down the supporting
   1903      * view hierarchy, if necessary.
   1904      */
   1905     private void dismissImmediate(View decorView, ViewGroup contentHolder, View contentView) {
   1906         // If this method gets called and the decor view doesn't have a parent,
   1907         // then it was either never added or was already removed. That should
   1908         // never happen, but it's worth checking to avoid potential crashes.
   1909         if (decorView.getParent() != null) {
   1910             mWindowManager.removeViewImmediate(decorView);
   1911         }
   1912 
   1913         if (contentHolder != null) {
   1914             contentHolder.removeView(contentView);
   1915         }
   1916 
   1917         // This needs to stay until after all transitions have ended since we
   1918         // need the reference to cancel transitions in preparePopup().
   1919         mDecorView = null;
   1920         mBackgroundView = null;
   1921         mIsTransitioningToDismiss = false;
   1922     }
   1923 
   1924     /**
   1925      * Sets the listener to be called when the window is dismissed.
   1926      *
   1927      * @param onDismissListener The listener.
   1928      */
   1929     public void setOnDismissListener(OnDismissListener onDismissListener) {
   1930         mOnDismissListener = onDismissListener;
   1931     }
   1932 
   1933     /**
   1934      * Updates the state of the popup window, if it is currently being displayed,
   1935      * from the currently set state.
   1936      * <p>
   1937      * This includes:
   1938      * <ul>
   1939      *     <li>{@link #setClippingEnabled(boolean)}</li>
   1940      *     <li>{@link #setFocusable(boolean)}</li>
   1941      *     <li>{@link #setIgnoreCheekPress()}</li>
   1942      *     <li>{@link #setInputMethodMode(int)}</li>
   1943      *     <li>{@link #setTouchable(boolean)}</li>
   1944      *     <li>{@link #setAnimationStyle(int)}</li>
   1945      * </ul>
   1946      */
   1947     public void update() {
   1948         if (!isShowing() || mContentView == null) {
   1949             return;
   1950         }
   1951 
   1952         final WindowManager.LayoutParams p =
   1953                 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
   1954 
   1955         boolean update = false;
   1956 
   1957         final int newAnim = computeAnimationResource();
   1958         if (newAnim != p.windowAnimations) {
   1959             p.windowAnimations = newAnim;
   1960             update = true;
   1961         }
   1962 
   1963         final int newFlags = computeFlags(p.flags);
   1964         if (newFlags != p.flags) {
   1965             p.flags = newFlags;
   1966             update = true;
   1967         }
   1968 
   1969         final int newGravity = computeGravity();
   1970         if (newGravity != p.gravity) {
   1971             p.gravity = newGravity;
   1972             update = true;
   1973         }
   1974 
   1975         if (update) {
   1976             setLayoutDirectionFromAnchor();
   1977             mWindowManager.updateViewLayout(mDecorView, p);
   1978         }
   1979     }
   1980 
   1981     /**
   1982      * Updates the dimension of the popup window.
   1983      * <p>
   1984      * Calling this function also updates the window with the current popup
   1985      * state as described for {@link #update()}.
   1986      *
   1987      * @param width the new width in pixels, must be >= 0 or -1 to ignore
   1988      * @param height the new height in pixels, must be >= 0 or -1 to ignore
   1989      */
   1990     public void update(int width, int height) {
   1991         final WindowManager.LayoutParams p =
   1992                 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
   1993         update(p.x, p.y, width, height, false);
   1994     }
   1995 
   1996     /**
   1997      * Updates the position and the dimension of the popup window.
   1998      * <p>
   1999      * Width and height can be set to -1 to update location only. Calling this
   2000      * function also updates the window with the current popup state as
   2001      * described for {@link #update()}.
   2002      *
   2003      * @param x the new x location
   2004      * @param y the new y location
   2005      * @param width the new width in pixels, must be >= 0 or -1 to ignore
   2006      * @param height the new height in pixels, must be >= 0 or -1 to ignore
   2007      */
   2008     public void update(int x, int y, int width, int height) {
   2009         update(x, y, width, height, false);
   2010     }
   2011 
   2012     /**
   2013      * Updates the position and the dimension of the popup window.
   2014      * <p>
   2015      * Width and height can be set to -1 to update location only. Calling this
   2016      * function also updates the window with the current popup state as
   2017      * described for {@link #update()}.
   2018      *
   2019      * @param x the new x location
   2020      * @param y the new y location
   2021      * @param width the new width in pixels, must be >= 0 or -1 to ignore
   2022      * @param height the new height in pixels, must be >= 0 or -1 to ignore
   2023      * @param force {@code true} to reposition the window even if the specified
   2024      *              position already seems to correspond to the LayoutParams,
   2025      *              {@code false} to only reposition if needed
   2026      */
   2027     public void update(int x, int y, int width, int height, boolean force) {
   2028         if (width >= 0) {
   2029             mLastWidth = width;
   2030             setWidth(width);
   2031         }
   2032 
   2033         if (height >= 0) {
   2034             mLastHeight = height;
   2035             setHeight(height);
   2036         }
   2037 
   2038         if (!isShowing() || mContentView == null) {
   2039             return;
   2040         }
   2041 
   2042         final WindowManager.LayoutParams p =
   2043                 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
   2044 
   2045         boolean update = force;
   2046 
   2047         final int finalWidth = mWidthMode < 0 ? mWidthMode : mLastWidth;
   2048         if (width != -1 && p.width != finalWidth) {
   2049             p.width = mLastWidth = finalWidth;
   2050             update = true;
   2051         }
   2052 
   2053         final int finalHeight = mHeightMode < 0 ? mHeightMode : mLastHeight;
   2054         if (height != -1 && p.height != finalHeight) {
   2055             p.height = mLastHeight = finalHeight;
   2056             update = true;
   2057         }
   2058 
   2059         if (p.x != x) {
   2060             p.x = x;
   2061             update = true;
   2062         }
   2063 
   2064         if (p.y != y) {
   2065             p.y = y;
   2066             update = true;
   2067         }
   2068 
   2069         final int newAnim = computeAnimationResource();
   2070         if (newAnim != p.windowAnimations) {
   2071             p.windowAnimations = newAnim;
   2072             update = true;
   2073         }
   2074 
   2075         final int newFlags = computeFlags(p.flags);
   2076         if (newFlags != p.flags) {
   2077             p.flags = newFlags;
   2078             update = true;
   2079         }
   2080 
   2081         final int newGravity = computeGravity();
   2082         if (newGravity != p.gravity) {
   2083             p.gravity = newGravity;
   2084             update = true;
   2085         }
   2086 
   2087         int newAccessibilityIdOfAnchor =
   2088                 (mAnchor != null) ? mAnchor.get().getAccessibilityViewId() : -1;
   2089         if (newAccessibilityIdOfAnchor != p.accessibilityIdOfAnchor) {
   2090             p.accessibilityIdOfAnchor = newAccessibilityIdOfAnchor;
   2091             update = true;
   2092         }
   2093 
   2094         if (update) {
   2095             setLayoutDirectionFromAnchor();
   2096             mWindowManager.updateViewLayout(mDecorView, p);
   2097         }
   2098     }
   2099 
   2100     /**
   2101      * Updates the position and the dimension of the popup window.
   2102      * <p>
   2103      * Calling this function also updates the window with the current popup
   2104      * state as described for {@link #update()}.
   2105      *
   2106      * @param anchor the popup's anchor view
   2107      * @param width the new width in pixels, must be >= 0 or -1 to ignore
   2108      * @param height the new height in pixels, must be >= 0 or -1 to ignore
   2109      */
   2110     public void update(View anchor, int width, int height) {
   2111         update(anchor, false, 0, 0, width, height);
   2112     }
   2113 
   2114     /**
   2115      * Updates the position and the dimension of the popup window.
   2116      * <p>
   2117      * Width and height can be set to -1 to update location only. Calling this
   2118      * function also updates the window with the current popup state as
   2119      * described for {@link #update()}.
   2120      * <p>
   2121      * If the view later scrolls to move {@code anchor} to a different
   2122      * location, the popup will be moved correspondingly.
   2123      *
   2124      * @param anchor the popup's anchor view
   2125      * @param xoff x offset from the view's left edge
   2126      * @param yoff y offset from the view's bottom edge
   2127      * @param width the new width in pixels, must be >= 0 or -1 to ignore
   2128      * @param height the new height in pixels, must be >= 0 or -1 to ignore
   2129      */
   2130     public void update(View anchor, int xoff, int yoff, int width, int height) {
   2131         update(anchor, true, xoff, yoff, width, height);
   2132     }
   2133 
   2134     private void update(View anchor, boolean updateLocation, int xoff, int yoff,
   2135             int width, int height) {
   2136 
   2137         if (!isShowing() || mContentView == null) {
   2138             return;
   2139         }
   2140 
   2141         final WeakReference<View> oldAnchor = mAnchor;
   2142         final int gravity = mAnchoredGravity;
   2143 
   2144         final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff);
   2145         if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) {
   2146             attachToAnchor(anchor, xoff, yoff, gravity);
   2147         } else if (needsUpdate) {
   2148             // No need to register again if this is a DropDown, showAsDropDown already did.
   2149             mAnchorXoff = xoff;
   2150             mAnchorYoff = yoff;
   2151         }
   2152 
   2153         final LayoutParams p = (LayoutParams) mDecorView.getLayoutParams();
   2154         final int oldGravity = p.gravity;
   2155         final int oldWidth = p.width;
   2156         final int oldHeight = p.height;
   2157         final int oldX = p.x;
   2158         final int oldY = p.y;
   2159 
   2160         // If an explicit width/height has not specified, use the most recent
   2161         // explicitly specified value (either from setWidth/Height or update).
   2162         if (width < 0) {
   2163             width = mWidth;
   2164         }
   2165         if (height < 0) {
   2166             height = mHeight;
   2167         }
   2168 
   2169         final boolean aboveAnchor = findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
   2170                 width, height, gravity);
   2171         updateAboveAnchor(aboveAnchor);
   2172 
   2173         final boolean paramsChanged = oldGravity != p.gravity || oldX != p.x || oldY != p.y
   2174                 || oldWidth != p.width || oldHeight != p.height;
   2175         // If width and mWidth were both < 0 then we have a MATCH_PARENT/WRAP_CONTENT case.
   2176         // findDropDownPosition will have resolved this to absolute values,
   2177         // but we don't want to update mWidth/mHeight to these absolute values.
   2178         update(p.x, p.y, width < 0 ? width : p.width, height < 0 ? height : p.height, paramsChanged);
   2179     }
   2180 
   2181     /**
   2182      * Listener that is called when this popup window is dismissed.
   2183      */
   2184     public interface OnDismissListener {
   2185         /**
   2186          * Called when this popup window is dismissed.
   2187          */
   2188         public void onDismiss();
   2189     }
   2190 
   2191     private void detachFromAnchor() {
   2192         final View anchor = mAnchor != null ? mAnchor.get() : null;
   2193         if (anchor != null) {
   2194             final ViewTreeObserver vto = anchor.getViewTreeObserver();
   2195             vto.removeOnScrollChangedListener(mOnScrollChangedListener);
   2196         }
   2197 
   2198         final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
   2199         if (anchorRoot != null) {
   2200             anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
   2201         }
   2202 
   2203         mAnchor = null;
   2204         mAnchorRoot = null;
   2205         mIsAnchorRootAttached = false;
   2206     }
   2207 
   2208     private void attachToAnchor(View anchor, int xoff, int yoff, int gravity) {
   2209         detachFromAnchor();
   2210 
   2211         final ViewTreeObserver vto = anchor.getViewTreeObserver();
   2212         if (vto != null) {
   2213             vto.addOnScrollChangedListener(mOnScrollChangedListener);
   2214         }
   2215 
   2216         final View anchorRoot = anchor.getRootView();
   2217         anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
   2218 
   2219         mAnchor = new WeakReference<>(anchor);
   2220         mAnchorRoot = new WeakReference<>(anchorRoot);
   2221         mIsAnchorRootAttached = anchorRoot.isAttachedToWindow();
   2222 
   2223         mAnchorXoff = xoff;
   2224         mAnchorYoff = yoff;
   2225         mAnchoredGravity = gravity;
   2226     }
   2227 
   2228     private class PopupDecorView extends FrameLayout {
   2229         private TransitionListenerAdapter mPendingExitListener;
   2230 
   2231         public PopupDecorView(Context context) {
   2232             super(context);
   2233         }
   2234 
   2235         @Override
   2236         public boolean dispatchKeyEvent(KeyEvent event) {
   2237             if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
   2238                 if (getKeyDispatcherState() == null) {
   2239                     return super.dispatchKeyEvent(event);
   2240                 }
   2241 
   2242                 if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
   2243                     final KeyEvent.DispatcherState state = getKeyDispatcherState();
   2244                     if (state != null) {
   2245                         state.startTracking(event, this);
   2246                     }
   2247                     return true;
   2248                 } else if (event.getAction() == KeyEvent.ACTION_UP) {
   2249                     final KeyEvent.DispatcherState state = getKeyDispatcherState();
   2250                     if (state != null && state.isTracking(event) && !event.isCanceled()) {
   2251                         dismiss();
   2252                         return true;
   2253                     }
   2254                 }
   2255                 return super.dispatchKeyEvent(event);
   2256             } else {
   2257                 return super.dispatchKeyEvent(event);
   2258             }
   2259         }
   2260 
   2261         @Override
   2262         public boolean dispatchTouchEvent(MotionEvent ev) {
   2263             if (mTouchInterceptor != null && mTouchInterceptor.onTouch(this, ev)) {
   2264                 return true;
   2265             }
   2266             return super.dispatchTouchEvent(ev);
   2267         }
   2268 
   2269         @Override
   2270         public boolean onTouchEvent(MotionEvent event) {
   2271             final int x = (int) event.getX();
   2272             final int y = (int) event.getY();
   2273 
   2274             if ((event.getAction() == MotionEvent.ACTION_DOWN)
   2275                     && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
   2276                 dismiss();
   2277                 return true;
   2278             } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
   2279                 dismiss();
   2280                 return true;
   2281             } else {
   2282                 return super.onTouchEvent(event);
   2283             }
   2284         }
   2285 
   2286         /**
   2287          * Requests that an enter transition run after the next layout pass.
   2288          */
   2289         public void requestEnterTransition(Transition transition) {
   2290             final ViewTreeObserver observer = getViewTreeObserver();
   2291             if (observer != null && transition != null) {
   2292                 final Transition enterTransition = transition.clone();
   2293 
   2294                 // Postpone the enter transition after the first layout pass.
   2295                 observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
   2296                     @Override
   2297                     public void onGlobalLayout() {
   2298                         final ViewTreeObserver observer = getViewTreeObserver();
   2299                         if (observer != null) {
   2300                             observer.removeOnGlobalLayoutListener(this);
   2301                         }
   2302 
   2303                         final Rect epicenter = getTransitionEpicenter();
   2304                         enterTransition.setEpicenterCallback(new EpicenterCallback() {
   2305                             @Override
   2306                             public Rect onGetEpicenter(Transition transition) {
   2307                                 return epicenter;
   2308                             }
   2309                         });
   2310                         startEnterTransition(enterTransition);
   2311                     }
   2312                 });
   2313             }
   2314         }
   2315 
   2316         /**
   2317          * Starts the pending enter transition, if one is set.
   2318          */
   2319         private void startEnterTransition(Transition enterTransition) {
   2320             final int count = getChildCount();
   2321             for (int i = 0; i < count; i++) {
   2322                 final View child = getChildAt(i);
   2323                 enterTransition.addTarget(child);
   2324                 child.setVisibility(View.INVISIBLE);
   2325             }
   2326 
   2327             TransitionManager.beginDelayedTransition(this, enterTransition);
   2328 
   2329             for (int i = 0; i < count; i++) {
   2330                 final View child = getChildAt(i);
   2331                 child.setVisibility(View.VISIBLE);
   2332             }
   2333         }
   2334 
   2335         /**
   2336          * Starts an exit transition immediately.
   2337          * <p>
   2338          * <strong>Note:</strong> The transition listener is guaranteed to have
   2339          * its {@code onTransitionEnd} method called even if the transition
   2340          * never starts; however, it may be called with a {@code null} argument.
   2341          */
   2342         public void startExitTransition(Transition transition, final View anchorRoot,
   2343                 final TransitionListener listener) {
   2344             if (transition == null) {
   2345                 return;
   2346             }
   2347 
   2348             // The anchor view's window may go away while we're executing our
   2349             // transition, in which case we need to end the transition
   2350             // immediately and execute the listener to remove the popup.
   2351             anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
   2352 
   2353             // The exit listener MUST be called for cleanup, even if the
   2354             // transition never starts or ends. Stash it for later.
   2355             mPendingExitListener = new TransitionListenerAdapter() {
   2356                 @Override
   2357                 public void onTransitionEnd(Transition transition) {
   2358                     anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
   2359                     listener.onTransitionEnd(transition);
   2360 
   2361                     // The listener was called. Our job here is done.
   2362                     mPendingExitListener = null;
   2363                 }
   2364             };
   2365 
   2366             final Transition exitTransition = transition.clone();
   2367             exitTransition.addListener(mPendingExitListener);
   2368 
   2369             final int count = getChildCount();
   2370             for (int i = 0; i < count; i++) {
   2371                 final View child = getChildAt(i);
   2372                 exitTransition.addTarget(child);
   2373             }
   2374 
   2375             TransitionManager.beginDelayedTransition(this, exitTransition);
   2376 
   2377             for (int i = 0; i < count; i++) {
   2378                 final View child = getChildAt(i);
   2379                 child.setVisibility(View.INVISIBLE);
   2380             }
   2381         }
   2382 
   2383         /**
   2384          * Cancels all pending or current transitions.
   2385          */
   2386         public void cancelTransitions() {
   2387             TransitionManager.endTransitions(this);
   2388 
   2389             if (mPendingExitListener != null) {
   2390                 mPendingExitListener.onTransitionEnd(null);
   2391             }
   2392         }
   2393 
   2394         private final OnAttachStateChangeListener mOnAnchorRootDetachedListener =
   2395                 new OnAttachStateChangeListener() {
   2396                     @Override
   2397                     public void onViewAttachedToWindow(View v) {}
   2398 
   2399                     @Override
   2400                     public void onViewDetachedFromWindow(View v) {
   2401                         v.removeOnAttachStateChangeListener(this);
   2402 
   2403                         TransitionManager.endTransitions(PopupDecorView.this);
   2404                     }
   2405                 };
   2406     }
   2407 
   2408     private class PopupBackgroundView extends FrameLayout {
   2409         public PopupBackgroundView(Context context) {
   2410             super(context);
   2411         }
   2412 
   2413         @Override
   2414         protected int[] onCreateDrawableState(int extraSpace) {
   2415             if (mAboveAnchor) {
   2416                 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
   2417                 View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET);
   2418                 return drawableState;
   2419             } else {
   2420                 return super.onCreateDrawableState(extraSpace);
   2421             }
   2422         }
   2423     }
   2424 }
   2425