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