blob: bf696f5a7c53732408e4cb958b66749c296a8ab6 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package android.widget;
18
Aurimas Liutikas99441c52016-10-11 16:48:32 -070019import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
20import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
Artur Satayeved5a6ae2019-12-10 17:47:54 +000021import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
Aurimas Liutikas99441c52016-10-11 16:48:32 -070022import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023
Alan Viveretteb854d072015-09-28 16:12:18 -040024import android.annotation.NonNull;
Alan Viverette1e940dc2016-03-18 09:55:10 -040025import android.annotation.Nullable;
Artur Satayeved5a6ae2019-12-10 17:47:54 +000026import android.compat.annotation.UnsupportedAppUsage;
svetoslavganov75986cf2009-05-14 22:28:01 -070027import android.content.Context;
28import android.content.res.TypedArray;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.graphics.PixelFormat;
30import android.graphics.Rect;
31import android.graphics.drawable.Drawable;
32import android.graphics.drawable.StateListDrawable;
Jeff Brown46e75292010-11-10 16:53:45 -080033import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.os.IBinder;
Alan Viverette5435a302015-01-29 10:25:34 -080035import android.transition.Transition;
36import android.transition.Transition.EpicenterCallback;
Alan Viverette8fd949e2015-03-11 12:21:30 -070037import android.transition.Transition.TransitionListener;
Alan Viverette5435a302015-01-29 10:25:34 -080038import android.transition.TransitionInflater;
Ben Weisse0c37bd2016-10-26 11:18:32 +010039import android.transition.TransitionListenerAdapter;
Alan Viverette5435a302015-01-29 10:25:34 -080040import android.transition.TransitionManager;
41import android.transition.TransitionSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.util.AttributeSet;
Adam Powellc3fa6302010-05-18 11:36:27 -070043import android.view.Gravity;
44import android.view.KeyEvent;
Peeyush Agarwal50db7312016-11-30 15:54:32 +000045import android.view.KeyboardShortcutGroup;
Adam Powellc3fa6302010-05-18 11:36:27 -070046import android.view.MotionEvent;
47import android.view.View;
Alan Viverette634a8082016-02-03 14:22:41 -050048import android.view.View.OnAttachStateChangeListener;
Adam Powella7287f42010-08-17 21:17:04 -070049import android.view.View.OnTouchListener;
Adam Powellc3fa6302010-05-18 11:36:27 -070050import android.view.ViewGroup;
Alan Viverette8fd949e2015-03-11 12:21:30 -070051import android.view.ViewParent;
Adam Powellc3fa6302010-05-18 11:36:27 -070052import android.view.ViewTreeObserver;
Alan Viverette8fd949e2015-03-11 12:21:30 -070053import android.view.ViewTreeObserver.OnGlobalLayoutListener;
Adam Powellc3fa6302010-05-18 11:36:27 -070054import android.view.ViewTreeObserver.OnScrollChangedListener;
Adam Powella7287f42010-08-17 21:17:04 -070055import android.view.WindowManager;
Alan Viverette259c2842015-03-22 17:39:39 -070056import android.view.WindowManager.LayoutParams;
Yohei Yukawa22dac1c2017-02-12 16:54:16 -080057import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -080058import android.view.WindowManagerGlobal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059
Aurimas Liutikas99441c52016-10-11 16:48:32 -070060import com.android.internal.R;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061
Aurimas Liutikas99441c52016-10-11 16:48:32 -070062import java.lang.ref.WeakReference;
Peeyush Agarwal50db7312016-11-30 15:54:32 +000063import java.util.List;
Alan Viverette1e940dc2016-03-18 09:55:10 -040064
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065/**
Alan Viverette1e940dc2016-03-18 09:55:10 -040066 * <p>
67 * This class represents a popup window that can be used to display an
68 * arbitrary view. The popup window is a floating container that appears on top
69 * of the current activity.
70 * </p>
71 * <a name="Animation"></a>
72 * <h3>Animation</h3>
73 * <p>
74 * On all versions of Android, popup window enter and exit animations may be
75 * specified by calling {@link #setAnimationStyle(int)} and passing the
76 * resource ID for an animation style that defines {@code windowEnterAnimation}
77 * and {@code windowExitAnimation}. For example, passing
78 * {@link android.R.style#Animation_Dialog} will give a scale and alpha
79 * animation.
80 * </br>
81 * A window animation style may also be specified in the popup window's style
82 * XML via the {@link android.R.styleable#PopupWindow_popupAnimationStyle popupAnimationStyle}
83 * attribute.
84 * </p>
85 * <p>
86 * Starting with API 23, more complex popup window enter and exit transitions
87 * may be specified by calling either {@link #setEnterTransition(Transition)}
88 * or {@link #setExitTransition(Transition)} and passing a {@link Transition}.
89 * </br>
90 * Popup enter and exit transitions may also be specified in the popup window's
91 * style XML via the {@link android.R.styleable#PopupWindow_popupEnterTransition popupEnterTransition}
92 * and {@link android.R.styleable#PopupWindow_popupExitTransition popupExitTransition}
93 * attributes, respectively.
94 * </p>
95 *
96 * @attr ref android.R.styleable#PopupWindow_overlapAnchor
97 * @attr ref android.R.styleable#PopupWindow_popupAnimationStyle
98 * @attr ref android.R.styleable#PopupWindow_popupBackground
99 * @attr ref android.R.styleable#PopupWindow_popupElevation
100 * @attr ref android.R.styleable#PopupWindow_popupEnterTransition
101 * @attr ref android.R.styleable#PopupWindow_popupExitTransition
Alan Viverette5435a302015-01-29 10:25:34 -0800102 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 * @see android.widget.AutoCompleteTextView
104 * @see android.widget.Spinner
105 */
106public class PopupWindow {
107 /**
Romain Guye29f0642009-06-23 21:27:02 -0700108 * Mode for {@link #setInputMethodMode(int)}: the requirements for the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 * input method should be based on the focusability of the popup. That is
110 * if it is focusable than it needs to work with the input method, else
111 * it doesn't.
112 */
113 public static final int INPUT_METHOD_FROM_FOCUSABLE = 0;
Alan Viverette5435a302015-01-29 10:25:34 -0800114
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 /**
Romain Guye29f0642009-06-23 21:27:02 -0700116 * Mode for {@link #setInputMethodMode(int)}: this popup always needs to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 * work with an input method, regardless of whether it is focusable. This
118 * means that it will always be displayed so that the user can also operate
119 * the input method while it is shown.
120 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 public static final int INPUT_METHOD_NEEDED = 1;
Alan Viverette5435a302015-01-29 10:25:34 -0800122
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 /**
Romain Guye29f0642009-06-23 21:27:02 -0700124 * Mode for {@link #setInputMethodMode(int)}: this popup never needs to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 * work with an input method, regardless of whether it is focusable. This
126 * means that it will always be displayed to use as much space on the
127 * screen as needed, regardless of whether this covers the input method.
128 */
129 public static final int INPUT_METHOD_NOT_NEEDED = 2;
Adam Powell54c94de2013-09-26 15:36:34 -0700130
131 private static final int DEFAULT_ANCHORED_GRAVITY = Gravity.TOP | Gravity.START;
132
Alan Viverette5435a302015-01-29 10:25:34 -0800133 /**
134 * Default animation style indicating that separate animations should be
135 * used for top/bottom anchoring states.
136 */
137 private static final int ANIMATION_STYLE_DEFAULT = -1;
138
Alan Viverettef50df432016-03-24 14:08:24 -0400139 private final int[] mTmpDrawingLocation = new int[2];
140 private final int[] mTmpScreenLocation = new int[2];
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -0800141 private final int[] mTmpAppLocation = new int[2];
Alan Viverette5435a302015-01-29 10:25:34 -0800142 private final Rect mTempRect = new Rect();
Alan Viverette5435a302015-01-29 10:25:34 -0800143
Mathew Inwood978c6e22018-08-21 15:58:55 +0100144 @UnsupportedAppUsage
Romain Guy448ecf52009-05-14 16:03:42 -0700145 private Context mContext;
Mathew Inwood978c6e22018-08-21 15:58:55 +0100146 @UnsupportedAppUsage
Romain Guy448ecf52009-05-14 16:03:42 -0700147 private WindowManager mWindowManager;
Alan Viverette5435a302015-01-29 10:25:34 -0800148
Peeyush Agarwal50db7312016-11-30 15:54:32 +0000149 /**
150 * Keeps track of popup's parent's decor view. This is needed to dispatch
151 * requestKeyboardShortcuts to the owning Activity.
152 */
153 private WeakReference<View> mParentRootView;
154
Mathew Inwood978c6e22018-08-21 15:58:55 +0100155 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156 private boolean mIsShowing;
Alan Viverette8fd949e2015-03-11 12:21:30 -0700157 private boolean mIsTransitioningToDismiss;
Mathew Inwood978c6e22018-08-21 15:58:55 +0100158 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800159 private boolean mIsDropdown;
160
Alan Viverette5435a302015-01-29 10:25:34 -0800161 /** View that handles event dispatch and content transitions. */
Mathew Inwood978c6e22018-08-21 15:58:55 +0100162 @UnsupportedAppUsage
Alan Viverette5435a302015-01-29 10:25:34 -0800163 private PopupDecorView mDecorView;
164
Alan Viverette697804e2015-08-06 12:36:47 -0400165 /** View that holds the background and may animate during a transition. */
Mathew Inwood978c6e22018-08-21 15:58:55 +0100166 @UnsupportedAppUsage
Alan Viverette697804e2015-08-06 12:36:47 -0400167 private View mBackgroundView;
168
169 /** The contents of the popup. May be identical to the background view. */
Mathew Inwood978c6e22018-08-21 15:58:55 +0100170 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 private View mContentView;
Alan Viverette5435a302015-01-29 10:25:34 -0800172
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 private boolean mFocusable;
174 private int mInputMethodMode = INPUT_METHOD_FROM_FOCUSABLE;
Yohei Yukawa22dac1c2017-02-12 16:54:16 -0800175 @SoftInputModeFlags
Dianne Hackborn7eab0942011-01-01 13:21:50 -0800176 private int mSoftInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177 private boolean mTouchable = true;
178 private boolean mOutsideTouchable = false;
179 private boolean mClippingEnabled = true;
Jeff Brown46e75292010-11-10 16:53:45 -0800180 private int mSplitTouchEnabled = -1;
Mathew Inwood978c6e22018-08-21 15:58:55 +0100181 @UnsupportedAppUsage
Adam Powellba0a2c32010-09-28 17:41:23 -0700182 private boolean mLayoutInScreen;
Adam Powell56c2d332010-11-05 20:03:03 -0700183 private boolean mClipToScreen;
Adam Powell348e69c2011-02-16 16:49:50 -0800184 private boolean mAllowScrollingAnchorParent = true;
Adam Powell0bd1d0a2011-07-22 19:35:06 -0700185 private boolean mLayoutInsetDecor = false;
Mathew Inwood978c6e22018-08-21 15:58:55 +0100186 @UnsupportedAppUsage
Adam Powelle0b6cd12011-09-28 22:06:11 -0700187 private boolean mNotTouchModal;
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700188 private boolean mAttachedInDecor = true;
189 private boolean mAttachedInDecorSet = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190
Mathew Inwood978c6e22018-08-21 15:58:55 +0100191 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 private OnTouchListener mTouchInterceptor;
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700193
Mathew Inwood978c6e22018-08-21 15:58:55 +0100194 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 private int mWidthMode;
Alan Viverette259c2842015-03-22 17:39:39 -0700196 private int mWidth = LayoutParams.WRAP_CONTENT;
Mathew Inwood978c6e22018-08-21 15:58:55 +0100197 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 private int mLastWidth;
Mathew Inwood978c6e22018-08-21 15:58:55 +0100199 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 private int mHeightMode;
Alan Viverette259c2842015-03-22 17:39:39 -0700201 private int mHeight = LayoutParams.WRAP_CONTENT;
Mathew Inwood978c6e22018-08-21 15:58:55 +0100202 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 private int mLastHeight;
204
Alan Viveretteccb11e12014-07-08 16:04:02 -0700205 private float mElevation;
206
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 private Drawable mBackground;
Mathew Inwood31755f92018-12-20 13:53:36 +0000208 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 private Drawable mAboveAnchorBackgroundDrawable;
Mathew Inwood31755f92018-12-20 13:53:36 +0000210 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 private Drawable mBelowAnchorBackgroundDrawable;
212
Alan Viverette5435a302015-01-29 10:25:34 -0800213 private Transition mEnterTransition;
214 private Transition mExitTransition;
Alan Viverette91098572016-01-19 14:07:31 -0500215 private Rect mEpicenterBounds;
Alan Viverette560f1702014-05-05 14:40:07 -0700216
Mathew Inwood978c6e22018-08-21 15:58:55 +0100217 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 private boolean mAboveAnchor;
Mathew Inwood978c6e22018-08-21 15:58:55 +0100219 @UnsupportedAppUsage
Adam Powell574b37e2010-10-07 11:15:19 -0700220 private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
Alan Viverette5435a302015-01-29 10:25:34 -0800221
Mathew Inwood978c6e22018-08-21 15:58:55 +0100222 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 private OnDismissListener mOnDismissListener;
224 private boolean mIgnoreCheekPress = false;
225
Mathew Inwood978c6e22018-08-21 15:58:55 +0100226 @UnsupportedAppUsage
Alan Viverette5435a302015-01-29 10:25:34 -0800227 private int mAnimationStyle = ANIMATION_STYLE_DEFAULT;
228
Robert Carr08516062016-08-23 10:17:54 -0700229 private int mGravity = Gravity.NO_GRAVITY;
230
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 private static final int[] ABOVE_ANCHOR_STATE_SET = new int[] {
Felipe Leme4753bb02017-03-22 20:24:00 -0700232 com.android.internal.R.attr.state_above_anchor
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 };
234
Vladislav Kaznacheevafaa9322017-02-09 11:43:28 -0800235 private final OnAttachStateChangeListener mOnAnchorDetachedListener =
236 new OnAttachStateChangeListener() {
237 @Override
238 public void onViewAttachedToWindow(View v) {
239 // Anchor might have been reattached in a different position.
240 alignToAnchor();
241 }
242
243 @Override
244 public void onViewDetachedFromWindow(View v) {
245 // Leave the popup in its current position.
246 // The anchor might become attached again.
247 }
248 };
249
Alan Viverette634a8082016-02-03 14:22:41 -0500250 private final OnAttachStateChangeListener mOnAnchorRootDetachedListener =
251 new OnAttachStateChangeListener() {
252 @Override
253 public void onViewAttachedToWindow(View v) {}
254
255 @Override
256 public void onViewDetachedFromWindow(View v) {
257 mIsAnchorRootAttached = false;
258 }
259 };
260
Mathew Inwood978c6e22018-08-21 15:58:55 +0100261 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 private WeakReference<View> mAnchor;
Alan Viverette634a8082016-02-03 14:22:41 -0500263 private WeakReference<View> mAnchorRoot;
264 private boolean mIsAnchorRootAttached;
Alan Viverette560f1702014-05-05 14:40:07 -0700265
Matvei Malkov30853002019-02-07 15:22:10 +0000266 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Vladislav Kaznacheevafaa9322017-02-09 11:43:28 -0800267 private final OnScrollChangedListener mOnScrollChangedListener = this::alignToAnchor;
Alan Viverette560f1702014-05-05 14:40:07 -0700268
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -0800269 private final View.OnLayoutChangeListener mOnLayoutChangeListener =
270 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> alignToAnchor();
271
Alan Viverette5435a302015-01-29 10:25:34 -0800272 private int mAnchorXoff;
273 private int mAnchorYoff;
274 private int mAnchoredGravity;
Matvei Malkov40001de2019-02-05 12:47:30 +0000275 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Alan Viverette560f1702014-05-05 14:40:07 -0700276 private boolean mOverlapAnchor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277
Fabrice Di Megliob003e282012-10-17 17:20:19 -0700278 private boolean mPopupViewInitialLayoutDirectionInherited;
279
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 /**
281 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
282 *
283 * <p>The popup does provide a background.</p>
284 */
285 public PopupWindow(Context context) {
286 this(context, null);
287 }
288
289 /**
290 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
291 *
292 * <p>The popup does provide a background.</p>
293 */
294 public PopupWindow(Context context, AttributeSet attrs) {
295 this(context, attrs, com.android.internal.R.attr.popupWindowStyle);
296 }
297
298 /**
299 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
300 *
301 * <p>The popup does provide a background.</p>
302 */
Alan Viverette617feb92013-09-09 18:09:13 -0700303 public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
304 this(context, attrs, defStyleAttr, 0);
Adam Powellc3fa6302010-05-18 11:36:27 -0700305 }
Alan Viverette5435a302015-01-29 10:25:34 -0800306
Adam Powellc3fa6302010-05-18 11:36:27 -0700307 /**
308 * <p>Create a new, empty, non focusable popup window of dimension (0,0).</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800309 *
Adam Powellc3fa6302010-05-18 11:36:27 -0700310 * <p>The popup does not provide a background.</p>
311 */
312 public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 mContext = context;
Alan Viverette75d83792015-01-07 15:51:54 -0800314 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315
Alan Viverette617feb92013-09-09 18:09:13 -0700316 final TypedArray a = context.obtainStyledAttributes(
Alan Viverettece8c3582014-11-07 13:19:38 -0800317 attrs, R.styleable.PopupWindow, defStyleAttr, defStyleRes);
318 final Drawable bg = a.getDrawable(R.styleable.PopupWindow_popupBackground);
Alan Viveretteccb11e12014-07-08 16:04:02 -0700319 mElevation = a.getDimension(R.styleable.PopupWindow_popupElevation, 0);
Alan Viverette560f1702014-05-05 14:40:07 -0700320 mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false);
Adam Powellc3808b52010-10-04 10:06:59 -0700321
Alan Viverette5435a302015-01-29 10:25:34 -0800322 // Preserve default behavior from Gingerbread. If the animation is
323 // undefined or explicitly specifies the Gingerbread animation style,
324 // use a sentinel value.
325 if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupAnimationStyle)) {
326 final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, 0);
327 if (animStyle == R.style.Animation_PopupWindow) {
328 mAnimationStyle = ANIMATION_STYLE_DEFAULT;
329 } else {
330 mAnimationStyle = animStyle;
331 }
332 } else {
333 mAnimationStyle = ANIMATION_STYLE_DEFAULT;
334 }
335
336 final Transition enterTransition = getTransition(a.getResourceId(
337 R.styleable.PopupWindow_popupEnterTransition, 0));
338 final Transition exitTransition;
339 if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupExitTransition)) {
340 exitTransition = getTransition(a.getResourceId(
341 R.styleable.PopupWindow_popupExitTransition, 0));
342 } else {
343 exitTransition = enterTransition == null ? null : enterTransition.clone();
344 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 a.recycle();
Alan Viverettece8c3582014-11-07 13:19:38 -0800347
Alan Viverette5435a302015-01-29 10:25:34 -0800348 setEnterTransition(enterTransition);
349 setExitTransition(exitTransition);
Alan Viverettece8c3582014-11-07 13:19:38 -0800350 setBackgroundDrawable(bg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 }
352
353 /**
354 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
355 *
356 * <p>The popup does not provide any background. This should be handled
357 * by the content view.</p>
358 */
359 public PopupWindow() {
360 this(null, 0, 0);
361 }
362
363 /**
364 * <p>Create a new non focusable popup window which can display the
365 * <tt>contentView</tt>. The dimension of the window are (0,0).</p>
366 *
367 * <p>The popup does not provide any background. This should be handled
368 * by the content view.</p>
369 *
370 * @param contentView the popup's content
371 */
372 public PopupWindow(View contentView) {
373 this(contentView, 0, 0);
374 }
375
376 /**
377 * <p>Create a new empty, non focusable popup window. The dimension of the
378 * window must be passed to this constructor.</p>
379 *
380 * <p>The popup does not provide any background. This should be handled
381 * by the content view.</p>
382 *
383 * @param width the popup's width
384 * @param height the popup's height
385 */
386 public PopupWindow(int width, int height) {
387 this(null, width, height);
388 }
389
390 /**
391 * <p>Create a new non focusable popup window which can display the
392 * <tt>contentView</tt>. The dimension of the window must be passed to
393 * this constructor.</p>
394 *
395 * <p>The popup does not provide any background. This should be handled
396 * by the content view.</p>
397 *
398 * @param contentView the popup's content
399 * @param width the popup's width
400 * @param height the popup's height
401 */
402 public PopupWindow(View contentView, int width, int height) {
403 this(contentView, width, height, false);
404 }
405
406 /**
407 * <p>Create a new popup window which can display the <tt>contentView</tt>.
408 * The dimension of the window must be passed to this constructor.</p>
409 *
410 * <p>The popup does not provide any background. This should be handled
411 * by the content view.</p>
412 *
413 * @param contentView the popup's content
414 * @param width the popup's width
415 * @param height the popup's height
416 * @param focusable true if the popup can be focused, false otherwise
417 */
Romain Guy448ecf52009-05-14 16:03:42 -0700418 public PopupWindow(View contentView, int width, int height, boolean focusable) {
419 if (contentView != null) {
420 mContext = contentView.getContext();
421 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
422 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700423
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 setContentView(contentView);
425 setWidth(width);
426 setHeight(height);
427 setFocusable(focusable);
428 }
429
Alan Viverette1e940dc2016-03-18 09:55:10 -0400430 /**
431 * Sets the enter transition to be used when the popup window is shown.
432 *
433 * @param enterTransition the enter transition, or {@code null} to clear
434 * @see #getEnterTransition()
435 * @attr ref android.R.styleable#PopupWindow_popupEnterTransition
436 */
437 public void setEnterTransition(@Nullable Transition enterTransition) {
Alan Viverette5435a302015-01-29 10:25:34 -0800438 mEnterTransition = enterTransition;
Alan Viverette5435a302015-01-29 10:25:34 -0800439 }
440
Alan Viverette1e940dc2016-03-18 09:55:10 -0400441 /**
442 * Returns the enter transition to be used when the popup window is shown.
443 *
444 * @return the enter transition, or {@code null} if not set
445 * @see #setEnterTransition(Transition)
446 * @attr ref android.R.styleable#PopupWindow_popupEnterTransition
447 */
448 @Nullable
449 public Transition getEnterTransition() {
450 return mEnterTransition;
451 }
452
453 /**
454 * Sets the exit transition to be used when the popup window is dismissed.
455 *
456 * @param exitTransition the exit transition, or {@code null} to clear
457 * @see #getExitTransition()
458 * @attr ref android.R.styleable#PopupWindow_popupExitTransition
459 */
460 public void setExitTransition(@Nullable Transition exitTransition) {
Alan Viverette5435a302015-01-29 10:25:34 -0800461 mExitTransition = exitTransition;
Alan Viverette5435a302015-01-29 10:25:34 -0800462 }
463
Alan Viverette91098572016-01-19 14:07:31 -0500464 /**
Alan Viverette1e940dc2016-03-18 09:55:10 -0400465 * Returns the exit transition to be used when the popup window is
466 * dismissed.
467 *
468 * @return the exit transition, or {@code null} if not set
469 * @see #setExitTransition(Transition)
470 * @attr ref android.R.styleable#PopupWindow_popupExitTransition
471 */
472 @Nullable
473 public Transition getExitTransition() {
474 return mExitTransition;
475 }
476
477 /**
Matvei Malkov30853002019-02-07 15:22:10 +0000478 * <p>Returns bounds which are used as a center of the enter and exit transitions.<p/>
479 *
480 * <p>Transitions use Rect, referred to as the epicenter, to orient
Alan Viverette91098572016-01-19 14:07:31 -0500481 * the direction of travel. For popup windows, the anchor view bounds are
Matvei Malkov30853002019-02-07 15:22:10 +0000482 * used as the default epicenter.</p>
483 *
484 * <p>See {@link Transition#setEpicenterCallback(EpicenterCallback)} for more
485 * information about how transition epicenters work.</p>
486 *
487 * @return bounds relative to anchor view, or {@code null} if not set
488 * @see #setEpicenterBounds(Rect)
489 */
490 @Nullable
491 public Rect getEpicenterBounds() {
Matvei Malkovb0bd3e72019-02-08 16:16:44 +0000492 return mEpicenterBounds != null ? new Rect(mEpicenterBounds) : null;
Matvei Malkov30853002019-02-07 15:22:10 +0000493 }
494
495 /**
496 * <p>Sets the bounds used as the epicenter of the enter and exit transitions.</p>
497 *
498 * <p>Transitions use Rect, referred to as the epicenter, to orient
499 * the direction of travel. For popup windows, the anchor view bounds are
500 * used as the default epicenter.</p>
501 *
502 * <p>See {@link Transition#setEpicenterCallback(EpicenterCallback)} for more
503 * information about how transition epicenters work.</p>
Alan Viverette91098572016-01-19 14:07:31 -0500504 *
505 * @param bounds the epicenter bounds relative to the anchor view, or
506 * {@code null} to use the default epicenter
Matvei Malkov30853002019-02-07 15:22:10 +0000507 *
508 * @see #getEpicenterBounds()
Alan Viverette91098572016-01-19 14:07:31 -0500509 */
Matvei Malkov30853002019-02-07 15:22:10 +0000510 public void setEpicenterBounds(@Nullable Rect bounds) {
Matvei Malkovb0bd3e72019-02-08 16:16:44 +0000511 mEpicenterBounds = bounds != null ? new Rect(bounds) : null;
Alan Viverette91098572016-01-19 14:07:31 -0500512 }
513
Alan Viverette5435a302015-01-29 10:25:34 -0800514 private Transition getTransition(int resId) {
515 if (resId != 0 && resId != R.transition.no_transition) {
516 final TransitionInflater inflater = TransitionInflater.from(mContext);
517 final Transition transition = inflater.inflateTransition(resId);
518 if (transition != null) {
519 final boolean isEmpty = transition instanceof TransitionSet
520 && ((TransitionSet) transition).getTransitionCount() == 0;
521 if (!isEmpty) {
522 return transition;
523 }
524 }
525 }
526 return null;
527 }
528
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700530 * Return the drawable used as the popup window's background.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800531 *
Alan Viveretteccb11e12014-07-08 16:04:02 -0700532 * @return the background drawable or {@code null} if not set
533 * @see #setBackgroundDrawable(Drawable)
534 * @attr ref android.R.styleable#PopupWindow_popupBackground
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535 */
536 public Drawable getBackground() {
537 return mBackground;
538 }
539
540 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700541 * Specifies the background drawable for this popup window. The background
542 * can be set to {@code null}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 *
544 * @param background the popup's background
Alan Viveretteccb11e12014-07-08 16:04:02 -0700545 * @see #getBackground()
546 * @attr ref android.R.styleable#PopupWindow_popupBackground
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 */
548 public void setBackgroundDrawable(Drawable background) {
549 mBackground = background;
Alan Viverettece8c3582014-11-07 13:19:38 -0800550
551 // If this is a StateListDrawable, try to find and store the drawable to be
552 // used when the drop-down is placed above its anchor view, and the one to be
553 // used when the drop-down is placed below its anchor view. We extract
554 // the drawables ourselves to work around a problem with using refreshDrawableState
555 // that it will take into account the padding of all drawables specified in a
556 // StateListDrawable, thus adding superfluous padding to drop-down views.
557 //
558 // We assume a StateListDrawable will have a drawable for ABOVE_ANCHOR_STATE_SET and
559 // at least one other drawable, intended for the 'below-anchor state'.
560 if (mBackground instanceof StateListDrawable) {
561 StateListDrawable stateList = (StateListDrawable) mBackground;
562
563 // Find the above-anchor view - this one's easy, it should be labeled as such.
Nader Jawad071149a2018-09-04 13:45:06 -0700564 int aboveAnchorStateIndex = stateList.findStateDrawableIndex(ABOVE_ANCHOR_STATE_SET);
Alan Viverettece8c3582014-11-07 13:19:38 -0800565
566 // Now, for the below-anchor view, look for any other drawable specified in the
567 // StateListDrawable which is not for the above-anchor state and use that.
568 int count = stateList.getStateCount();
569 int belowAnchorStateIndex = -1;
570 for (int i = 0; i < count; i++) {
571 if (i != aboveAnchorStateIndex) {
572 belowAnchorStateIndex = i;
573 break;
574 }
575 }
576
577 // Store the drawables we found, if we found them. Otherwise, set them both
578 // to null so that we'll just use refreshDrawableState.
579 if (aboveAnchorStateIndex != -1 && belowAnchorStateIndex != -1) {
580 mAboveAnchorBackgroundDrawable = stateList.getStateDrawable(aboveAnchorStateIndex);
581 mBelowAnchorBackgroundDrawable = stateList.getStateDrawable(belowAnchorStateIndex);
582 } else {
583 mBelowAnchorBackgroundDrawable = null;
584 mAboveAnchorBackgroundDrawable = null;
585 }
586 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 }
588
589 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700590 * @return the elevation for this popup window in pixels
591 * @see #setElevation(float)
592 * @attr ref android.R.styleable#PopupWindow_popupElevation
593 */
594 public float getElevation() {
595 return mElevation;
596 }
597
598 /**
599 * Specifies the elevation for this popup window.
600 *
601 * @param elevation the popup's elevation in pixels
602 * @see #getElevation()
603 * @attr ref android.R.styleable#PopupWindow_popupElevation
604 */
605 public void setElevation(float elevation) {
606 mElevation = elevation;
607 }
608
609 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 * <p>Return the animation style to use the popup appears and disappears</p>
611 *
612 * @return the animation style to use the popup appears and disappears
613 */
614 public int getAnimationStyle() {
615 return mAnimationStyle;
616 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700617
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 /**
Shuhrat Dehkanov54ec76d2014-08-28 17:12:23 +0900619 * Set the flag on popup to ignore cheek press events; by default this flag
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 * is set to false
Shuhrat Dehkanov54ec76d2014-08-28 17:12:23 +0900621 * which means the popup will not ignore cheek press dispatch events.
Alan Viverette5435a302015-01-29 10:25:34 -0800622 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800623 * <p>If the popup is showing, calling this method will take effect only
624 * the next time the popup is shown or through a manual call to one of
625 * the {@link #update()} methods.</p>
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700626 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800627 * @see #update()
628 */
629 public void setIgnoreCheekPress() {
630 mIgnoreCheekPress = true;
631 }
Alan Viverette5435a302015-01-29 10:25:34 -0800632
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800633 /**
634 * <p>Change the animation style resource for this popup.</p>
635 *
636 * <p>If the popup is showing, calling this method will take effect only
637 * the next time the popup is shown or through a manual call to one of
638 * the {@link #update()} methods.</p>
639 *
640 * @param animationStyle animation style to use when the popup appears
641 * and disappears. Set to -1 for the default animation, 0 for no
642 * animation, or a resource identifier for an explicit animation.
Alan Viverette5435a302015-01-29 10:25:34 -0800643 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 * @see #update()
645 */
646 public void setAnimationStyle(int animationStyle) {
647 mAnimationStyle = animationStyle;
648 }
Alan Viverette5435a302015-01-29 10:25:34 -0800649
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800650 /**
651 * <p>Return the view used as the content of the popup window.</p>
652 *
653 * @return a {@link android.view.View} representing the popup's content
654 *
655 * @see #setContentView(android.view.View)
656 */
657 public View getContentView() {
658 return mContentView;
659 }
660
661 /**
662 * <p>Change the popup's content. The content is represented by an instance
663 * of {@link android.view.View}.</p>
664 *
Gilles Debunne81f08082011-02-17 14:07:19 -0800665 * <p>This method has no effect if called when the popup is showing.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666 *
667 * @param contentView the new content for the popup
668 *
669 * @see #getContentView()
670 * @see #isShowing()
671 */
672 public void setContentView(View contentView) {
673 if (isShowing()) {
674 return;
675 }
676
677 mContentView = contentView;
Romain Guy448ecf52009-05-14 16:03:42 -0700678
Romain Guy0c0b7682011-05-16 11:54:09 -0700679 if (mContext == null && mContentView != null) {
Romain Guy448ecf52009-05-14 16:03:42 -0700680 mContext = mContentView.getContext();
681 }
682
Romain Guy0c0b7682011-05-16 11:54:09 -0700683 if (mWindowManager == null && mContentView != null) {
Romain Guy448ecf52009-05-14 16:03:42 -0700684 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
685 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700686
687 // Setting the default for attachedInDecor based on SDK version here
688 // instead of in the constructor since we might not have the context
689 // object in the constructor. We only want to set default here if the
690 // app hasn't already set the attachedInDecor.
691 if (mContext != null && !mAttachedInDecorSet) {
692 // Attach popup window in decor frame of parent window by default for
693 // {@link Build.VERSION_CODES.LOLLIPOP_MR1} or greater. Keep current
694 // behavior of not attaching to decor frame for older SDKs.
695 setAttachedInDecor(mContext.getApplicationInfo().targetSdkVersion
696 >= Build.VERSION_CODES.LOLLIPOP_MR1);
697 }
698
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 }
700
701 /**
702 * Set a callback for all touch events being dispatched to the popup
703 * window.
704 */
705 public void setTouchInterceptor(OnTouchListener l) {
706 mTouchInterceptor = l;
707 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700708
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800709 /**
710 * <p>Indicate whether the popup window can grab the focus.</p>
711 *
712 * @return true if the popup is focusable, false otherwise
713 *
714 * @see #setFocusable(boolean)
715 */
716 public boolean isFocusable() {
717 return mFocusable;
718 }
719
720 /**
721 * <p>Changes the focusability of the popup window. When focusable, the
722 * window will grab the focus from the current focused widget if the popup
723 * contains a focusable {@link android.view.View}. By default a popup
724 * window is not focusable.</p>
725 *
726 * <p>If the popup is showing, calling this method will take effect only
727 * the next time the popup is shown or through a manual call to one of
728 * the {@link #update()} methods.</p>
729 *
730 * @param focusable true if the popup should grab focus, false otherwise.
731 *
732 * @see #isFocusable()
Alan Viverette5435a302015-01-29 10:25:34 -0800733 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 * @see #update()
735 */
736 public void setFocusable(boolean focusable) {
737 mFocusable = focusable;
738 }
739
740 /**
741 * Return the current value in {@link #setInputMethodMode(int)}.
Alan Viverette5435a302015-01-29 10:25:34 -0800742 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800743 * @see #setInputMethodMode(int)
744 */
745 public int getInputMethodMode() {
746 return mInputMethodMode;
Alan Viverette5435a302015-01-29 10:25:34 -0800747
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800748 }
Alan Viverette5435a302015-01-29 10:25:34 -0800749
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750 /**
751 * Control how the popup operates with an input method: one of
752 * {@link #INPUT_METHOD_FROM_FOCUSABLE}, {@link #INPUT_METHOD_NEEDED},
753 * or {@link #INPUT_METHOD_NOT_NEEDED}.
Alan Viverette5435a302015-01-29 10:25:34 -0800754 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800755 * <p>If the popup is showing, calling this method will take effect only
756 * the next time the popup is shown or through a manual call to one of
757 * the {@link #update()} methods.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800758 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 * @see #getInputMethodMode()
760 * @see #update()
761 */
762 public void setInputMethodMode(int mode) {
763 mInputMethodMode = mode;
764 }
Romain Guy374aaaed32009-07-14 15:11:59 -0700765
766 /**
767 * Sets the operating mode for the soft input area.
768 *
769 * @param mode The desired mode, see
770 * {@link android.view.WindowManager.LayoutParams#softInputMode}
771 * for the full list
772 *
773 * @see android.view.WindowManager.LayoutParams#softInputMode
774 * @see #getSoftInputMode()
775 */
Yohei Yukawa22dac1c2017-02-12 16:54:16 -0800776 public void setSoftInputMode(@SoftInputModeFlags int mode) {
Romain Guy374aaaed32009-07-14 15:11:59 -0700777 mSoftInputMode = mode;
778 }
779
780 /**
781 * Returns the current value in {@link #setSoftInputMode(int)}.
782 *
783 * @see #setSoftInputMode(int)
784 * @see android.view.WindowManager.LayoutParams#softInputMode
785 */
Yohei Yukawa22dac1c2017-02-12 16:54:16 -0800786 @SoftInputModeFlags
Romain Guy374aaaed32009-07-14 15:11:59 -0700787 public int getSoftInputMode() {
788 return mSoftInputMode;
789 }
Alan Viverette5435a302015-01-29 10:25:34 -0800790
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791 /**
792 * <p>Indicates whether the popup window receives touch events.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800793 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800794 * @return true if the popup is touchable, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800795 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 * @see #setTouchable(boolean)
797 */
798 public boolean isTouchable() {
799 return mTouchable;
800 }
801
802 /**
803 * <p>Changes the touchability of the popup window. When touchable, the
804 * window will receive touch events, otherwise touch events will go to the
805 * window below it. By default the window is touchable.</p>
806 *
807 * <p>If the popup is showing, calling this method will take effect only
808 * the next time the popup is shown or through a manual call to one of
809 * the {@link #update()} methods.</p>
810 *
811 * @param touchable true if the popup should receive touch events, false otherwise
812 *
813 * @see #isTouchable()
Alan Viverette5435a302015-01-29 10:25:34 -0800814 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800815 * @see #update()
816 */
817 public void setTouchable(boolean touchable) {
818 mTouchable = touchable;
819 }
820
821 /**
822 * <p>Indicates whether the popup window will be informed of touch events
823 * outside of its window.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800824 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825 * @return true if the popup is outside touchable, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800826 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800827 * @see #setOutsideTouchable(boolean)
828 */
829 public boolean isOutsideTouchable() {
830 return mOutsideTouchable;
831 }
832
833 /**
834 * <p>Controls whether the pop-up will be informed of touch events outside
835 * of its window. This only makes sense for pop-ups that are touchable
836 * but not focusable, which means touches outside of the window will
837 * be delivered to the window behind. The default is false.</p>
838 *
839 * <p>If the popup is showing, calling this method will take effect only
840 * the next time the popup is shown or through a manual call to one of
841 * the {@link #update()} methods.</p>
842 *
843 * @param touchable true if the popup should receive outside
844 * touch events, false otherwise
845 *
846 * @see #isOutsideTouchable()
Alan Viverette5435a302015-01-29 10:25:34 -0800847 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800848 * @see #update()
849 */
850 public void setOutsideTouchable(boolean touchable) {
851 mOutsideTouchable = touchable;
852 }
853
854 /**
855 * <p>Indicates whether clipping of the popup window is enabled.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800856 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800857 * @return true if the clipping is enabled, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800858 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 * @see #setClippingEnabled(boolean)
860 */
861 public boolean isClippingEnabled() {
862 return mClippingEnabled;
863 }
864
865 /**
866 * <p>Allows the popup window to extend beyond the bounds of the screen. By default the
867 * window is clipped to the screen boundaries. Setting this to false will allow windows to be
868 * accurately positioned.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800869 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800870 * <p>If the popup is showing, calling this method will take effect only
871 * the next time the popup is shown or through a manual call to one of
872 * the {@link #update()} methods.</p>
873 *
874 * @param enabled false if the window should be allowed to extend outside of the screen
Alan Viverette5435a302015-01-29 10:25:34 -0800875 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800876 * @see #isClippingEnabled()
877 * @see #update()
878 */
879 public void setClippingEnabled(boolean enabled) {
880 mClippingEnabled = enabled;
881 }
882
883 /**
Matvei Malkov30853002019-02-07 15:22:10 +0000884 * <p>Indicates whether this popup will be clipped to the screen and not to the
885 * containing window<p/>
Adam Powell56c2d332010-11-05 20:03:03 -0700886 *
Matvei Malkov30853002019-02-07 15:22:10 +0000887 * @return true if popup will be clipped to the screen instead of the window, false otherwise
Matvei Malkov71772652019-03-14 15:41:52 +0000888 * @deprecated Use {@link #isClippedToScreen()} instead
Ian Pedowitzcece8d32019-05-12 19:44:49 +0000889 * @removed
Matvei Malkov71772652019-03-14 15:41:52 +0000890 */
891 @Deprecated
892 public boolean isClipToScreenEnabled() {
893 return mClipToScreen;
894 }
895
896 /**
897 * <p>Clip this popup window to the screen, but not to the containing window.</p>
898 *
899 * <p>If the popup is showing, calling this method will take effect only
900 * the next time the popup is shown or through a manual call to one of
901 * the {@link #update()} methods.</p>
902 *
903 * @deprecated Use {@link #setIsClippedToScreen(boolean)} instead
Ian Pedowitzcece8d32019-05-12 19:44:49 +0000904 * @removed
Matvei Malkov71772652019-03-14 15:41:52 +0000905 */
906 @Deprecated
907 public void setClipToScreenEnabled(boolean enabled) {
908 mClipToScreen = enabled;
909 }
910
911 /**
912 * <p>Indicates whether this popup will be clipped to the screen and not to the
913 * containing window<p/>
914 *
915 * @return true if popup will be clipped to the screen instead of the window, false otherwise
Matvei Malkov30853002019-02-07 15:22:10 +0000916 *
Matvei Malkov25c9ac32019-03-12 12:26:40 +0000917 * @see #setIsClippedToScreen(boolean)
Adam Powell56c2d332010-11-05 20:03:03 -0700918 */
Matvei Malkov25c9ac32019-03-12 12:26:40 +0000919 public boolean isClippedToScreen() {
Matvei Malkov30853002019-02-07 15:22:10 +0000920 return mClipToScreen;
921 }
922
923 /**
924 * <p>Clip this popup window to the screen, but not to the containing window.</p>
925 *
926 * <p>If the popup is showing, calling this method will take effect only
927 * the next time the popup is shown or through a manual call to one of
928 * the {@link #update()} methods.</p>
929 *
930 * @param enabled true to clip to the screen.
931 *
Matvei Malkov25c9ac32019-03-12 12:26:40 +0000932 * @see #isClippedToScreen()
Matvei Malkov30853002019-02-07 15:22:10 +0000933 */
Matvei Malkov25c9ac32019-03-12 12:26:40 +0000934 public void setIsClippedToScreen(boolean enabled) {
Adam Powell56c2d332010-11-05 20:03:03 -0700935 mClipToScreen = enabled;
Adam Powell56c2d332010-11-05 20:03:03 -0700936 }
Adam Powell348e69c2011-02-16 16:49:50 -0800937
938 /**
939 * Allow PopupWindow to scroll the anchor's parent to provide more room
940 * for the popup. Enabled by default.
941 *
942 * @param enabled True to scroll the anchor's parent when more room is desired by the popup.
943 */
Mathew Inwood978c6e22018-08-21 15:58:55 +0100944 @UnsupportedAppUsage
Adam Powell348e69c2011-02-16 16:49:50 -0800945 void setAllowScrollingAnchorParent(boolean enabled) {
946 mAllowScrollingAnchorParent = enabled;
947 }
Alan Viverette5435a302015-01-29 10:25:34 -0800948
Felipe Leme4753bb02017-03-22 20:24:00 -0700949 /** @hide */
950 protected final boolean getAllowScrollingAnchorParent() {
951 return mAllowScrollingAnchorParent;
952 }
953
Adam Powell56c2d332010-11-05 20:03:03 -0700954 /**
Jeff Brown01ce2e92010-09-26 22:20:12 -0700955 * <p>Indicates whether the popup window supports splitting touches.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800956 *
Jeff Brown01ce2e92010-09-26 22:20:12 -0700957 * @return true if the touch splitting is enabled, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800958 *
Jeff Brown01ce2e92010-09-26 22:20:12 -0700959 * @see #setSplitTouchEnabled(boolean)
Jeff Brown01ce2e92010-09-26 22:20:12 -0700960 */
961 public boolean isSplitTouchEnabled() {
Jeff Brown46e75292010-11-10 16:53:45 -0800962 if (mSplitTouchEnabled < 0 && mContext != null) {
963 return mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB;
964 }
965 return mSplitTouchEnabled == 1;
Jeff Brown01ce2e92010-09-26 22:20:12 -0700966 }
967
968 /**
969 * <p>Allows the popup window to split touches across other windows that also
Jeff Brown46e75292010-11-10 16:53:45 -0800970 * support split touch. When this flag is false, the first pointer
Jeff Brown01ce2e92010-09-26 22:20:12 -0700971 * that goes down determines the window to which all subsequent touches
Jeff Brown46e75292010-11-10 16:53:45 -0800972 * go until all pointers go up. When this flag is true, each pointer
Jeff Brown01ce2e92010-09-26 22:20:12 -0700973 * (not necessarily the first) that goes down determines the window
974 * to which all subsequent touches of that pointer will go until that
975 * pointer goes up thereby enabling touches with multiple pointers
976 * to be split across multiple windows.</p>
977 *
978 * @param enabled true if the split touches should be enabled, false otherwise
979 * @see #isSplitTouchEnabled()
Jeff Brown01ce2e92010-09-26 22:20:12 -0700980 */
981 public void setSplitTouchEnabled(boolean enabled) {
Jeff Brown46e75292010-11-10 16:53:45 -0800982 mSplitTouchEnabled = enabled ? 1 : 0;
Jeff Brown01ce2e92010-09-26 22:20:12 -0700983 }
984
985 /**
Adam Powellba0a2c32010-09-28 17:41:23 -0700986 * <p>Indicates whether the popup window will be forced into using absolute screen coordinates
987 * for positioning.</p>
988 *
989 * @return true if the window will always be positioned in screen coordinates.
Matvei Malkov30853002019-02-07 15:22:10 +0000990 *
Matvei Malkov71772652019-03-14 15:41:52 +0000991 * @deprecated Use {@link #isLaidOutInScreen()} instead
Ian Pedowitzcece8d32019-05-12 19:44:49 +0000992 * @removed
Matvei Malkov71772652019-03-14 15:41:52 +0000993 */
994 @Deprecated
995 public boolean isLayoutInScreenEnabled() {
996 return mLayoutInScreen;
997 }
998
999 /**
1000 * <p>Allows the popup window to force the flag
1001 * {@link WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN}, overriding default behavior.
1002 * This will cause the popup to be positioned in absolute screen coordinates.</p>
1003 *
1004 * @param enabled true if the popup should always be positioned in screen coordinates
1005 * @deprecated Use {@link #setIsLaidOutInScreen(boolean)} instead
Ian Pedowitzcece8d32019-05-12 19:44:49 +00001006 * @removed
Matvei Malkov71772652019-03-14 15:41:52 +00001007 */
1008 @Deprecated
1009 public void setLayoutInScreenEnabled(boolean enabled) {
1010 mLayoutInScreen = enabled;
1011 }
1012
1013 /**
1014 * <p>Indicates whether the popup window will be forced into using absolute screen coordinates
1015 * for positioning.</p>
1016 *
1017 * @return true if the window will always be positioned in screen coordinates.
1018 *
Matvei Malkov25c9ac32019-03-12 12:26:40 +00001019 * @see #setIsLaidOutInScreen(boolean)
Adam Powellba0a2c32010-09-28 17:41:23 -07001020 */
Matvei Malkov25c9ac32019-03-12 12:26:40 +00001021 public boolean isLaidOutInScreen() {
Adam Powellba0a2c32010-09-28 17:41:23 -07001022 return mLayoutInScreen;
1023 }
1024
1025 /**
1026 * <p>Allows the popup window to force the flag
1027 * {@link WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN}, overriding default behavior.
1028 * This will cause the popup to be positioned in absolute screen coordinates.</p>
1029 *
1030 * @param enabled true if the popup should always be positioned in screen coordinates
Matvei Malkov30853002019-02-07 15:22:10 +00001031 *
Matvei Malkov25c9ac32019-03-12 12:26:40 +00001032 * @see #isLaidOutInScreen()
Adam Powellba0a2c32010-09-28 17:41:23 -07001033 */
Matvei Malkov25c9ac32019-03-12 12:26:40 +00001034 public void setIsLaidOutInScreen(boolean enabled) {
Adam Powellba0a2c32010-09-28 17:41:23 -07001035 mLayoutInScreen = enabled;
1036 }
1037
1038 /**
Wale Ogunwale393b1c12014-10-18 16:22:01 -07001039 * <p>Indicates whether the popup window will be attached in the decor frame of its parent
1040 * window.
1041 *
1042 * @return true if the window will be attached to the decor frame of its parent window.
1043 *
1044 * @see #setAttachedInDecor(boolean)
1045 * @see WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR
1046 */
1047 public boolean isAttachedInDecor() {
1048 return mAttachedInDecor;
1049 }
1050
1051 /**
1052 * <p>This will attach the popup window to the decor frame of the parent window to avoid
1053 * overlaping with screen decorations like the navigation bar. Overrides the default behavior of
1054 * the flag {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR}.
1055 *
1056 * <p>By default the flag is set on SDK version {@link Build.VERSION_CODES#LOLLIPOP_MR1} or
1057 * greater and cleared on lesser SDK versions.
1058 *
1059 * @param enabled true if the popup should be attached to the decor frame of its parent window.
1060 *
1061 * @see WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR
1062 */
1063 public void setAttachedInDecor(boolean enabled) {
1064 mAttachedInDecor = enabled;
1065 mAttachedInDecorSet = true;
1066 }
1067
1068 /**
Adam Powell0bd1d0a2011-07-22 19:35:06 -07001069 * Allows the popup window to force the flag
1070 * {@link WindowManager.LayoutParams#FLAG_LAYOUT_INSET_DECOR}, overriding default behavior.
1071 * This will cause the popup to inset its content to account for system windows overlaying
1072 * the screen, such as the status bar.
1073 *
Matvei Malkov25c9ac32019-03-12 12:26:40 +00001074 * <p>This will often be combined with {@link #setIsLaidOutInScreen(boolean)}.
Adam Powell0bd1d0a2011-07-22 19:35:06 -07001075 *
1076 * @param enabled true if the popup's views should inset content to account for system windows,
1077 * the way that decor views behave for full-screen windows.
1078 * @hide
1079 */
Mathew Inwood978c6e22018-08-21 15:58:55 +01001080 @UnsupportedAppUsage
Adam Powell0bd1d0a2011-07-22 19:35:06 -07001081 public void setLayoutInsetDecor(boolean enabled) {
1082 mLayoutInsetDecor = enabled;
1083 }
1084
Felipe Leme4753bb02017-03-22 20:24:00 -07001085 /** @hide */
1086 protected final boolean isLayoutInsetDecor() {
1087 return mLayoutInsetDecor;
1088 }
1089
Adam Powell0bd1d0a2011-07-22 19:35:06 -07001090 /**
Alan Viverette80ebe0d2015-04-30 15:53:11 -07001091 * Set the layout type for this window.
1092 * <p>
1093 * See {@link WindowManager.LayoutParams#type} for possible values.
Adam Powell574b37e2010-10-07 11:15:19 -07001094 *
1095 * @param layoutType Layout type for this window.
Chris Banes36344a92015-04-14 10:43:16 +01001096 *
1097 * @see WindowManager.LayoutParams#type
Adam Powell574b37e2010-10-07 11:15:19 -07001098 */
1099 public void setWindowLayoutType(int layoutType) {
1100 mWindowLayoutType = layoutType;
1101 }
1102
1103 /**
Chris Banes36344a92015-04-14 10:43:16 +01001104 * Returns the layout type for this window.
1105 *
1106 * @see #setWindowLayoutType(int)
Adam Powell574b37e2010-10-07 11:15:19 -07001107 */
1108 public int getWindowLayoutType() {
1109 return mWindowLayoutType;
1110 }
1111
1112 /**
Matvei Malkov30853002019-02-07 15:22:10 +00001113 * <p>Indicates whether outside touches will be sent to this window
1114 * or other windows behind it<p/>
1115 *
1116 * @return true if touches will be sent to this window, false otherwise
1117 *
1118 * @see #setTouchModal(boolean)
Adam Powelle0b6cd12011-09-28 22:06:11 -07001119 */
Matvei Malkov30853002019-02-07 15:22:10 +00001120 public boolean isTouchModal() {
1121 return !mNotTouchModal;
1122 }
1123
1124 /**
1125 * <p>Set whether this window is touch modal or if outside touches will be sent to
1126 * other windows behind it.<p/>
1127 *
1128 * <p>If the popup is showing, calling this method will take effect only
1129 * the next time the popup is shown or through a manual call to one of
1130 * the {@link #update()} methods.</p>
1131 *
1132 * @param touchModal true to sent all outside touches to this window,
1133 * false to other windows behind it
1134 *
1135 * @see #isTouchModal()
1136 */
Adam Powelle0b6cd12011-09-28 22:06:11 -07001137 public void setTouchModal(boolean touchModal) {
1138 mNotTouchModal = !touchModal;
1139 }
1140
1141 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001142 * <p>Change the width and height measure specs that are given to the
1143 * window manager by the popup. By default these are 0, meaning that
1144 * the current width or height is requested as an explicit size from
1145 * the window manager. You can supply
Alan Viverette5435a302015-01-29 10:25:34 -08001146 * {@link ViewGroup.LayoutParams#WRAP_CONTENT} or
Romain Guy980a9382010-01-08 15:06:28 -08001147 * {@link ViewGroup.LayoutParams#MATCH_PARENT} to have that measure
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001148 * spec supplied instead, replacing the absolute width and height that
1149 * has been set in the popup.</p>
1150 *
1151 * <p>If the popup is showing, calling this method will take effect only
1152 * the next time the popup is shown.</p>
1153 *
1154 * @param widthSpec an explicit width measure spec mode, either
1155 * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
Romain Guy980a9382010-01-08 15:06:28 -08001156 * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001157 * width.
1158 * @param heightSpec an explicit height measure spec mode, either
1159 * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
Romain Guy980a9382010-01-08 15:06:28 -08001160 * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001161 * height.
Alan Viverette259c2842015-03-22 17:39:39 -07001162 *
1163 * @deprecated Use {@link #setWidth(int)} and {@link #setHeight(int)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001164 */
Alan Viverette259c2842015-03-22 17:39:39 -07001165 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001166 public void setWindowLayoutMode(int widthSpec, int heightSpec) {
1167 mWidthMode = widthSpec;
1168 mHeightMode = heightSpec;
1169 }
Alan Viverette5435a302015-01-29 10:25:34 -08001170
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001171 /**
Alan Viverettec129b582016-06-21 11:09:03 -04001172 * Returns the popup's requested height. May be a layout constant such as
1173 * {@link LayoutParams#WRAP_CONTENT} or {@link LayoutParams#MATCH_PARENT}.
1174 * <p>
1175 * The actual size of the popup may depend on other factors such as
1176 * clipping and window layout.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 *
Alan Viverettec129b582016-06-21 11:09:03 -04001178 * @return the popup height in pixels or a layout constant
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001179 * @see #setHeight(int)
1180 */
1181 public int getHeight() {
1182 return mHeight;
1183 }
1184
1185 /**
Alan Viverettec129b582016-06-21 11:09:03 -04001186 * Sets the popup's requested height. May be a layout constant such as
1187 * {@link LayoutParams#WRAP_CONTENT} or {@link LayoutParams#MATCH_PARENT}.
1188 * <p>
1189 * The actual size of the popup may depend on other factors such as
1190 * clipping and window layout.
Alan Viverette259c2842015-03-22 17:39:39 -07001191 * <p>
1192 * If the popup is showing, calling this method will take effect the next
1193 * time the popup is shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001194 *
Alan Viverettec129b582016-06-21 11:09:03 -04001195 * @param height the popup height in pixels or a layout constant
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001196 * @see #getHeight()
Alan Viverette5435a302015-01-29 10:25:34 -08001197 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001198 */
1199 public void setHeight(int height) {
1200 mHeight = height;
1201 }
1202
1203 /**
Alan Viverettec129b582016-06-21 11:09:03 -04001204 * Returns the popup's requested width. May be a layout constant such as
1205 * {@link LayoutParams#WRAP_CONTENT} or {@link LayoutParams#MATCH_PARENT}.
1206 * <p>
1207 * The actual size of the popup may depend on other factors such as
1208 * clipping and window layout.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001209 *
Alan Viverettec129b582016-06-21 11:09:03 -04001210 * @return the popup width in pixels or a layout constant
Alan Viverette5435a302015-01-29 10:25:34 -08001211 * @see #setWidth(int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001212 */
1213 public int getWidth() {
1214 return mWidth;
1215 }
1216
1217 /**
Alan Viverettec129b582016-06-21 11:09:03 -04001218 * Sets the popup's requested width. May be a layout constant such as
1219 * {@link LayoutParams#WRAP_CONTENT} or {@link LayoutParams#MATCH_PARENT}.
1220 * <p>
1221 * The actual size of the popup may depend on other factors such as
1222 * clipping and window layout.
Alan Viverette259c2842015-03-22 17:39:39 -07001223 * <p>
1224 * If the popup is showing, calling this method will take effect the next
1225 * time the popup is shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001226 *
Alan Viverettec129b582016-06-21 11:09:03 -04001227 * @param width the popup width in pixels or a layout constant
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001228 * @see #getWidth()
1229 * @see #isShowing()
1230 */
1231 public void setWidth(int width) {
1232 mWidth = width;
1233 }
1234
1235 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001236 * Sets whether the popup window should overlap its anchor view when
1237 * displayed as a drop-down.
1238 * <p>
1239 * If the popup is showing, calling this method will take effect only
1240 * the next time the popup is shown.
1241 *
1242 * @param overlapAnchor Whether the popup should overlap its anchor.
1243 *
1244 * @see #getOverlapAnchor()
1245 * @see #isShowing()
1246 */
1247 public void setOverlapAnchor(boolean overlapAnchor) {
1248 mOverlapAnchor = overlapAnchor;
1249 }
1250
1251 /**
1252 * Returns whether the popup window should overlap its anchor view when
1253 * displayed as a drop-down.
1254 *
1255 * @return Whether the popup should overlap its anchor.
1256 *
1257 * @see #setOverlapAnchor(boolean)
1258 */
1259 public boolean getOverlapAnchor() {
1260 return mOverlapAnchor;
1261 }
1262
1263 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001264 * <p>Indicate whether this popup window is showing on screen.</p>
1265 *
1266 * @return true if the popup is showing, false otherwise
1267 */
1268 public boolean isShowing() {
1269 return mIsShowing;
1270 }
1271
Felipe Leme4753bb02017-03-22 20:24:00 -07001272 /** @hide */
1273 protected final void setShowing(boolean isShowing) {
1274 mIsShowing = isShowing;
1275 }
1276
1277 /** @hide */
1278 protected final void setDropDown(boolean isDropDown) {
1279 mIsDropdown = isDropDown;
1280 }
1281
1282 /** @hide */
1283 protected final void setTransitioningToDismiss(boolean transitioningToDismiss) {
1284 mIsTransitioningToDismiss = transitioningToDismiss;
1285 }
1286
1287 /** @hide */
1288 protected final boolean isTransitioningToDismiss() {
1289 return mIsTransitioningToDismiss;
1290 }
1291
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292 /**
1293 * <p>
1294 * Display the content view in a popup window at the specified location. If the popup window
1295 * cannot fit on screen, it will be clipped. See {@link android.view.WindowManager.LayoutParams}
1296 * for more information on how gravity and the x and y parameters are related. Specifying
1297 * a gravity of {@link android.view.Gravity#NO_GRAVITY} is similar to specifying
1298 * <code>Gravity.LEFT | Gravity.TOP</code>.
1299 * </p>
Alan Viverette5435a302015-01-29 10:25:34 -08001300 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001301 * @param parent a parent view to get the {@link android.view.View#getWindowToken()} token from
1302 * @param gravity the gravity which controls the placement of the popup window
1303 * @param x the popup's x location offset
1304 * @param y the popup's y location offset
1305 */
1306 public void showAtLocation(View parent, int gravity, int x, int y) {
Peeyush Agarwal50db7312016-11-30 15:54:32 +00001307 mParentRootView = new WeakReference<>(parent.getRootView());
Adam Powell8ee6d7c2011-09-18 14:59:28 -07001308 showAtLocation(parent.getWindowToken(), gravity, x, y);
1309 }
1310
1311 /**
1312 * Display the content view in a popup window at the specified location.
1313 *
1314 * @param token Window token to use for creating the new window
1315 * @param gravity the gravity which controls the placement of the popup window
1316 * @param x the popup's x location offset
1317 * @param y the popup's y location offset
1318 *
1319 * @hide Internal use only. Applications should use
1320 * {@link #showAtLocation(View, int, int, int)} instead.
1321 */
Mathew Inwood978c6e22018-08-21 15:58:55 +01001322 @UnsupportedAppUsage
Adam Powell8ee6d7c2011-09-18 14:59:28 -07001323 public void showAtLocation(IBinder token, int gravity, int x, int y) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001324 if (isShowing() || mContentView == null) {
1325 return;
1326 }
1327
Alan Viverettee025ed22015-02-02 11:27:21 -08001328 TransitionManager.endTransitions(mDecorView);
1329
Alan Viverettef50df432016-03-24 14:08:24 -04001330 detachFromAnchor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001331
1332 mIsShowing = true;
1333 mIsDropdown = false;
Robert Carr08516062016-08-23 10:17:54 -07001334 mGravity = gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001335
Alan Viverettee025ed22015-02-02 11:27:21 -08001336 final WindowManager.LayoutParams p = createPopupLayoutParams(token);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001337 preparePopup(p);
Alan Viverettee025ed22015-02-02 11:27:21 -08001338
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001339 p.x = x;
1340 p.y = y;
Alan Viverettee025ed22015-02-02 11:27:21 -08001341
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001342 invokePopup(p);
1343 }
1344
1345 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001346 * Display the content view in a popup window anchored to the bottom-left
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001347 * corner of the anchor view. If there is not enough room on screen to show
1348 * the popup in its entirety, this method tries to find a parent scroll
Alan Viverette75d83792015-01-07 15:51:54 -08001349 * view to scroll. If no parent scroll view can be scrolled, the
1350 * bottom-left corner of the popup is pinned at the top left corner of the
1351 * anchor view.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001352 *
1353 * @param anchor the view on which to pin the popup window
1354 *
1355 * @see #dismiss()
1356 */
1357 public void showAsDropDown(View anchor) {
1358 showAsDropDown(anchor, 0, 0);
1359 }
1360
1361 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001362 * Display the content view in a popup window anchored to the bottom-left
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001363 * corner of the anchor view offset by the specified x and y coordinates.
Alan Viverette75d83792015-01-07 15:51:54 -08001364 * If there is not enough room on screen to show the popup in its entirety,
1365 * this method tries to find a parent scroll view to scroll. If no parent
1366 * scroll view can be scrolled, the bottom-left corner of the popup is
1367 * pinned at the top left corner of the anchor view.
1368 * <p>
1369 * If the view later scrolls to move <code>anchor</code> to a different
1370 * location, the popup will be moved correspondingly.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001371 *
1372 * @param anchor the view on which to pin the popup window
Adam Powell54c94de2013-09-26 15:36:34 -07001373 * @param xoff A horizontal offset from the anchor in pixels
1374 * @param yoff A vertical offset from the anchor in pixels
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001375 *
1376 * @see #dismiss()
1377 */
1378 public void showAsDropDown(View anchor, int xoff, int yoff) {
Adam Powell54c94de2013-09-26 15:36:34 -07001379 showAsDropDown(anchor, xoff, yoff, DEFAULT_ANCHORED_GRAVITY);
1380 }
1381
1382 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001383 * Displays the content view in a popup window anchored to the corner of
1384 * another view. The window is positioned according to the specified
1385 * gravity and offset by the specified x and y coordinates.
1386 * <p>
1387 * If there is not enough room on screen to show the popup in its entirety,
1388 * this method tries to find a parent scroll view to scroll. If no parent
1389 * view can be scrolled, the specified vertical gravity will be ignored and
1390 * the popup will anchor itself such that it is visible.
1391 * <p>
1392 * If the view later scrolls to move <code>anchor</code> to a different
1393 * location, the popup will be moved correspondingly.
Adam Powell54c94de2013-09-26 15:36:34 -07001394 *
1395 * @param anchor the view on which to pin the popup window
1396 * @param xoff A horizontal offset from the anchor in pixels
1397 * @param yoff A vertical offset from the anchor in pixels
1398 * @param gravity Alignment of the popup relative to the anchor
1399 *
1400 * @see #dismiss()
1401 */
1402 public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001403 if (isShowing() || !hasContentView()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001404 return;
1405 }
1406
Alan Viverettee025ed22015-02-02 11:27:21 -08001407 TransitionManager.endTransitions(mDecorView);
1408
Alan Viverettef50df432016-03-24 14:08:24 -04001409 attachToAnchor(anchor, xoff, yoff, gravity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001410
1411 mIsShowing = true;
1412 mIsDropdown = true;
1413
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -08001414 final WindowManager.LayoutParams p =
1415 createPopupLayoutParams(anchor.getApplicationWindowToken());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001416 preparePopup(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001417
Alan Viverettef50df432016-03-24 14:08:24 -04001418 final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff,
Alan Viverette50df07a2017-01-06 14:40:21 -05001419 p.width, p.height, gravity, mAllowScrollingAnchorParent);
Alan Viverettee025ed22015-02-02 11:27:21 -08001420 updateAboveAnchor(aboveAnchor);
Phil Weaver396d5492016-03-22 17:53:50 -07001421 p.accessibilityIdOfAnchor = (anchor != null) ? anchor.getAccessibilityViewId() : -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001422
1423 invokePopup(p);
1424 }
1425
Felipe Leme4753bb02017-03-22 20:24:00 -07001426 /** @hide */
Mathew Inwood978c6e22018-08-21 15:58:55 +01001427 @UnsupportedAppUsage
Felipe Leme4753bb02017-03-22 20:24:00 -07001428 protected final void updateAboveAnchor(boolean aboveAnchor) {
Romain Guy3e141682010-03-08 17:44:40 -08001429 if (aboveAnchor != mAboveAnchor) {
1430 mAboveAnchor = aboveAnchor;
1431
Alan Viverette697804e2015-08-06 12:36:47 -04001432 if (mBackground != null && mBackgroundView != null) {
1433 // If the background drawable provided was a StateListDrawable
1434 // with above-anchor and below-anchor states, use those.
1435 // Otherwise, rely on refreshDrawableState to do the job.
Romain Guy3e141682010-03-08 17:44:40 -08001436 if (mAboveAnchorBackgroundDrawable != null) {
1437 if (mAboveAnchor) {
Alan Viverette697804e2015-08-06 12:36:47 -04001438 mBackgroundView.setBackground(mAboveAnchorBackgroundDrawable);
Romain Guy3e141682010-03-08 17:44:40 -08001439 } else {
Alan Viverette697804e2015-08-06 12:36:47 -04001440 mBackgroundView.setBackground(mBelowAnchorBackgroundDrawable);
Romain Guy3e141682010-03-08 17:44:40 -08001441 }
1442 } else {
Alan Viverette697804e2015-08-06 12:36:47 -04001443 mBackgroundView.refreshDrawableState();
Romain Guy3e141682010-03-08 17:44:40 -08001444 }
1445 }
1446 }
1447 }
1448
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001449 /**
1450 * Indicates whether the popup is showing above (the y coordinate of the popup's bottom
1451 * is less than the y coordinate of the anchor) or below the anchor view (the y coordinate
1452 * of the popup is greater than y coordinate of the anchor's bottom).
1453 *
1454 * The value returned
1455 * by this method is meaningful only after {@link #showAsDropDown(android.view.View)}
1456 * or {@link #showAsDropDown(android.view.View, int, int)} was invoked.
1457 *
1458 * @return True if this popup is showing above the anchor view, false otherwise.
1459 */
1460 public boolean isAboveAnchor() {
1461 return mAboveAnchor;
1462 }
1463
1464 /**
Alan Viverettee025ed22015-02-02 11:27:21 -08001465 * Prepare the popup by embedding it into a new ViewGroup if the background
1466 * drawable is not null. If embedding is required, the layout parameters'
1467 * height is modified to take into account the background's padding.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001468 *
1469 * @param p the layout parameters of the popup's content view
1470 */
Mathew Inwood978c6e22018-08-21 15:58:55 +01001471 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001472 private void preparePopup(WindowManager.LayoutParams p) {
Romain Guy448ecf52009-05-14 16:03:42 -07001473 if (mContentView == null || mContext == null || mWindowManager == null) {
1474 throw new IllegalStateException("You must specify a valid content view by "
1475 + "calling setContentView() before attempting to show the popup.");
1476 }
1477
Phil Weaverdd697852017-06-16 15:50:16 -07001478 if (p.accessibilityTitle == null) {
1479 p.accessibilityTitle = mContext.getString(R.string.popup_window_default_title);
1480 }
1481
Alan Viverette8fd949e2015-03-11 12:21:30 -07001482 // The old decor view may be transitioning out. Make sure it finishes
1483 // and cleans up before we try to create another one.
1484 if (mDecorView != null) {
1485 mDecorView.cancelTransitions();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001486 }
Alan Viveretteccb11e12014-07-08 16:04:02 -07001487
Alan Viverette8fd949e2015-03-11 12:21:30 -07001488 // When a background is available, we embed the content view within
1489 // another view that owns the background drawable.
Alan Viverette8fd949e2015-03-11 12:21:30 -07001490 if (mBackground != null) {
Alan Viverette697804e2015-08-06 12:36:47 -04001491 mBackgroundView = createBackgroundView(mContentView);
1492 mBackgroundView.setBackground(mBackground);
Alan Viverette8fd949e2015-03-11 12:21:30 -07001493 } else {
Alan Viverette697804e2015-08-06 12:36:47 -04001494 mBackgroundView = mContentView;
Alan Viverette8fd949e2015-03-11 12:21:30 -07001495 }
1496
Alan Viverette697804e2015-08-06 12:36:47 -04001497 mDecorView = createDecorView(mBackgroundView);
Evan Roskya7f94072017-05-05 18:28:34 -07001498 mDecorView.setIsRootNamespace(true);
Alan Viverette5435a302015-01-29 10:25:34 -08001499
1500 // The background owner should be elevated so that it casts a shadow.
Alan Viverette697804e2015-08-06 12:36:47 -04001501 mBackgroundView.setElevation(mElevation);
Alan Viverette5435a302015-01-29 10:25:34 -08001502
1503 // We may wrap that in another view, so we'll need to manually specify
1504 // the surface insets.
Wale Ogunwale246c2092016-04-07 14:12:44 -07001505 p.setSurfaceInsets(mBackgroundView, true /*manual*/, true /*preservePrevious*/);
Alan Viverette5435a302015-01-29 10:25:34 -08001506
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001507 mPopupViewInitialLayoutDirectionInherited =
Alan Viverette5435a302015-01-29 10:25:34 -08001508 (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001509 }
1510
1511 /**
Alan Viverette5435a302015-01-29 10:25:34 -08001512 * Wraps a content view in a PopupViewContainer.
1513 *
1514 * @param contentView the content view to wrap
1515 * @return a PopupViewContainer that wraps the content view
1516 */
1517 private PopupBackgroundView createBackgroundView(View contentView) {
1518 final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
1519 final int height;
Robert Carr26656042016-04-19 19:10:44 -07001520 if (layoutParams != null && layoutParams.height == WRAP_CONTENT) {
1521 height = WRAP_CONTENT;
Alan Viverette5435a302015-01-29 10:25:34 -08001522 } else {
Robert Carr26656042016-04-19 19:10:44 -07001523 height = MATCH_PARENT;
Alan Viverette5435a302015-01-29 10:25:34 -08001524 }
1525
1526 final PopupBackgroundView backgroundView = new PopupBackgroundView(mContext);
1527 final PopupBackgroundView.LayoutParams listParams = new PopupBackgroundView.LayoutParams(
Robert Carr26656042016-04-19 19:10:44 -07001528 MATCH_PARENT, height);
Alan Viverette5435a302015-01-29 10:25:34 -08001529 backgroundView.addView(contentView, listParams);
1530
1531 return backgroundView;
1532 }
1533
1534 /**
1535 * Wraps a content view in a FrameLayout.
1536 *
1537 * @param contentView the content view to wrap
1538 * @return a FrameLayout that wraps the content view
1539 */
1540 private PopupDecorView createDecorView(View contentView) {
1541 final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
1542 final int height;
Robert Carr26656042016-04-19 19:10:44 -07001543 if (layoutParams != null && layoutParams.height == WRAP_CONTENT) {
1544 height = WRAP_CONTENT;
Alan Viverette5435a302015-01-29 10:25:34 -08001545 } else {
Robert Carr26656042016-04-19 19:10:44 -07001546 height = MATCH_PARENT;
Alan Viverette5435a302015-01-29 10:25:34 -08001547 }
1548
1549 final PopupDecorView decorView = new PopupDecorView(mContext);
Robert Carr26656042016-04-19 19:10:44 -07001550 decorView.addView(contentView, MATCH_PARENT, height);
Alan Viverette5435a302015-01-29 10:25:34 -08001551 decorView.setClipChildren(false);
1552 decorView.setClipToPadding(false);
1553
1554 return decorView;
1555 }
1556
1557 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001558 * <p>Invoke the popup window by adding the content view to the window
1559 * manager.</p>
1560 *
1561 * <p>The content view must be non-null when this method is invoked.</p>
1562 *
1563 * @param p the layout parameters of the popup's content view
1564 */
Matvei Malkov30853002019-02-07 15:22:10 +00001565 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001566 private void invokePopup(WindowManager.LayoutParams p) {
Romain Guy0c0b7682011-05-16 11:54:09 -07001567 if (mContext != null) {
1568 p.packageName = mContext.getPackageName();
1569 }
Alan Viverette5435a302015-01-29 10:25:34 -08001570
Alan Viverette8fd949e2015-03-11 12:21:30 -07001571 final PopupDecorView decorView = mDecorView;
1572 decorView.setFitsSystemWindows(mLayoutInsetDecor);
Alan Viverette8fd949e2015-03-11 12:21:30 -07001573
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001574 setLayoutDirectionFromAnchor();
Alan Viverette5435a302015-01-29 10:25:34 -08001575
Alan Viverette8fd949e2015-03-11 12:21:30 -07001576 mWindowManager.addView(decorView, p);
Alan Viverette95888c02015-04-16 13:27:50 -07001577
1578 if (mEnterTransition != null) {
1579 decorView.requestEnterTransition(mEnterTransition);
1580 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001581 }
1582
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001583 private void setLayoutDirectionFromAnchor() {
1584 if (mAnchor != null) {
1585 View anchor = mAnchor.get();
1586 if (anchor != null && mPopupViewInitialLayoutDirectionInherited) {
Alan Viverette5435a302015-01-29 10:25:34 -08001587 mDecorView.setLayoutDirection(anchor.getLayoutDirection());
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001588 }
1589 }
1590 }
1591
Robert Carr489c39d2016-06-24 13:58:16 -07001592 private int computeGravity() {
Robert Carr08516062016-08-23 10:17:54 -07001593 int gravity = mGravity == Gravity.NO_GRAVITY ? Gravity.START | Gravity.TOP : mGravity;
1594 if (mIsDropdown && (mClipToScreen || mClippingEnabled)) {
Alan Viverette9705fa062016-06-30 13:17:26 -04001595 gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
Robert Carr489c39d2016-06-24 13:58:16 -07001596 }
1597 return gravity;
1598 }
1599
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001600 /**
1601 * <p>Generate the layout parameters for the popup window.</p>
1602 *
1603 * @param token the window token used to bind the popup's window
1604 *
1605 * @return the layout parameters to pass to the window manager
Felipe Leme4753bb02017-03-22 20:24:00 -07001606 *
1607 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001608 */
Mathew Inwood978c6e22018-08-21 15:58:55 +01001609 @UnsupportedAppUsage
Felipe Leme4753bb02017-03-22 20:24:00 -07001610 protected final WindowManager.LayoutParams createPopupLayoutParams(IBinder token) {
Alan Viverettee025ed22015-02-02 11:27:21 -08001611 final WindowManager.LayoutParams p = new WindowManager.LayoutParams();
1612
1613 // These gravity settings put the view at the top left corner of the
1614 // screen. The view is then positioned to the appropriate location by
1615 // setting the x and y offsets to match the anchor's bottom-left
1616 // corner.
Robert Carr489c39d2016-06-24 13:58:16 -07001617 p.gravity = computeGravity();
Alan Viverettee025ed22015-02-02 11:27:21 -08001618 p.flags = computeFlags(p.flags);
1619 p.type = mWindowLayoutType;
1620 p.token = token;
1621 p.softInputMode = mSoftInputMode;
1622 p.windowAnimations = computeAnimationResource();
1623
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001624 if (mBackground != null) {
1625 p.format = mBackground.getOpacity();
1626 } else {
1627 p.format = PixelFormat.TRANSLUCENT;
1628 }
Alan Viverettee025ed22015-02-02 11:27:21 -08001629
1630 if (mHeightMode < 0) {
1631 p.height = mLastHeight = mHeightMode;
1632 } else {
1633 p.height = mLastHeight = mHeight;
1634 }
1635
1636 if (mWidthMode < 0) {
1637 p.width = mLastWidth = mWidthMode;
1638 } else {
1639 p.width = mLastWidth = mWidth;
1640 }
1641
Wale Ogunwale8216eb22015-12-18 10:42:42 -08001642 p.privateFlags = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH
1643 | PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
Robert Carra1eb4392015-12-10 12:43:51 -08001644
Alan Viverettee025ed22015-02-02 11:27:21 -08001645 // Used for debugging.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001646 p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
1647
1648 return p;
1649 }
1650
1651 private int computeFlags(int curFlags) {
1652 curFlags &= ~(
1653 WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES |
1654 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
1655 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
1656 WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
1657 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
Adam Powellba0a2c32010-09-28 17:41:23 -07001658 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
1659 WindowManager.LayoutParams.FLAG_SPLIT_TOUCH);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001660 if(mIgnoreCheekPress) {
1661 curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
1662 }
1663 if (!mFocusable) {
1664 curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1665 if (mInputMethodMode == INPUT_METHOD_NEEDED) {
1666 curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1667 }
1668 } else if (mInputMethodMode == INPUT_METHOD_NOT_NEEDED) {
1669 curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1670 }
1671 if (!mTouchable) {
1672 curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
1673 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001674 if (mOutsideTouchable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001675 curFlags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
1676 }
Robert Carrcb8dcec2016-06-13 19:12:13 -07001677 if (!mClippingEnabled || mClipToScreen) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001678 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
1679 }
Jeff Brown46e75292010-11-10 16:53:45 -08001680 if (isSplitTouchEnabled()) {
Jeff Brown01ce2e92010-09-26 22:20:12 -07001681 curFlags |= WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
1682 }
Adam Powellba0a2c32010-09-28 17:41:23 -07001683 if (mLayoutInScreen) {
1684 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
1685 }
Adam Powell0bd1d0a2011-07-22 19:35:06 -07001686 if (mLayoutInsetDecor) {
1687 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
1688 }
Adam Powelle0b6cd12011-09-28 22:06:11 -07001689 if (mNotTouchModal) {
1690 curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
1691 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -07001692 if (mAttachedInDecor) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001693 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR;
Wale Ogunwale393b1c12014-10-18 16:22:01 -07001694 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001695 return curFlags;
1696 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -07001697
Mathew Inwood978c6e22018-08-21 15:58:55 +01001698 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001699 private int computeAnimationResource() {
Alan Viverette5435a302015-01-29 10:25:34 -08001700 if (mAnimationStyle == ANIMATION_STYLE_DEFAULT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001701 if (mIsDropdown) {
1702 return mAboveAnchor
1703 ? com.android.internal.R.style.Animation_DropDownUp
1704 : com.android.internal.R.style.Animation_DropDownDown;
1705 }
1706 return 0;
1707 }
1708 return mAnimationStyle;
1709 }
Alan Viverette560f1702014-05-05 14:40:07 -07001710
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001711 /**
Alan Viverette560f1702014-05-05 14:40:07 -07001712 * Positions the popup window on screen. When the popup window is too tall
1713 * to fit under the anchor, a parent scroll view is seeked and scrolled up
1714 * to reclaim space. If scrolling is not possible or not enough, the popup
1715 * window gets moved on top of the anchor.
1716 * <p>
Alan Viverettef50df432016-03-24 14:08:24 -04001717 * The results of positioning are placed in {@code outParams}.
Alan Viverette5435a302015-01-29 10:25:34 -08001718 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001719 * @param anchor the view on which the popup window must be anchored
Alan Viverettef50df432016-03-24 14:08:24 -04001720 * @param outParams the layout parameters used to display the drop down
Alan Viverette9125fba2016-04-04 15:07:18 -04001721 * @param xOffset absolute horizontal offset from the left of the anchor
Alan Viverette6acf2f92016-03-24 17:11:37 -04001722 * @param yOffset absolute vertical offset from the top of the anchor
Alan Viverette560f1702014-05-05 14:40:07 -07001723 * @param gravity horizontal gravity specifying popup alignment
Alan Viverette50df07a2017-01-06 14:40:21 -05001724 * @param allowScroll whether the anchor view's parent may be scrolled
1725 * when the popup window doesn't fit on screen
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001726 * @return true if the popup is translated upwards to fit on screen
Felipe Leme4753bb02017-03-22 20:24:00 -07001727 *
1728 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001729 */
Dake Gu9f879a22018-01-25 18:08:08 -08001730 protected boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams,
Alan Viverette50df07a2017-01-06 14:40:21 -05001731 int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) {
Adam Powell62e2bde2011-08-15 15:50:05 -07001732 final int anchorHeight = anchor.getHeight();
Alan Viverette560f1702014-05-05 14:40:07 -07001733 final int anchorWidth = anchor.getWidth();
1734 if (mOverlapAnchor) {
Alan Viverettef50df432016-03-24 14:08:24 -04001735 yOffset -= anchorHeight;
Alan Viverette560f1702014-05-05 14:40:07 -07001736 }
1737
Alan Viverette6acf2f92016-03-24 17:11:37 -04001738 // Initially, align to the bottom-left corner of the anchor plus offsets.
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -08001739 final int[] appScreenLocation = mTmpAppLocation;
1740 final View appRootView = getAppRootView(anchor);
1741 appRootView.getLocationOnScreen(appScreenLocation);
1742
1743 final int[] screenLocation = mTmpScreenLocation;
1744 anchor.getLocationOnScreen(screenLocation);
1745
Alan Viverettef50df432016-03-24 14:08:24 -04001746 final int[] drawingLocation = mTmpDrawingLocation;
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -08001747 drawingLocation[0] = screenLocation[0] - appScreenLocation[0];
1748 drawingLocation[1] = screenLocation[1] - appScreenLocation[1];
Alan Viverettef50df432016-03-24 14:08:24 -04001749 outParams.x = drawingLocation[0] + xOffset;
1750 outParams.y = drawingLocation[1] + anchorHeight + yOffset;
Adam Powell54c94de2013-09-26 15:36:34 -07001751
Robert Carrf6e801d2016-04-12 14:33:18 -07001752 final Rect displayFrame = new Rect();
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -08001753 appRootView.getWindowVisibleDisplayFrame(displayFrame);
Robert Carr26656042016-04-19 19:10:44 -07001754 if (width == MATCH_PARENT) {
Robert Carrf6e801d2016-04-12 14:33:18 -07001755 width = displayFrame.right - displayFrame.left;
1756 }
Robert Carr26656042016-04-19 19:10:44 -07001757 if (height == MATCH_PARENT) {
Robert Carrf6e801d2016-04-12 14:33:18 -07001758 height = displayFrame.bottom - displayFrame.top;
1759 }
1760
Robert Carrcb8dcec2016-06-13 19:12:13 -07001761 // Let the window manager know to align the top to y.
Robert Carr8367c502016-07-12 15:48:53 -07001762 outParams.gravity = computeGravity();
Robert Carrcb8dcec2016-06-13 19:12:13 -07001763 outParams.width = width;
1764 outParams.height = height;
1765
Alan Viverette6acf2f92016-03-24 17:11:37 -04001766 // If we need to adjust for gravity RIGHT, align to the bottom-right
1767 // corner of the anchor (still accounting for offsets).
Alan Viverette560f1702014-05-05 14:40:07 -07001768 final int hgrav = Gravity.getAbsoluteGravity(gravity, anchor.getLayoutDirection())
1769 & Gravity.HORIZONTAL_GRAVITY_MASK;
Adam Powell54c94de2013-09-26 15:36:34 -07001770 if (hgrav == Gravity.RIGHT) {
Alan Viverettef50df432016-03-24 14:08:24 -04001771 outParams.x -= width - anchorWidth;
Adam Powell54c94de2013-09-26 15:36:34 -07001772 }
Alan Viverette560f1702014-05-05 14:40:07 -07001773
Alan Viverette9125fba2016-04-04 15:07:18 -04001774 // First, attempt to fit the popup vertically without resizing.
1775 final boolean fitsVertical = tryFitVertical(outParams, yOffset, height,
1776 anchorHeight, drawingLocation[1], screenLocation[1], displayFrame.top,
1777 displayFrame.bottom, false);
Alan Viverette6acf2f92016-03-24 17:11:37 -04001778
Alan Viverette9125fba2016-04-04 15:07:18 -04001779 // Next, attempt to fit the popup horizontally without resizing.
1780 final boolean fitsHorizontal = tryFitHorizontal(outParams, xOffset, width,
1781 anchorWidth, drawingLocation[0], screenLocation[0], displayFrame.left,
1782 displayFrame.right, false);
Romain Guy3e141682010-03-08 17:44:40 -08001783
Alan Viverette9125fba2016-04-04 15:07:18 -04001784 // If the popup still doesn't fit, attempt to scroll the parent.
1785 if (!fitsVertical || !fitsHorizontal) {
1786 final int scrollX = anchor.getScrollX();
1787 final int scrollY = anchor.getScrollY();
1788 final Rect r = new Rect(scrollX, scrollY, scrollX + width + xOffset,
1789 scrollY + height + anchorHeight + yOffset);
Alan Viverette50df07a2017-01-06 14:40:21 -05001790 if (allowScroll && anchor.requestRectangleOnScreen(r, true)) {
Alan Viverette9125fba2016-04-04 15:07:18 -04001791 // Reset for the new anchor position.
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -08001792 anchor.getLocationOnScreen(screenLocation);
1793 drawingLocation[0] = screenLocation[0] - appScreenLocation[0];
1794 drawingLocation[1] = screenLocation[1] - appScreenLocation[1];
Alan Viverette9125fba2016-04-04 15:07:18 -04001795 outParams.x = drawingLocation[0] + xOffset;
1796 outParams.y = drawingLocation[1] + anchorHeight + yOffset;
Adam Powell54c94de2013-09-26 15:36:34 -07001797
Alan Viverette9125fba2016-04-04 15:07:18 -04001798 // Preserve the gravity adjustment.
1799 if (hgrav == Gravity.RIGHT) {
1800 outParams.x -= width - anchorWidth;
Oren Blasberged391262015-09-01 12:12:51 -07001801 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001802 }
Alan Viverette9125fba2016-04-04 15:07:18 -04001803
1804 // Try to fit the popup again and allowing resizing.
1805 tryFitVertical(outParams, yOffset, height, anchorHeight, drawingLocation[1],
1806 screenLocation[1], displayFrame.top, displayFrame.bottom, mClipToScreen);
1807 tryFitHorizontal(outParams, xOffset, width, anchorWidth, drawingLocation[0],
1808 screenLocation[0], displayFrame.left, displayFrame.right, mClipToScreen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001809 }
1810
Alan Viverette9125fba2016-04-04 15:07:18 -04001811 // Return whether the popup's top edge is above the anchor's top edge.
1812 return outParams.y < drawingLocation[1];
1813 }
Alan Viverettef50df432016-03-24 14:08:24 -04001814
Alan Viverette9125fba2016-04-04 15:07:18 -04001815 private boolean tryFitVertical(@NonNull LayoutParams outParams, int yOffset, int height,
1816 int anchorHeight, int drawingLocationY, int screenLocationY, int displayFrameTop,
1817 int displayFrameBottom, boolean allowResize) {
Alan Viverette55f0a992016-04-19 10:17:00 -04001818 final int winOffsetY = screenLocationY - drawingLocationY;
Vladislav Kaznacheev3b105d92017-05-11 23:49:52 +00001819 final int anchorTopInScreen = outParams.y + winOffsetY;
1820 final int spaceBelow = displayFrameBottom - anchorTopInScreen;
1821 if (anchorTopInScreen >= 0 && height <= spaceBelow) {
Alan Viverette9125fba2016-04-04 15:07:18 -04001822 return true;
Adam Powell56c2d332010-11-05 20:03:03 -07001823 }
1824
Vladislav Kaznacheev3b105d92017-05-11 23:49:52 +00001825 final int spaceAbove = anchorTopInScreen - anchorHeight - displayFrameTop;
Alan Viverette9125fba2016-04-04 15:07:18 -04001826 if (height <= spaceAbove) {
1827 // Move everything up.
1828 if (mOverlapAnchor) {
Vladislav Kaznacheev3b105d92017-05-11 23:49:52 +00001829 yOffset += anchorHeight;
Alan Viverette9125fba2016-04-04 15:07:18 -04001830 }
1831 outParams.y = drawingLocationY - height + yOffset;
1832
1833 return true;
1834 }
1835
1836 if (positionInDisplayVertical(outParams, height, drawingLocationY, screenLocationY,
1837 displayFrameTop, displayFrameBottom, allowResize)) {
1838 return true;
1839 }
1840
1841 return false;
1842 }
1843
1844 private boolean positionInDisplayVertical(@NonNull LayoutParams outParams, int height,
1845 int drawingLocationY, int screenLocationY, int displayFrameTop, int displayFrameBottom,
1846 boolean canResize) {
1847 boolean fitsInDisplay = true;
1848
1849 final int winOffsetY = screenLocationY - drawingLocationY;
1850 outParams.y += winOffsetY;
Alan Viverettef50df432016-03-24 14:08:24 -04001851 outParams.height = height;
Alan Viverette560f1702014-05-05 14:40:07 -07001852
Alan Viverette9125fba2016-04-04 15:07:18 -04001853 final int bottom = outParams.y + height;
1854 if (bottom > displayFrameBottom) {
1855 // The popup is too far down, move it back in.
1856 outParams.y -= bottom - displayFrameBottom;
1857 }
1858
1859 if (outParams.y < displayFrameTop) {
1860 // The popup is too far up, move it back in and clip if
1861 // it's still too large.
1862 outParams.y = displayFrameTop;
1863
1864 final int displayFrameHeight = displayFrameBottom - displayFrameTop;
1865 if (canResize && height > displayFrameHeight) {
1866 outParams.height = displayFrameHeight;
1867 } else {
1868 fitsInDisplay = false;
1869 }
1870 }
1871
1872 outParams.y -= winOffsetY;
1873
1874 return fitsInDisplay;
1875 }
1876
1877 private boolean tryFitHorizontal(@NonNull LayoutParams outParams, int xOffset, int width,
1878 int anchorWidth, int drawingLocationX, int screenLocationX, int displayFrameLeft,
1879 int displayFrameRight, boolean allowResize) {
Alan Viverette55f0a992016-04-19 10:17:00 -04001880 final int winOffsetX = screenLocationX - drawingLocationX;
1881 final int anchorLeftInScreen = outParams.x + winOffsetX;
Alan Viverette9125fba2016-04-04 15:07:18 -04001882 final int spaceRight = displayFrameRight - anchorLeftInScreen;
Alan Viverette55f0a992016-04-19 10:17:00 -04001883 if (anchorLeftInScreen >= 0 && width <= spaceRight) {
Alan Viverette9125fba2016-04-04 15:07:18 -04001884 return true;
1885 }
1886
1887 if (positionInDisplayHorizontal(outParams, width, drawingLocationX, screenLocationX,
1888 displayFrameLeft, displayFrameRight, allowResize)) {
1889 return true;
1890 }
1891
1892 return false;
1893 }
1894
1895 private boolean positionInDisplayHorizontal(@NonNull LayoutParams outParams, int width,
1896 int drawingLocationX, int screenLocationX, int displayFrameLeft, int displayFrameRight,
1897 boolean canResize) {
1898 boolean fitsInDisplay = true;
1899
1900 // Use screen coordinates for comparison against display frame.
1901 final int winOffsetX = screenLocationX - drawingLocationX;
1902 outParams.x += winOffsetX;
1903
1904 final int right = outParams.x + width;
1905 if (right > displayFrameRight) {
1906 // The popup is too far right, move it back in.
1907 outParams.x -= right - displayFrameRight;
1908 }
1909
1910 if (outParams.x < displayFrameLeft) {
1911 // The popup is too far left, move it back in and clip if it's
1912 // still too large.
1913 outParams.x = displayFrameLeft;
1914
1915 final int displayFrameWidth = displayFrameRight - displayFrameLeft;
1916 if (canResize && width > displayFrameWidth) {
1917 outParams.width = displayFrameWidth;
1918 } else {
1919 fitsInDisplay = false;
1920 }
1921 }
1922
1923 outParams.x -= winOffsetX;
1924
1925 return fitsInDisplay;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001926 }
Alan Viverette5435a302015-01-29 10:25:34 -08001927
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001928 /**
1929 * Returns the maximum height that is available for the popup to be
1930 * completely shown. It is recommended that this height be the maximum for
1931 * the popup's height, otherwise it is possible that the popup will be
1932 * clipped.
Alan Viverette5435a302015-01-29 10:25:34 -08001933 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001934 * @param anchor The view on which the popup window must be anchored.
1935 * @return The maximum available height for the popup to be completely
1936 * shown.
1937 */
Alan Viveretteb854d072015-09-28 16:12:18 -04001938 public int getMaxAvailableHeight(@NonNull View anchor) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001939 return getMaxAvailableHeight(anchor, 0);
1940 }
1941
1942 /**
1943 * Returns the maximum height that is available for the popup to be
1944 * completely shown. It is recommended that this height be the maximum for
1945 * the popup's height, otherwise it is possible that the popup will be
1946 * clipped.
1947 *
1948 * @param anchor The view on which the popup window must be anchored.
1949 * @param yOffset y offset from the view's bottom edge
1950 * @return The maximum available height for the popup to be completely
1951 * shown.
1952 */
Alan Viveretteb854d072015-09-28 16:12:18 -04001953 public int getMaxAvailableHeight(@NonNull View anchor, int yOffset) {
Mike LeBeau98acd542009-05-07 19:04:39 -07001954 return getMaxAvailableHeight(anchor, yOffset, false);
1955 }
Alan Viverette5435a302015-01-29 10:25:34 -08001956
Mike LeBeau98acd542009-05-07 19:04:39 -07001957 /**
1958 * Returns the maximum height that is available for the popup to be
1959 * completely shown, optionally ignoring any bottom decorations such as
1960 * the input method. It is recommended that this height be the maximum for
1961 * the popup's height, otherwise it is possible that the popup will be
1962 * clipped.
Alan Viverette5435a302015-01-29 10:25:34 -08001963 *
Mike LeBeau98acd542009-05-07 19:04:39 -07001964 * @param anchor The view on which the popup window must be anchored.
1965 * @param yOffset y offset from the view's bottom edge
1966 * @param ignoreBottomDecorations if true, the height returned will be
1967 * all the way to the bottom of the display, ignoring any
1968 * bottom decorations
1969 * @return The maximum available height for the popup to be completely
1970 * shown.
Mike LeBeau98acd542009-05-07 19:04:39 -07001971 */
Alan Viveretteb854d072015-09-28 16:12:18 -04001972 public int getMaxAvailableHeight(
1973 @NonNull View anchor, int yOffset, boolean ignoreBottomDecorations) {
Robert Carr701d7302016-09-06 19:07:39 -07001974 Rect displayFrame = null;
1975 final Rect visibleDisplayFrame = new Rect();
1976
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -08001977 final View appView = getAppRootView(anchor);
1978 appView.getWindowVisibleDisplayFrame(visibleDisplayFrame);
Jorim Jaggi81758462016-02-29 14:41:09 +01001979 if (ignoreBottomDecorations) {
Robert Carr701d7302016-09-06 19:07:39 -07001980 // In the ignore bottom decorations case we want to
1981 // still respect all other decorations so we use the inset visible
1982 // frame on the top right and left and take the bottom
1983 // value from the full frame.
1984 displayFrame = new Rect();
Jorim Jaggi81758462016-02-29 14:41:09 +01001985 anchor.getWindowDisplayFrame(displayFrame);
Robert Carr701d7302016-09-06 19:07:39 -07001986 displayFrame.top = visibleDisplayFrame.top;
1987 displayFrame.right = visibleDisplayFrame.right;
1988 displayFrame.left = visibleDisplayFrame.left;
Jorim Jaggi81758462016-02-29 14:41:09 +01001989 } else {
Robert Carr701d7302016-09-06 19:07:39 -07001990 displayFrame = visibleDisplayFrame;
Jorim Jaggi81758462016-02-29 14:41:09 +01001991 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001992
Alan Viverettef50df432016-03-24 14:08:24 -04001993 final int[] anchorPos = mTmpDrawingLocation;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001994 anchor.getLocationOnScreen(anchorPos);
Alan Viverette5435a302015-01-29 10:25:34 -08001995
Jorim Jaggi81758462016-02-29 14:41:09 +01001996 final int bottomEdge = displayFrame.bottom;
Oren Blasberged391262015-09-01 12:12:51 -07001997
1998 final int distanceToBottom;
1999 if (mOverlapAnchor) {
2000 distanceToBottom = bottomEdge - anchorPos[1] - yOffset;
2001 } else {
2002 distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
2003 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002004 final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
2005
2006 // anchorPos[1] is distance from anchor to top of screen
2007 int returnedHeight = Math.max(distanceToBottom, distanceToTop);
2008 if (mBackground != null) {
2009 mBackground.getPadding(mTempRect);
Alan Viverette5435a302015-01-29 10:25:34 -08002010 returnedHeight -= mTempRect.top + mTempRect.bottom;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002011 }
Alan Viverette5435a302015-01-29 10:25:34 -08002012
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002013 return returnedHeight;
2014 }
Alan Viverette5435a302015-01-29 10:25:34 -08002015
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002016 /**
Alan Viverette7878edf2015-02-03 15:49:18 -08002017 * Disposes of the popup window. This method can be invoked only after
2018 * {@link #showAsDropDown(android.view.View)} has been executed. Failing
2019 * that, calling this method will have no effect.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002020 *
Alan Viverette5435a302015-01-29 10:25:34 -08002021 * @see #showAsDropDown(android.view.View)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002022 */
2023 public void dismiss() {
Felipe Leme4753bb02017-03-22 20:24:00 -07002024 if (!isShowing() || isTransitioningToDismiss()) {
Alan Viverettee025ed22015-02-02 11:27:21 -08002025 return;
2026 }
Svetoslav Ganov06f938e2011-11-10 14:31:37 -08002027
Alan Viverette8fd949e2015-03-11 12:21:30 -07002028 final PopupDecorView decorView = mDecorView;
2029 final View contentView = mContentView;
2030
2031 final ViewGroup contentHolder;
2032 final ViewParent contentParent = contentView.getParent();
2033 if (contentParent instanceof ViewGroup) {
2034 contentHolder = ((ViewGroup) contentParent);
2035 } else {
2036 contentHolder = null;
2037 }
2038
2039 // Ensure any ongoing or pending transitions are canceled.
2040 decorView.cancelTransitions();
2041
Alan Viverettee025ed22015-02-02 11:27:21 -08002042 mIsShowing = false;
Alan Viverette8fd949e2015-03-11 12:21:30 -07002043 mIsTransitioningToDismiss = true;
Craig Mautnerb82d0742012-05-23 08:48:39 -07002044
Alan Viverette634a8082016-02-03 14:22:41 -05002045 // This method may be called as part of window detachment, in which
2046 // case the anchor view (and its root) will still return true from
2047 // isAttachedToWindow() during execution of this method; however, we
2048 // can expect the OnAttachStateChangeListener to have been called prior
2049 // to executing this method, so we can rely on that instead.
Alan Viverette95888c02015-04-16 13:27:50 -07002050 final Transition exitTransition = mExitTransition;
Alan Viverette054c1722016-12-01 13:33:44 -05002051 if (exitTransition != null && decorView.isLaidOut()
2052 && (mIsAnchorRootAttached || mAnchorRoot == null)) {
Yohei Yukawadbd299d2016-03-06 22:27:40 -08002053 // The decor view is non-interactive and non-IME-focusable during exit transitions.
Alan Viverette95888c02015-04-16 13:27:50 -07002054 final LayoutParams p = (LayoutParams) decorView.getLayoutParams();
2055 p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
2056 p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
Yohei Yukawadbd299d2016-03-06 22:27:40 -08002057 p.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
Alan Viverette95888c02015-04-16 13:27:50 -07002058 mWindowManager.updateViewLayout(decorView, p);
2059
Alan Viverette054c1722016-12-01 13:33:44 -05002060 final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
2061 final Rect epicenter = getTransitionEpicenter();
2062
Alan Viverette634a8082016-02-03 14:22:41 -05002063 // Once we start dismissing the decor view, all state (including
2064 // the anchor root) needs to be moved to the decor view since we
2065 // may open another popup while it's busy exiting.
Alan Viverette054c1722016-12-01 13:33:44 -05002066 decorView.startExitTransition(exitTransition, anchorRoot, epicenter,
Alan Viverette634a8082016-02-03 14:22:41 -05002067 new TransitionListenerAdapter() {
2068 @Override
2069 public void onTransitionEnd(Transition transition) {
Alan Viverette79708942016-02-25 16:57:08 -05002070 dismissImmediate(decorView, contentHolder, contentView);
Alan Viverette634a8082016-02-03 14:22:41 -05002071 }
2072 });
Alan Viverettee025ed22015-02-02 11:27:21 -08002073 } else {
Alan Viverette79708942016-02-25 16:57:08 -05002074 dismissImmediate(decorView, contentHolder, contentView);
Alan Viverette7878edf2015-02-03 15:49:18 -08002075 }
2076
Alan Viverette95888c02015-04-16 13:27:50 -07002077 // Clears the anchor view.
Alan Viverettef50df432016-03-24 14:08:24 -04002078 detachFromAnchor();
Alan Viverette79708942016-02-25 16:57:08 -05002079
2080 if (mOnDismissListener != null) {
2081 mOnDismissListener.onDismiss();
2082 }
Alan Viverette5435a302015-01-29 10:25:34 -08002083 }
2084
Alan Viverette91098572016-01-19 14:07:31 -05002085 /**
2086 * Returns the window-relative epicenter bounds to be used by enter and
2087 * exit transitions.
2088 * <p>
2089 * <strong>Note:</strong> This is distinct from the rect passed to
2090 * {@link #setEpicenterBounds(Rect)}, which is anchor-relative.
2091 *
2092 * @return the window-relative epicenter bounds to be used by enter and
2093 * exit transitions
Felipe Leme4753bb02017-03-22 20:24:00 -07002094 *
2095 * @hide
Alan Viverette91098572016-01-19 14:07:31 -05002096 */
Felipe Leme4753bb02017-03-22 20:24:00 -07002097 protected final Rect getTransitionEpicenter() {
Alan Viverette95888c02015-04-16 13:27:50 -07002098 final View anchor = mAnchor != null ? mAnchor.get() : null;
2099 final View decor = mDecorView;
2100 if (anchor == null || decor == null) {
2101 return null;
2102 }
2103
2104 final int[] anchorLocation = anchor.getLocationOnScreen();
2105 final int[] popupLocation = mDecorView.getLocationOnScreen();
2106
2107 // Compute the position of the anchor relative to the popup.
2108 final Rect bounds = new Rect(0, 0, anchor.getWidth(), anchor.getHeight());
2109 bounds.offset(anchorLocation[0] - popupLocation[0], anchorLocation[1] - popupLocation[1]);
Alan Viverette91098572016-01-19 14:07:31 -05002110
2111 // Use anchor-relative epicenter, if specified.
2112 if (mEpicenterBounds != null) {
2113 final int offsetX = bounds.left;
2114 final int offsetY = bounds.top;
2115 bounds.set(mEpicenterBounds);
2116 bounds.offset(offsetX, offsetY);
2117 }
2118
Alan Viverette95888c02015-04-16 13:27:50 -07002119 return bounds;
2120 }
2121
Alan Viverette5435a302015-01-29 10:25:34 -08002122 /**
2123 * Removes the popup from the window manager and tears down the supporting
2124 * view hierarchy, if necessary.
2125 */
Alan Viverette79708942016-02-25 16:57:08 -05002126 private void dismissImmediate(View decorView, ViewGroup contentHolder, View contentView) {
Alan Viverette8fd949e2015-03-11 12:21:30 -07002127 // If this method gets called and the decor view doesn't have a parent,
2128 // then it was either never added or was already removed. That should
2129 // never happen, but it's worth checking to avoid potential crashes.
2130 if (decorView.getParent() != null) {
2131 mWindowManager.removeViewImmediate(decorView);
Alan Viverettedf4639a02015-03-02 12:40:34 -08002132 }
2133
Alan Viverette8fd949e2015-03-11 12:21:30 -07002134 if (contentHolder != null) {
2135 contentHolder.removeView(contentView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002136 }
Alan Viverette8fd949e2015-03-11 12:21:30 -07002137
2138 // This needs to stay until after all transitions have ended since we
2139 // need the reference to cancel transitions in preparePopup().
2140 mDecorView = null;
Alan Viverette697804e2015-08-06 12:36:47 -04002141 mBackgroundView = null;
Alan Viverette8fd949e2015-03-11 12:21:30 -07002142 mIsTransitioningToDismiss = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002143 }
2144
2145 /**
2146 * Sets the listener to be called when the window is dismissed.
Alan Viverette5435a302015-01-29 10:25:34 -08002147 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002148 * @param onDismissListener The listener.
2149 */
2150 public void setOnDismissListener(OnDismissListener onDismissListener) {
2151 mOnDismissListener = onDismissListener;
2152 }
Alan Viverette5435a302015-01-29 10:25:34 -08002153
Felipe Leme4753bb02017-03-22 20:24:00 -07002154 /** @hide */
2155 protected final OnDismissListener getOnDismissListener() {
2156 return mOnDismissListener;
2157 }
2158
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002159 /**
2160 * Updates the state of the popup window, if it is currently being displayed,
Alan Viverette259c2842015-03-22 17:39:39 -07002161 * from the currently set state.
2162 * <p>
2163 * This includes:
2164 * <ul>
2165 * <li>{@link #setClippingEnabled(boolean)}</li>
2166 * <li>{@link #setFocusable(boolean)}</li>
2167 * <li>{@link #setIgnoreCheekPress()}</li>
2168 * <li>{@link #setInputMethodMode(int)}</li>
2169 * <li>{@link #setTouchable(boolean)}</li>
2170 * <li>{@link #setAnimationStyle(int)}</li>
Matvei Malkov30853002019-02-07 15:22:10 +00002171 * <li>{@link #setTouchModal(boolean)} (boolean)}</li>
Matvei Malkov25c9ac32019-03-12 12:26:40 +00002172 * <li>{@link #setIsClippedToScreen(boolean)}</li>
Alan Viverette259c2842015-03-22 17:39:39 -07002173 * </ul>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002174 */
2175 public void update() {
Felipe Leme4753bb02017-03-22 20:24:00 -07002176 if (!isShowing() || !hasContentView()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002177 return;
2178 }
Alan Viverette5435a302015-01-29 10:25:34 -08002179
Felipe Leme4753bb02017-03-22 20:24:00 -07002180 final WindowManager.LayoutParams p = getDecorViewLayoutParams();
Alan Viverette5435a302015-01-29 10:25:34 -08002181
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002182 boolean update = false;
Alan Viverette5435a302015-01-29 10:25:34 -08002183
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002184 final int newAnim = computeAnimationResource();
2185 if (newAnim != p.windowAnimations) {
2186 p.windowAnimations = newAnim;
2187 update = true;
2188 }
2189
2190 final int newFlags = computeFlags(p.flags);
2191 if (newFlags != p.flags) {
2192 p.flags = newFlags;
2193 update = true;
2194 }
Fabrice Di Megliob003e282012-10-17 17:20:19 -07002195
Robert Carr489c39d2016-06-24 13:58:16 -07002196 final int newGravity = computeGravity();
2197 if (newGravity != p.gravity) {
2198 p.gravity = newGravity;
2199 update = true;
2200 }
2201
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002202 if (update) {
Felipe Leme6f797172017-03-27 15:05:35 -07002203 update(mAnchor != null ? mAnchor.get() : null, p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002204 }
2205 }
Romain Guyd6a463a2009-05-21 23:10:10 -07002206
Felipe Leme4753bb02017-03-22 20:24:00 -07002207 /** @hide */
2208 protected void update(View anchor, WindowManager.LayoutParams params) {
2209 setLayoutDirectionFromAnchor();
2210 mWindowManager.updateViewLayout(mDecorView, params);
2211 }
2212
Romain Guyd6a463a2009-05-21 23:10:10 -07002213 /**
Alan Viverette259c2842015-03-22 17:39:39 -07002214 * Updates the dimension of the popup window.
2215 * <p>
2216 * Calling this function also updates the window with the current popup
2217 * state as described for {@link #update()}.
Romain Guyd6a463a2009-05-21 23:10:10 -07002218 *
Alan Viverettec129b582016-06-21 11:09:03 -04002219 * @param width the new width in pixels, must be >= 0 or -1 to ignore
2220 * @param height the new height in pixels, must be >= 0 or -1 to ignore
Romain Guyd6a463a2009-05-21 23:10:10 -07002221 */
2222 public void update(int width, int height) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002223 final WindowManager.LayoutParams p = getDecorViewLayoutParams();
Romain Guyd6a463a2009-05-21 23:10:10 -07002224 update(p.x, p.y, width, height, false);
2225 }
Alan Viverette5435a302015-01-29 10:25:34 -08002226
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002227 /**
Alan Viverette259c2842015-03-22 17:39:39 -07002228 * Updates the position and the dimension of the popup window.
2229 * <p>
2230 * Width and height can be set to -1 to update location only. Calling this
2231 * function also updates the window with the current popup state as
2232 * described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002233 *
2234 * @param x the new x location
2235 * @param y the new y location
Alan Viverettec129b582016-06-21 11:09:03 -04002236 * @param width the new width in pixels, must be >= 0 or -1 to ignore
2237 * @param height the new height in pixels, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002238 */
2239 public void update(int x, int y, int width, int height) {
2240 update(x, y, width, height, false);
2241 }
2242
2243 /**
Alan Viverette259c2842015-03-22 17:39:39 -07002244 * Updates the position and the dimension of the popup window.
2245 * <p>
2246 * Width and height can be set to -1 to update location only. Calling this
2247 * function also updates the window with the current popup state as
2248 * described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002249 *
2250 * @param x the new x location
2251 * @param y the new y location
Alan Viverettec129b582016-06-21 11:09:03 -04002252 * @param width the new width in pixels, must be >= 0 or -1 to ignore
2253 * @param height the new height in pixels, must be >= 0 or -1 to ignore
Alan Viverette259c2842015-03-22 17:39:39 -07002254 * @param force {@code true} to reposition the window even if the specified
2255 * position already seems to correspond to the LayoutParams,
2256 * {@code false} to only reposition if needed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002257 */
2258 public void update(int x, int y, int width, int height, boolean force) {
Alan Viverette259c2842015-03-22 17:39:39 -07002259 if (width >= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002260 mLastWidth = width;
2261 setWidth(width);
2262 }
2263
Alan Viverette259c2842015-03-22 17:39:39 -07002264 if (height >= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002265 mLastHeight = height;
2266 setHeight(height);
2267 }
2268
Felipe Leme4753bb02017-03-22 20:24:00 -07002269 if (!isShowing() || !hasContentView()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002270 return;
2271 }
2272
Felipe Leme4753bb02017-03-22 20:24:00 -07002273 final WindowManager.LayoutParams p = getDecorViewLayoutParams();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002274
2275 boolean update = force;
2276
2277 final int finalWidth = mWidthMode < 0 ? mWidthMode : mLastWidth;
2278 if (width != -1 && p.width != finalWidth) {
2279 p.width = mLastWidth = finalWidth;
2280 update = true;
2281 }
2282
2283 final int finalHeight = mHeightMode < 0 ? mHeightMode : mLastHeight;
2284 if (height != -1 && p.height != finalHeight) {
2285 p.height = mLastHeight = finalHeight;
2286 update = true;
2287 }
2288
2289 if (p.x != x) {
2290 p.x = x;
2291 update = true;
2292 }
2293
2294 if (p.y != y) {
2295 p.y = y;
2296 update = true;
2297 }
2298
2299 final int newAnim = computeAnimationResource();
2300 if (newAnim != p.windowAnimations) {
2301 p.windowAnimations = newAnim;
2302 update = true;
2303 }
2304
2305 final int newFlags = computeFlags(p.flags);
2306 if (newFlags != p.flags) {
2307 p.flags = newFlags;
2308 update = true;
2309 }
Mike LeBeau98acd542009-05-07 19:04:39 -07002310
Robert Carr489c39d2016-06-24 13:58:16 -07002311 final int newGravity = computeGravity();
2312 if (newGravity != p.gravity) {
2313 p.gravity = newGravity;
2314 update = true;
2315 }
2316
Felipe Lemedd237772017-03-27 09:33:23 -07002317 View anchor = null;
2318 int newAccessibilityIdOfAnchor = -1;
2319
2320 if (mAnchor != null && mAnchor.get() != null) {
2321 anchor = mAnchor.get();
2322 newAccessibilityIdOfAnchor = anchor.getAccessibilityViewId();
2323 }
2324
Phil Weaver396d5492016-03-22 17:53:50 -07002325 if (newAccessibilityIdOfAnchor != p.accessibilityIdOfAnchor) {
2326 p.accessibilityIdOfAnchor = newAccessibilityIdOfAnchor;
2327 update = true;
2328 }
2329
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002330 if (update) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002331 update(anchor, p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002332 }
2333 }
2334
Felipe Leme4753bb02017-03-22 20:24:00 -07002335 /** @hide */
2336 protected boolean hasContentView() {
2337 return mContentView != null;
2338 }
2339
2340 /** @hide */
2341 protected boolean hasDecorView() {
2342 return mDecorView != null;
2343 }
2344
2345 /** @hide */
2346 protected WindowManager.LayoutParams getDecorViewLayoutParams() {
2347 return (WindowManager.LayoutParams) mDecorView.getLayoutParams();
2348 }
2349
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002350 /**
Alan Viverette259c2842015-03-22 17:39:39 -07002351 * Updates the position and the dimension of the popup window.
2352 * <p>
2353 * Calling this function also updates the window with the current popup
2354 * state as described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002355 *
2356 * @param anchor the popup's anchor view
Alan Viverettec129b582016-06-21 11:09:03 -04002357 * @param width the new width in pixels, must be >= 0 or -1 to ignore
2358 * @param height the new height in pixels, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002359 */
2360 public void update(View anchor, int width, int height) {
Alan Viveretteb91d6d02016-03-29 14:55:04 -04002361 update(anchor, false, 0, 0, width, height);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002362 }
2363
2364 /**
Alan Viverette259c2842015-03-22 17:39:39 -07002365 * Updates the position and the dimension of the popup window.
2366 * <p>
2367 * Width and height can be set to -1 to update location only. Calling this
2368 * function also updates the window with the current popup state as
2369 * described for {@link #update()}.
2370 * <p>
2371 * If the view later scrolls to move {@code anchor} to a different
2372 * location, the popup will be moved correspondingly.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002373 *
2374 * @param anchor the popup's anchor view
2375 * @param xoff x offset from the view's left edge
2376 * @param yoff y offset from the view's bottom edge
Alan Viverettec129b582016-06-21 11:09:03 -04002377 * @param width the new width in pixels, must be >= 0 or -1 to ignore
2378 * @param height the new height in pixels, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002379 */
2380 public void update(View anchor, int xoff, int yoff, int width, int height) {
Alan Viveretteb91d6d02016-03-29 14:55:04 -04002381 update(anchor, true, xoff, yoff, width, height);
The Android Open Source Project10592532009-03-18 17:39:46 -07002382 }
2383
2384 private void update(View anchor, boolean updateLocation, int xoff, int yoff,
Alan Viveretteb91d6d02016-03-29 14:55:04 -04002385 int width, int height) {
The Android Open Source Project10592532009-03-18 17:39:46 -07002386
Felipe Leme4753bb02017-03-22 20:24:00 -07002387 if (!isShowing() || !hasContentView()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002388 return;
2389 }
2390
Alan Viverette75d83792015-01-07 15:51:54 -08002391 final WeakReference<View> oldAnchor = mAnchor;
Alan Viverettef50df432016-03-24 14:08:24 -04002392 final int gravity = mAnchoredGravity;
2393
Alan Viverette75d83792015-01-07 15:51:54 -08002394 final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff);
Gilles Debunne81f08082011-02-17 14:07:19 -08002395 if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) {
Alan Viverettef50df432016-03-24 14:08:24 -04002396 attachToAnchor(anchor, xoff, yoff, gravity);
Gilles Debunne81f08082011-02-17 14:07:19 -08002397 } else if (needsUpdate) {
2398 // No need to register again if this is a DropDown, showAsDropDown already did.
2399 mAnchorXoff = xoff;
2400 mAnchorYoff = yoff;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002401 }
2402
Felipe Leme4753bb02017-03-22 20:24:00 -07002403 final WindowManager.LayoutParams p = getDecorViewLayoutParams();
Alan Viverettef50df432016-03-24 14:08:24 -04002404 final int oldGravity = p.gravity;
2405 final int oldWidth = p.width;
2406 final int oldHeight = p.height;
2407 final int oldX = p.x;
2408 final int oldY = p.y;
Alan Viverettef95b2d92016-03-22 11:23:05 -04002409
Alan Viveretteb91d6d02016-03-29 14:55:04 -04002410 // If an explicit width/height has not specified, use the most recent
2411 // explicitly specified value (either from setWidth/Height or update).
Robert Carrf6e801d2016-04-12 14:33:18 -07002412 if (width < 0) {
Alan Viveretteb91d6d02016-03-29 14:55:04 -04002413 width = mWidth;
2414 }
Robert Carrf6e801d2016-04-12 14:33:18 -07002415 if (height < 0) {
Alan Viveretteb91d6d02016-03-29 14:55:04 -04002416 height = mHeight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002417 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002418
Alan Viverettef50df432016-03-24 14:08:24 -04002419 final boolean aboveAnchor = findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
Alan Viverette50df07a2017-01-06 14:40:21 -05002420 width, height, gravity, mAllowScrollingAnchorParent);
Alan Viverettef50df432016-03-24 14:08:24 -04002421 updateAboveAnchor(aboveAnchor);
Fabrice Di Megliob003e282012-10-17 17:20:19 -07002422
Alan Viverettef50df432016-03-24 14:08:24 -04002423 final boolean paramsChanged = oldGravity != p.gravity || oldX != p.x || oldY != p.y
2424 || oldWidth != p.width || oldHeight != p.height;
Alan Viverette50df07a2017-01-06 14:40:21 -05002425
2426 // If width and mWidth were both < 0 then we have a MATCH_PARENT or
2427 // WRAP_CONTENT case. findDropDownPosition will have resolved this to
2428 // absolute values, but we don't want to update mWidth/mHeight to these
2429 // absolute values.
2430 final int newWidth = width < 0 ? width : p.width;
2431 final int newHeight = height < 0 ? height : p.height;
2432 update(p.x, p.y, newWidth, newHeight, paramsChanged);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002433 }
2434
2435 /**
2436 * Listener that is called when this popup window is dismissed.
2437 */
2438 public interface OnDismissListener {
2439 /**
2440 * Called when this popup window is dismissed.
2441 */
2442 public void onDismiss();
2443 }
2444
Felipe Leme4753bb02017-03-22 20:24:00 -07002445 /** @hide */
Svet Ganov77150c52017-09-22 17:19:04 -07002446 protected void detachFromAnchor() {
2447 final View anchor = getAnchor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002448 if (anchor != null) {
Alan Viverettee025ed22015-02-02 11:27:21 -08002449 final ViewTreeObserver vto = anchor.getViewTreeObserver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002450 vto.removeOnScrollChangedListener(mOnScrollChangedListener);
Vladislav Kaznacheevafaa9322017-02-09 11:43:28 -08002451 anchor.removeOnAttachStateChangeListener(mOnAnchorDetachedListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002452 }
Alan Viverettee025ed22015-02-02 11:27:21 -08002453
Alan Viverette634a8082016-02-03 14:22:41 -05002454 final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
2455 if (anchorRoot != null) {
2456 anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -08002457 anchorRoot.removeOnLayoutChangeListener(mOnLayoutChangeListener);
Alan Viverette634a8082016-02-03 14:22:41 -05002458 }
2459
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002460 mAnchor = null;
Alan Viverette634a8082016-02-03 14:22:41 -05002461 mAnchorRoot = null;
2462 mIsAnchorRootAttached = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002463 }
2464
Felipe Leme4753bb02017-03-22 20:24:00 -07002465 /** @hide */
Svet Ganov77150c52017-09-22 17:19:04 -07002466 protected void attachToAnchor(View anchor, int xoff, int yoff, int gravity) {
Alan Viverettef50df432016-03-24 14:08:24 -04002467 detachFromAnchor();
Alan Viverettee025ed22015-02-02 11:27:21 -08002468
2469 final ViewTreeObserver vto = anchor.getViewTreeObserver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002470 if (vto != null) {
2471 vto.addOnScrollChangedListener(mOnScrollChangedListener);
2472 }
Vladislav Kaznacheevafaa9322017-02-09 11:43:28 -08002473 anchor.addOnAttachStateChangeListener(mOnAnchorDetachedListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002474
Alan Viverette634a8082016-02-03 14:22:41 -05002475 final View anchorRoot = anchor.getRootView();
2476 anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -08002477 anchorRoot.addOnLayoutChangeListener(mOnLayoutChangeListener);
Alan Viverette634a8082016-02-03 14:22:41 -05002478
2479 mAnchor = new WeakReference<>(anchor);
2480 mAnchorRoot = new WeakReference<>(anchorRoot);
2481 mIsAnchorRootAttached = anchorRoot.isAttachedToWindow();
Peeyush Agarwal50db7312016-11-30 15:54:32 +00002482 mParentRootView = mAnchorRoot;
Alan Viverette634a8082016-02-03 14:22:41 -05002483
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002484 mAnchorXoff = xoff;
2485 mAnchorYoff = yoff;
Adam Powell54c94de2013-09-26 15:36:34 -07002486 mAnchoredGravity = gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002487 }
2488
Svet Ganov77150c52017-09-22 17:19:04 -07002489 /** @hide */
2490 protected @Nullable View getAnchor() {
2491 return mAnchor != null ? mAnchor.get() : null;
2492 }
2493
Vladislav Kaznacheevafaa9322017-02-09 11:43:28 -08002494 private void alignToAnchor() {
2495 final View anchor = mAnchor != null ? mAnchor.get() : null;
Felipe Leme4753bb02017-03-22 20:24:00 -07002496 if (anchor != null && anchor.isAttachedToWindow() && hasDecorView()) {
2497 final WindowManager.LayoutParams p = getDecorViewLayoutParams();
Vladislav Kaznacheevafaa9322017-02-09 11:43:28 -08002498
2499 updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
2500 p.width, p.height, mAnchoredGravity, false));
2501 update(p.x, p.y, -1, -1, true);
2502 }
2503 }
2504
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -08002505 private View getAppRootView(View anchor) {
2506 final View appWindowView = WindowManagerGlobal.getInstance().getWindowView(
2507 anchor.getApplicationWindowToken());
2508 if (appWindowView != null) {
2509 return appWindowView;
2510 }
2511 return anchor.getRootView();
2512 }
2513
Alan Viverette5435a302015-01-29 10:25:34 -08002514 private class PopupDecorView extends FrameLayout {
Alan Viverette7e1aeb72017-03-22 11:06:59 -04002515 /** Runnable used to clean up listeners after exit transition. */
2516 private Runnable mCleanupAfterExit;
Alan Viverette8fd949e2015-03-11 12:21:30 -07002517
Alan Viverette5435a302015-01-29 10:25:34 -08002518 public PopupDecorView(Context context) {
2519 super(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002520 }
2521
2522 @Override
2523 public boolean dispatchKeyEvent(KeyEvent event) {
2524 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
Per Andersson4ae02b32011-01-17 11:16:23 +01002525 if (getKeyDispatcherState() == null) {
2526 return super.dispatchKeyEvent(event);
2527 }
2528
Alan Viverette5435a302015-01-29 10:25:34 -08002529 if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
2530 final KeyEvent.DispatcherState state = getKeyDispatcherState();
Jeff Brownb3ea9222011-01-10 16:26:36 -08002531 if (state != null) {
2532 state.startTracking(event, this);
2533 }
Dianne Hackborn8d374262009-09-14 21:21:52 -07002534 return true;
Jeff Brownb3ea9222011-01-10 16:26:36 -08002535 } else if (event.getAction() == KeyEvent.ACTION_UP) {
Alan Viverette5435a302015-01-29 10:25:34 -08002536 final KeyEvent.DispatcherState state = getKeyDispatcherState();
Jeff Brownb3ea9222011-01-10 16:26:36 -08002537 if (state != null && state.isTracking(event) && !event.isCanceled()) {
2538 dismiss();
2539 return true;
2540 }
Dianne Hackborn8d374262009-09-14 21:21:52 -07002541 }
2542 return super.dispatchKeyEvent(event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002543 } else {
2544 return super.dispatchKeyEvent(event);
2545 }
2546 }
2547
2548 @Override
2549 public boolean dispatchTouchEvent(MotionEvent ev) {
2550 if (mTouchInterceptor != null && mTouchInterceptor.onTouch(this, ev)) {
2551 return true;
2552 }
2553 return super.dispatchTouchEvent(ev);
2554 }
2555
2556 @Override
2557 public boolean onTouchEvent(MotionEvent event) {
2558 final int x = (int) event.getX();
2559 final int y = (int) event.getY();
Alan Viverette5435a302015-01-29 10:25:34 -08002560
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002561 if ((event.getAction() == MotionEvent.ACTION_DOWN)
2562 && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
2563 dismiss();
2564 return true;
2565 } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
2566 dismiss();
2567 return true;
2568 } else {
2569 return super.onTouchEvent(event);
2570 }
2571 }
Alan Viverette8fd949e2015-03-11 12:21:30 -07002572
2573 /**
2574 * Requests that an enter transition run after the next layout pass.
2575 */
2576 public void requestEnterTransition(Transition transition) {
2577 final ViewTreeObserver observer = getViewTreeObserver();
2578 if (observer != null && transition != null) {
2579 final Transition enterTransition = transition.clone();
2580
2581 // Postpone the enter transition after the first layout pass.
2582 observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
2583 @Override
2584 public void onGlobalLayout() {
2585 final ViewTreeObserver observer = getViewTreeObserver();
2586 if (observer != null) {
2587 observer.removeOnGlobalLayoutListener(this);
2588 }
2589
Alan Viverette91098572016-01-19 14:07:31 -05002590 final Rect epicenter = getTransitionEpicenter();
Alan Viverette95888c02015-04-16 13:27:50 -07002591 enterTransition.setEpicenterCallback(new EpicenterCallback() {
2592 @Override
2593 public Rect onGetEpicenter(Transition transition) {
2594 return epicenter;
2595 }
2596 });
Alan Viverette8fd949e2015-03-11 12:21:30 -07002597 startEnterTransition(enterTransition);
2598 }
2599 });
2600 }
2601 }
2602
2603 /**
2604 * Starts the pending enter transition, if one is set.
2605 */
2606 private void startEnterTransition(Transition enterTransition) {
2607 final int count = getChildCount();
2608 for (int i = 0; i < count; i++) {
2609 final View child = getChildAt(i);
2610 enterTransition.addTarget(child);
Evan Roskyadf5bec2017-10-19 10:24:07 -07002611 child.setTransitionVisibility(View.INVISIBLE);
Alan Viverette8fd949e2015-03-11 12:21:30 -07002612 }
2613
2614 TransitionManager.beginDelayedTransition(this, enterTransition);
2615
2616 for (int i = 0; i < count; i++) {
2617 final View child = getChildAt(i);
Evan Roskyadf5bec2017-10-19 10:24:07 -07002618 child.setTransitionVisibility(View.VISIBLE);
Alan Viverette8fd949e2015-03-11 12:21:30 -07002619 }
2620 }
2621
2622 /**
2623 * Starts an exit transition immediately.
2624 * <p>
2625 * <strong>Note:</strong> The transition listener is guaranteed to have
2626 * its {@code onTransitionEnd} method called even if the transition
Alan Viverette7e1aeb72017-03-22 11:06:59 -04002627 * never starts.
Alan Viverette8fd949e2015-03-11 12:21:30 -07002628 */
Alan Viverette054c1722016-12-01 13:33:44 -05002629 public void startExitTransition(@NonNull Transition transition,
2630 @Nullable final View anchorRoot, @Nullable final Rect epicenter,
2631 @NonNull final TransitionListener listener) {
Alan Viverette8fd949e2015-03-11 12:21:30 -07002632 if (transition == null) {
2633 return;
2634 }
2635
Alan Viverette634a8082016-02-03 14:22:41 -05002636 // The anchor view's window may go away while we're executing our
2637 // transition, in which case we need to end the transition
2638 // immediately and execute the listener to remove the popup.
Alan Viverette054c1722016-12-01 13:33:44 -05002639 if (anchorRoot != null) {
2640 anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
2641 }
Alan Viverette634a8082016-02-03 14:22:41 -05002642
Alan Viverette7e1aeb72017-03-22 11:06:59 -04002643 // The cleanup runnable MUST be called even if the transition is
2644 // canceled before it starts (and thus can't call onTransitionEnd).
2645 mCleanupAfterExit = () -> {
2646 listener.onTransitionEnd(transition);
Alan Viverette054c1722016-12-01 13:33:44 -05002647
Alan Viverette7e1aeb72017-03-22 11:06:59 -04002648 if (anchorRoot != null) {
2649 anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
Alan Viverette8fd949e2015-03-11 12:21:30 -07002650 }
Alan Viverette7e1aeb72017-03-22 11:06:59 -04002651
2652 // The listener was called. Our job here is done.
2653 mCleanupAfterExit = null;
Alan Viverette8fd949e2015-03-11 12:21:30 -07002654 };
2655
2656 final Transition exitTransition = transition.clone();
Alan Viverette7e1aeb72017-03-22 11:06:59 -04002657 exitTransition.addListener(new TransitionListenerAdapter() {
2658 @Override
2659 public void onTransitionEnd(Transition t) {
2660 t.removeListener(this);
2661
2662 // This null check shouldn't be necessary, but it's easier
2663 // to check here than it is to test every possible case.
2664 if (mCleanupAfterExit != null) {
2665 mCleanupAfterExit.run();
2666 }
2667 }
2668 });
Alan Viverette054c1722016-12-01 13:33:44 -05002669 exitTransition.setEpicenterCallback(new EpicenterCallback() {
2670 @Override
2671 public Rect onGetEpicenter(Transition transition) {
2672 return epicenter;
2673 }
2674 });
Alan Viverette8fd949e2015-03-11 12:21:30 -07002675
2676 final int count = getChildCount();
2677 for (int i = 0; i < count; i++) {
2678 final View child = getChildAt(i);
2679 exitTransition.addTarget(child);
2680 }
2681
2682 TransitionManager.beginDelayedTransition(this, exitTransition);
2683
2684 for (int i = 0; i < count; i++) {
2685 final View child = getChildAt(i);
2686 child.setVisibility(View.INVISIBLE);
2687 }
2688 }
2689
2690 /**
2691 * Cancels all pending or current transitions.
2692 */
2693 public void cancelTransitions() {
2694 TransitionManager.endTransitions(this);
2695
Alan Viverette7e1aeb72017-03-22 11:06:59 -04002696 // If the cleanup runnable is still around, that means the
2697 // transition never started. We should run it now to clean up.
2698 if (mCleanupAfterExit != null) {
2699 mCleanupAfterExit.run();
Alan Viverette8fd949e2015-03-11 12:21:30 -07002700 }
2701 }
Alan Viverette634a8082016-02-03 14:22:41 -05002702
2703 private final OnAttachStateChangeListener mOnAnchorRootDetachedListener =
2704 new OnAttachStateChangeListener() {
2705 @Override
2706 public void onViewAttachedToWindow(View v) {}
2707
2708 @Override
2709 public void onViewDetachedFromWindow(View v) {
2710 v.removeOnAttachStateChangeListener(this);
2711
Alan Viverette6d89f482018-01-05 15:33:24 -05002712 if (isAttachedToWindow()) {
2713 TransitionManager.endTransitions(PopupDecorView.this);
2714 }
Alan Viverette634a8082016-02-03 14:22:41 -05002715 }
2716 };
Peeyush Agarwal50db7312016-11-30 15:54:32 +00002717
2718 @Override
2719 public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> list, int deviceId) {
2720 if (mParentRootView != null) {
2721 View parentRoot = mParentRootView.get();
2722 if (parentRoot != null) {
2723 parentRoot.requestKeyboardShortcuts(list, deviceId);
2724 }
2725 }
2726 }
Alan Viverette5435a302015-01-29 10:25:34 -08002727 }
svetoslavganov75986cf2009-05-14 22:28:01 -07002728
Alan Viverette5435a302015-01-29 10:25:34 -08002729 private class PopupBackgroundView extends FrameLayout {
2730 public PopupBackgroundView(Context context) {
2731 super(context);
2732 }
2733
svetoslavganov75986cf2009-05-14 22:28:01 -07002734 @Override
Alan Viverette5435a302015-01-29 10:25:34 -08002735 protected int[] onCreateDrawableState(int extraSpace) {
2736 if (mAboveAnchor) {
2737 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
2738 View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET);
2739 return drawableState;
svetoslavganov75986cf2009-05-14 22:28:01 -07002740 } else {
Alan Viverette5435a302015-01-29 10:25:34 -08002741 return super.onCreateDrawableState(extraSpace);
svetoslavganov75986cf2009-05-14 22:28:01 -07002742 }
2743 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002744 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002745}