blob: 7217def3cf087c3577453cc8c8f05762e048ff05 [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;
21import static android.view.WindowManager.LayoutParams
22 .PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
23import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024
Alan Viveretteb854d072015-09-28 16:12:18 -040025import android.annotation.NonNull;
Alan Viverette1e940dc2016-03-18 09:55:10 -040026import android.annotation.Nullable;
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
Romain Guy448ecf52009-05-14 16:03:42 -0700144 private Context mContext;
145 private WindowManager mWindowManager;
Alan Viverette5435a302015-01-29 10:25:34 -0800146
Peeyush Agarwal50db7312016-11-30 15:54:32 +0000147 /**
148 * Keeps track of popup's parent's decor view. This is needed to dispatch
149 * requestKeyboardShortcuts to the owning Activity.
150 */
151 private WeakReference<View> mParentRootView;
152
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 private boolean mIsShowing;
Alan Viverette8fd949e2015-03-11 12:21:30 -0700154 private boolean mIsTransitioningToDismiss;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 private boolean mIsDropdown;
156
Alan Viverette5435a302015-01-29 10:25:34 -0800157 /** View that handles event dispatch and content transitions. */
158 private PopupDecorView mDecorView;
159
Alan Viverette697804e2015-08-06 12:36:47 -0400160 /** View that holds the background and may animate during a transition. */
161 private View mBackgroundView;
162
163 /** The contents of the popup. May be identical to the background view. */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 private View mContentView;
Alan Viverette5435a302015-01-29 10:25:34 -0800165
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 private boolean mFocusable;
167 private int mInputMethodMode = INPUT_METHOD_FROM_FOCUSABLE;
Yohei Yukawa22dac1c2017-02-12 16:54:16 -0800168 @SoftInputModeFlags
Dianne Hackborn7eab0942011-01-01 13:21:50 -0800169 private int mSoftInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 private boolean mTouchable = true;
171 private boolean mOutsideTouchable = false;
172 private boolean mClippingEnabled = true;
Jeff Brown46e75292010-11-10 16:53:45 -0800173 private int mSplitTouchEnabled = -1;
Adam Powellba0a2c32010-09-28 17:41:23 -0700174 private boolean mLayoutInScreen;
Adam Powell56c2d332010-11-05 20:03:03 -0700175 private boolean mClipToScreen;
Adam Powell348e69c2011-02-16 16:49:50 -0800176 private boolean mAllowScrollingAnchorParent = true;
Adam Powell0bd1d0a2011-07-22 19:35:06 -0700177 private boolean mLayoutInsetDecor = false;
Adam Powelle0b6cd12011-09-28 22:06:11 -0700178 private boolean mNotTouchModal;
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700179 private boolean mAttachedInDecor = true;
180 private boolean mAttachedInDecorSet = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181
182 private OnTouchListener mTouchInterceptor;
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700183
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 private int mWidthMode;
Alan Viverette259c2842015-03-22 17:39:39 -0700185 private int mWidth = LayoutParams.WRAP_CONTENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 private int mLastWidth;
187 private int mHeightMode;
Alan Viverette259c2842015-03-22 17:39:39 -0700188 private int mHeight = LayoutParams.WRAP_CONTENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 private int mLastHeight;
190
Alan Viveretteccb11e12014-07-08 16:04:02 -0700191 private float mElevation;
192
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 private Drawable mBackground;
194 private Drawable mAboveAnchorBackgroundDrawable;
195 private Drawable mBelowAnchorBackgroundDrawable;
196
Alan Viverette5435a302015-01-29 10:25:34 -0800197 private Transition mEnterTransition;
198 private Transition mExitTransition;
Alan Viverette91098572016-01-19 14:07:31 -0500199 private Rect mEpicenterBounds;
Alan Viverette560f1702014-05-05 14:40:07 -0700200
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 private boolean mAboveAnchor;
Adam Powell574b37e2010-10-07 11:15:19 -0700202 private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
Alan Viverette5435a302015-01-29 10:25:34 -0800203
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 private OnDismissListener mOnDismissListener;
205 private boolean mIgnoreCheekPress = false;
206
Alan Viverette5435a302015-01-29 10:25:34 -0800207 private int mAnimationStyle = ANIMATION_STYLE_DEFAULT;
208
Robert Carr08516062016-08-23 10:17:54 -0700209 private int mGravity = Gravity.NO_GRAVITY;
210
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 private static final int[] ABOVE_ANCHOR_STATE_SET = new int[] {
Felipe Leme4753bb02017-03-22 20:24:00 -0700212 com.android.internal.R.attr.state_above_anchor
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 };
214
Vladislav Kaznacheevafaa9322017-02-09 11:43:28 -0800215 private final OnAttachStateChangeListener mOnAnchorDetachedListener =
216 new OnAttachStateChangeListener() {
217 @Override
218 public void onViewAttachedToWindow(View v) {
219 // Anchor might have been reattached in a different position.
220 alignToAnchor();
221 }
222
223 @Override
224 public void onViewDetachedFromWindow(View v) {
225 // Leave the popup in its current position.
226 // The anchor might become attached again.
227 }
228 };
229
Alan Viverette634a8082016-02-03 14:22:41 -0500230 private final OnAttachStateChangeListener mOnAnchorRootDetachedListener =
231 new OnAttachStateChangeListener() {
232 @Override
233 public void onViewAttachedToWindow(View v) {}
234
235 @Override
236 public void onViewDetachedFromWindow(View v) {
237 mIsAnchorRootAttached = false;
238 }
239 };
240
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 private WeakReference<View> mAnchor;
Alan Viverette634a8082016-02-03 14:22:41 -0500242 private WeakReference<View> mAnchorRoot;
243 private boolean mIsAnchorRootAttached;
Alan Viverette560f1702014-05-05 14:40:07 -0700244
Vladislav Kaznacheevafaa9322017-02-09 11:43:28 -0800245 private final OnScrollChangedListener mOnScrollChangedListener = this::alignToAnchor;
Alan Viverette560f1702014-05-05 14:40:07 -0700246
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -0800247 private final View.OnLayoutChangeListener mOnLayoutChangeListener =
248 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> alignToAnchor();
249
Alan Viverette5435a302015-01-29 10:25:34 -0800250 private int mAnchorXoff;
251 private int mAnchorYoff;
252 private int mAnchoredGravity;
Alan Viverette560f1702014-05-05 14:40:07 -0700253 private boolean mOverlapAnchor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254
Fabrice Di Megliob003e282012-10-17 17:20:19 -0700255 private boolean mPopupViewInitialLayoutDirectionInherited;
256
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257 /**
258 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
259 *
260 * <p>The popup does provide a background.</p>
261 */
262 public PopupWindow(Context context) {
263 this(context, null);
264 }
265
266 /**
267 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
268 *
269 * <p>The popup does provide a background.</p>
270 */
271 public PopupWindow(Context context, AttributeSet attrs) {
272 this(context, attrs, com.android.internal.R.attr.popupWindowStyle);
273 }
274
275 /**
276 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
277 *
278 * <p>The popup does provide a background.</p>
279 */
Alan Viverette617feb92013-09-09 18:09:13 -0700280 public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
281 this(context, attrs, defStyleAttr, 0);
Adam Powellc3fa6302010-05-18 11:36:27 -0700282 }
Alan Viverette5435a302015-01-29 10:25:34 -0800283
Adam Powellc3fa6302010-05-18 11:36:27 -0700284 /**
285 * <p>Create a new, empty, non focusable popup window of dimension (0,0).</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800286 *
Adam Powellc3fa6302010-05-18 11:36:27 -0700287 * <p>The popup does not provide a background.</p>
288 */
289 public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290 mContext = context;
Alan Viverette75d83792015-01-07 15:51:54 -0800291 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292
Alan Viverette617feb92013-09-09 18:09:13 -0700293 final TypedArray a = context.obtainStyledAttributes(
Alan Viverettece8c3582014-11-07 13:19:38 -0800294 attrs, R.styleable.PopupWindow, defStyleAttr, defStyleRes);
295 final Drawable bg = a.getDrawable(R.styleable.PopupWindow_popupBackground);
Alan Viveretteccb11e12014-07-08 16:04:02 -0700296 mElevation = a.getDimension(R.styleable.PopupWindow_popupElevation, 0);
Alan Viverette560f1702014-05-05 14:40:07 -0700297 mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false);
Adam Powellc3808b52010-10-04 10:06:59 -0700298
Alan Viverette5435a302015-01-29 10:25:34 -0800299 // Preserve default behavior from Gingerbread. If the animation is
300 // undefined or explicitly specifies the Gingerbread animation style,
301 // use a sentinel value.
302 if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupAnimationStyle)) {
303 final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, 0);
304 if (animStyle == R.style.Animation_PopupWindow) {
305 mAnimationStyle = ANIMATION_STYLE_DEFAULT;
306 } else {
307 mAnimationStyle = animStyle;
308 }
309 } else {
310 mAnimationStyle = ANIMATION_STYLE_DEFAULT;
311 }
312
313 final Transition enterTransition = getTransition(a.getResourceId(
314 R.styleable.PopupWindow_popupEnterTransition, 0));
315 final Transition exitTransition;
316 if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupExitTransition)) {
317 exitTransition = getTransition(a.getResourceId(
318 R.styleable.PopupWindow_popupExitTransition, 0));
319 } else {
320 exitTransition = enterTransition == null ? null : enterTransition.clone();
321 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 a.recycle();
Alan Viverettece8c3582014-11-07 13:19:38 -0800324
Alan Viverette5435a302015-01-29 10:25:34 -0800325 setEnterTransition(enterTransition);
326 setExitTransition(exitTransition);
Alan Viverettece8c3582014-11-07 13:19:38 -0800327 setBackgroundDrawable(bg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 }
329
330 /**
331 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
332 *
333 * <p>The popup does not provide any background. This should be handled
334 * by the content view.</p>
335 */
336 public PopupWindow() {
337 this(null, 0, 0);
338 }
339
340 /**
341 * <p>Create a new non focusable popup window which can display the
342 * <tt>contentView</tt>. The dimension of the window are (0,0).</p>
343 *
344 * <p>The popup does not provide any background. This should be handled
345 * by the content view.</p>
346 *
347 * @param contentView the popup's content
348 */
349 public PopupWindow(View contentView) {
350 this(contentView, 0, 0);
351 }
352
353 /**
354 * <p>Create a new empty, non focusable popup window. The dimension of the
355 * window must be passed to this constructor.</p>
356 *
357 * <p>The popup does not provide any background. This should be handled
358 * by the content view.</p>
359 *
360 * @param width the popup's width
361 * @param height the popup's height
362 */
363 public PopupWindow(int width, int height) {
364 this(null, width, height);
365 }
366
367 /**
368 * <p>Create a new non focusable popup window which can display the
369 * <tt>contentView</tt>. The dimension of the window must be passed to
370 * this constructor.</p>
371 *
372 * <p>The popup does not provide any background. This should be handled
373 * by the content view.</p>
374 *
375 * @param contentView the popup's content
376 * @param width the popup's width
377 * @param height the popup's height
378 */
379 public PopupWindow(View contentView, int width, int height) {
380 this(contentView, width, height, false);
381 }
382
383 /**
384 * <p>Create a new popup window which can display the <tt>contentView</tt>.
385 * The dimension of the window must be passed to this constructor.</p>
386 *
387 * <p>The popup does not provide any background. This should be handled
388 * by the content view.</p>
389 *
390 * @param contentView the popup's content
391 * @param width the popup's width
392 * @param height the popup's height
393 * @param focusable true if the popup can be focused, false otherwise
394 */
Romain Guy448ecf52009-05-14 16:03:42 -0700395 public PopupWindow(View contentView, int width, int height, boolean focusable) {
396 if (contentView != null) {
397 mContext = contentView.getContext();
398 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
399 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700400
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 setContentView(contentView);
402 setWidth(width);
403 setHeight(height);
404 setFocusable(focusable);
405 }
406
Alan Viverette1e940dc2016-03-18 09:55:10 -0400407 /**
408 * Sets the enter transition to be used when the popup window is shown.
409 *
410 * @param enterTransition the enter transition, or {@code null} to clear
411 * @see #getEnterTransition()
412 * @attr ref android.R.styleable#PopupWindow_popupEnterTransition
413 */
414 public void setEnterTransition(@Nullable Transition enterTransition) {
Alan Viverette5435a302015-01-29 10:25:34 -0800415 mEnterTransition = enterTransition;
Alan Viverette5435a302015-01-29 10:25:34 -0800416 }
417
Alan Viverette1e940dc2016-03-18 09:55:10 -0400418 /**
419 * Returns the enter transition to be used when the popup window is shown.
420 *
421 * @return the enter transition, or {@code null} if not set
422 * @see #setEnterTransition(Transition)
423 * @attr ref android.R.styleable#PopupWindow_popupEnterTransition
424 */
425 @Nullable
426 public Transition getEnterTransition() {
427 return mEnterTransition;
428 }
429
430 /**
431 * Sets the exit transition to be used when the popup window is dismissed.
432 *
433 * @param exitTransition the exit transition, or {@code null} to clear
434 * @see #getExitTransition()
435 * @attr ref android.R.styleable#PopupWindow_popupExitTransition
436 */
437 public void setExitTransition(@Nullable Transition exitTransition) {
Alan Viverette5435a302015-01-29 10:25:34 -0800438 mExitTransition = exitTransition;
Alan Viverette5435a302015-01-29 10:25:34 -0800439 }
440
Alan Viverette91098572016-01-19 14:07:31 -0500441 /**
Alan Viverette1e940dc2016-03-18 09:55:10 -0400442 * Returns the exit transition to be used when the popup window is
443 * dismissed.
444 *
445 * @return the exit transition, or {@code null} if not set
446 * @see #setExitTransition(Transition)
447 * @attr ref android.R.styleable#PopupWindow_popupExitTransition
448 */
449 @Nullable
450 public Transition getExitTransition() {
451 return mExitTransition;
452 }
453
454 /**
Alan Viverette91098572016-01-19 14:07:31 -0500455 * Sets the bounds used as the epicenter of the enter and exit transitions.
456 * <p>
457 * Transitions use a point or Rect, referred to as the epicenter, to orient
458 * the direction of travel. For popup windows, the anchor view bounds are
459 * used as the default epicenter.
460 * <p>
461 * See {@link Transition#setEpicenterCallback(EpicenterCallback)} for more
462 * information about how transition epicenters.
463 *
464 * @param bounds the epicenter bounds relative to the anchor view, or
465 * {@code null} to use the default epicenter
466 * @see #getTransitionEpicenter()
467 * @hide
468 */
469 public void setEpicenterBounds(Rect bounds) {
470 mEpicenterBounds = bounds;
471 }
472
Alan Viverette5435a302015-01-29 10:25:34 -0800473 private Transition getTransition(int resId) {
474 if (resId != 0 && resId != R.transition.no_transition) {
475 final TransitionInflater inflater = TransitionInflater.from(mContext);
476 final Transition transition = inflater.inflateTransition(resId);
477 if (transition != null) {
478 final boolean isEmpty = transition instanceof TransitionSet
479 && ((TransitionSet) transition).getTransitionCount() == 0;
480 if (!isEmpty) {
481 return transition;
482 }
483 }
484 }
485 return null;
486 }
487
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700489 * Return the drawable used as the popup window's background.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 *
Alan Viveretteccb11e12014-07-08 16:04:02 -0700491 * @return the background drawable or {@code null} if not set
492 * @see #setBackgroundDrawable(Drawable)
493 * @attr ref android.R.styleable#PopupWindow_popupBackground
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 */
495 public Drawable getBackground() {
496 return mBackground;
497 }
498
499 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700500 * Specifies the background drawable for this popup window. The background
501 * can be set to {@code null}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502 *
503 * @param background the popup's background
Alan Viveretteccb11e12014-07-08 16:04:02 -0700504 * @see #getBackground()
505 * @attr ref android.R.styleable#PopupWindow_popupBackground
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506 */
507 public void setBackgroundDrawable(Drawable background) {
508 mBackground = background;
Alan Viverettece8c3582014-11-07 13:19:38 -0800509
510 // If this is a StateListDrawable, try to find and store the drawable to be
511 // used when the drop-down is placed above its anchor view, and the one to be
512 // used when the drop-down is placed below its anchor view. We extract
513 // the drawables ourselves to work around a problem with using refreshDrawableState
514 // that it will take into account the padding of all drawables specified in a
515 // StateListDrawable, thus adding superfluous padding to drop-down views.
516 //
517 // We assume a StateListDrawable will have a drawable for ABOVE_ANCHOR_STATE_SET and
518 // at least one other drawable, intended for the 'below-anchor state'.
519 if (mBackground instanceof StateListDrawable) {
520 StateListDrawable stateList = (StateListDrawable) mBackground;
521
522 // Find the above-anchor view - this one's easy, it should be labeled as such.
523 int aboveAnchorStateIndex = stateList.getStateDrawableIndex(ABOVE_ANCHOR_STATE_SET);
524
525 // Now, for the below-anchor view, look for any other drawable specified in the
526 // StateListDrawable which is not for the above-anchor state and use that.
527 int count = stateList.getStateCount();
528 int belowAnchorStateIndex = -1;
529 for (int i = 0; i < count; i++) {
530 if (i != aboveAnchorStateIndex) {
531 belowAnchorStateIndex = i;
532 break;
533 }
534 }
535
536 // Store the drawables we found, if we found them. Otherwise, set them both
537 // to null so that we'll just use refreshDrawableState.
538 if (aboveAnchorStateIndex != -1 && belowAnchorStateIndex != -1) {
539 mAboveAnchorBackgroundDrawable = stateList.getStateDrawable(aboveAnchorStateIndex);
540 mBelowAnchorBackgroundDrawable = stateList.getStateDrawable(belowAnchorStateIndex);
541 } else {
542 mBelowAnchorBackgroundDrawable = null;
543 mAboveAnchorBackgroundDrawable = null;
544 }
545 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800546 }
547
548 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700549 * @return the elevation for this popup window in pixels
550 * @see #setElevation(float)
551 * @attr ref android.R.styleable#PopupWindow_popupElevation
552 */
553 public float getElevation() {
554 return mElevation;
555 }
556
557 /**
558 * Specifies the elevation for this popup window.
559 *
560 * @param elevation the popup's elevation in pixels
561 * @see #getElevation()
562 * @attr ref android.R.styleable#PopupWindow_popupElevation
563 */
564 public void setElevation(float elevation) {
565 mElevation = elevation;
566 }
567
568 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 * <p>Return the animation style to use the popup appears and disappears</p>
570 *
571 * @return the animation style to use the popup appears and disappears
572 */
573 public int getAnimationStyle() {
574 return mAnimationStyle;
575 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700576
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577 /**
Shuhrat Dehkanov54ec76d2014-08-28 17:12:23 +0900578 * Set the flag on popup to ignore cheek press events; by default this flag
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 * is set to false
Shuhrat Dehkanov54ec76d2014-08-28 17:12:23 +0900580 * which means the popup will not ignore cheek press dispatch events.
Alan Viverette5435a302015-01-29 10:25:34 -0800581 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582 * <p>If the popup is showing, calling this method will take effect only
583 * the next time the popup is shown or through a manual call to one of
584 * the {@link #update()} methods.</p>
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700585 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800586 * @see #update()
587 */
588 public void setIgnoreCheekPress() {
589 mIgnoreCheekPress = true;
590 }
Alan Viverette5435a302015-01-29 10:25:34 -0800591
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800592 /**
593 * <p>Change the animation style resource for this popup.</p>
594 *
595 * <p>If the popup is showing, calling this method will take effect only
596 * the next time the popup is shown or through a manual call to one of
597 * the {@link #update()} methods.</p>
598 *
599 * @param animationStyle animation style to use when the popup appears
600 * and disappears. Set to -1 for the default animation, 0 for no
601 * animation, or a resource identifier for an explicit animation.
Alan Viverette5435a302015-01-29 10:25:34 -0800602 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603 * @see #update()
604 */
605 public void setAnimationStyle(int animationStyle) {
606 mAnimationStyle = animationStyle;
607 }
Alan Viverette5435a302015-01-29 10:25:34 -0800608
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 /**
610 * <p>Return the view used as the content of the popup window.</p>
611 *
612 * @return a {@link android.view.View} representing the popup's content
613 *
614 * @see #setContentView(android.view.View)
615 */
616 public View getContentView() {
617 return mContentView;
618 }
619
620 /**
621 * <p>Change the popup's content. The content is represented by an instance
622 * of {@link android.view.View}.</p>
623 *
Gilles Debunne81f08082011-02-17 14:07:19 -0800624 * <p>This method has no effect if called when the popup is showing.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800625 *
626 * @param contentView the new content for the popup
627 *
628 * @see #getContentView()
629 * @see #isShowing()
630 */
631 public void setContentView(View contentView) {
632 if (isShowing()) {
633 return;
634 }
635
636 mContentView = contentView;
Romain Guy448ecf52009-05-14 16:03:42 -0700637
Romain Guy0c0b7682011-05-16 11:54:09 -0700638 if (mContext == null && mContentView != null) {
Romain Guy448ecf52009-05-14 16:03:42 -0700639 mContext = mContentView.getContext();
640 }
641
Romain Guy0c0b7682011-05-16 11:54:09 -0700642 if (mWindowManager == null && mContentView != null) {
Romain Guy448ecf52009-05-14 16:03:42 -0700643 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
644 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700645
646 // Setting the default for attachedInDecor based on SDK version here
647 // instead of in the constructor since we might not have the context
648 // object in the constructor. We only want to set default here if the
649 // app hasn't already set the attachedInDecor.
650 if (mContext != null && !mAttachedInDecorSet) {
651 // Attach popup window in decor frame of parent window by default for
652 // {@link Build.VERSION_CODES.LOLLIPOP_MR1} or greater. Keep current
653 // behavior of not attaching to decor frame for older SDKs.
654 setAttachedInDecor(mContext.getApplicationInfo().targetSdkVersion
655 >= Build.VERSION_CODES.LOLLIPOP_MR1);
656 }
657
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 }
659
660 /**
661 * Set a callback for all touch events being dispatched to the popup
662 * window.
663 */
664 public void setTouchInterceptor(OnTouchListener l) {
665 mTouchInterceptor = l;
666 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700667
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 /**
669 * <p>Indicate whether the popup window can grab the focus.</p>
670 *
671 * @return true if the popup is focusable, false otherwise
672 *
673 * @see #setFocusable(boolean)
674 */
675 public boolean isFocusable() {
676 return mFocusable;
677 }
678
679 /**
680 * <p>Changes the focusability of the popup window. When focusable, the
681 * window will grab the focus from the current focused widget if the popup
682 * contains a focusable {@link android.view.View}. By default a popup
683 * window is not focusable.</p>
684 *
685 * <p>If the popup is showing, calling this method will take effect only
686 * the next time the popup is shown or through a manual call to one of
687 * the {@link #update()} methods.</p>
688 *
689 * @param focusable true if the popup should grab focus, false otherwise.
690 *
691 * @see #isFocusable()
Alan Viverette5435a302015-01-29 10:25:34 -0800692 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693 * @see #update()
694 */
695 public void setFocusable(boolean focusable) {
696 mFocusable = focusable;
697 }
698
699 /**
700 * Return the current value in {@link #setInputMethodMode(int)}.
Alan Viverette5435a302015-01-29 10:25:34 -0800701 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800702 * @see #setInputMethodMode(int)
703 */
704 public int getInputMethodMode() {
705 return mInputMethodMode;
Alan Viverette5435a302015-01-29 10:25:34 -0800706
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800707 }
Alan Viverette5435a302015-01-29 10:25:34 -0800708
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800709 /**
710 * Control how the popup operates with an input method: one of
711 * {@link #INPUT_METHOD_FROM_FOCUSABLE}, {@link #INPUT_METHOD_NEEDED},
712 * or {@link #INPUT_METHOD_NOT_NEEDED}.
Alan Viverette5435a302015-01-29 10:25:34 -0800713 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800714 * <p>If the popup is showing, calling this method will take effect only
715 * the next time the popup is shown or through a manual call to one of
716 * the {@link #update()} methods.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800717 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 * @see #getInputMethodMode()
719 * @see #update()
720 */
721 public void setInputMethodMode(int mode) {
722 mInputMethodMode = mode;
723 }
Romain Guy374aaaed32009-07-14 15:11:59 -0700724
725 /**
726 * Sets the operating mode for the soft input area.
727 *
728 * @param mode The desired mode, see
729 * {@link android.view.WindowManager.LayoutParams#softInputMode}
730 * for the full list
731 *
732 * @see android.view.WindowManager.LayoutParams#softInputMode
733 * @see #getSoftInputMode()
734 */
Yohei Yukawa22dac1c2017-02-12 16:54:16 -0800735 public void setSoftInputMode(@SoftInputModeFlags int mode) {
Romain Guy374aaaed32009-07-14 15:11:59 -0700736 mSoftInputMode = mode;
737 }
738
739 /**
740 * Returns the current value in {@link #setSoftInputMode(int)}.
741 *
742 * @see #setSoftInputMode(int)
743 * @see android.view.WindowManager.LayoutParams#softInputMode
744 */
Yohei Yukawa22dac1c2017-02-12 16:54:16 -0800745 @SoftInputModeFlags
Romain Guy374aaaed32009-07-14 15:11:59 -0700746 public int getSoftInputMode() {
747 return mSoftInputMode;
748 }
Alan Viverette5435a302015-01-29 10:25:34 -0800749
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750 /**
751 * <p>Indicates whether the popup window receives touch events.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800752 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800753 * @return true if the popup is touchable, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800754 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800755 * @see #setTouchable(boolean)
756 */
757 public boolean isTouchable() {
758 return mTouchable;
759 }
760
761 /**
762 * <p>Changes the touchability of the popup window. When touchable, the
763 * window will receive touch events, otherwise touch events will go to the
764 * window below it. By default the window is touchable.</p>
765 *
766 * <p>If the popup is showing, calling this method will take effect only
767 * the next time the popup is shown or through a manual call to one of
768 * the {@link #update()} methods.</p>
769 *
770 * @param touchable true if the popup should receive touch events, false otherwise
771 *
772 * @see #isTouchable()
Alan Viverette5435a302015-01-29 10:25:34 -0800773 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800774 * @see #update()
775 */
776 public void setTouchable(boolean touchable) {
777 mTouchable = touchable;
778 }
779
780 /**
781 * <p>Indicates whether the popup window will be informed of touch events
782 * outside of its window.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800783 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 * @return true if the popup is outside touchable, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800785 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800786 * @see #setOutsideTouchable(boolean)
787 */
788 public boolean isOutsideTouchable() {
789 return mOutsideTouchable;
790 }
791
792 /**
793 * <p>Controls whether the pop-up will be informed of touch events outside
794 * of its window. This only makes sense for pop-ups that are touchable
795 * but not focusable, which means touches outside of the window will
796 * be delivered to the window behind. The default is false.</p>
797 *
798 * <p>If the popup is showing, calling this method will take effect only
799 * the next time the popup is shown or through a manual call to one of
800 * the {@link #update()} methods.</p>
801 *
802 * @param touchable true if the popup should receive outside
803 * touch events, false otherwise
804 *
805 * @see #isOutsideTouchable()
Alan Viverette5435a302015-01-29 10:25:34 -0800806 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800807 * @see #update()
808 */
809 public void setOutsideTouchable(boolean touchable) {
810 mOutsideTouchable = touchable;
811 }
812
813 /**
814 * <p>Indicates whether clipping of the popup window is enabled.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800815 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800816 * @return true if the clipping is enabled, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800817 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 * @see #setClippingEnabled(boolean)
819 */
820 public boolean isClippingEnabled() {
821 return mClippingEnabled;
822 }
823
824 /**
825 * <p>Allows the popup window to extend beyond the bounds of the screen. By default the
826 * window is clipped to the screen boundaries. Setting this to false will allow windows to be
827 * accurately positioned.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800828 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800829 * <p>If the popup is showing, calling this method will take effect only
830 * the next time the popup is shown or through a manual call to one of
831 * the {@link #update()} methods.</p>
832 *
833 * @param enabled false if the window should be allowed to extend outside of the screen
Alan Viverette5435a302015-01-29 10:25:34 -0800834 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800835 * @see #isClippingEnabled()
836 * @see #update()
837 */
838 public void setClippingEnabled(boolean enabled) {
839 mClippingEnabled = enabled;
840 }
841
842 /**
Adam Powell56c2d332010-11-05 20:03:03 -0700843 * Clip this popup window to the screen, but not to the containing window.
844 *
845 * @param enabled True to clip to the screen.
846 * @hide
847 */
848 public void setClipToScreenEnabled(boolean enabled) {
849 mClipToScreen = enabled;
Adam Powell56c2d332010-11-05 20:03:03 -0700850 }
Adam Powell348e69c2011-02-16 16:49:50 -0800851
852 /**
853 * Allow PopupWindow to scroll the anchor's parent to provide more room
854 * for the popup. Enabled by default.
855 *
856 * @param enabled True to scroll the anchor's parent when more room is desired by the popup.
857 */
858 void setAllowScrollingAnchorParent(boolean enabled) {
859 mAllowScrollingAnchorParent = enabled;
860 }
Alan Viverette5435a302015-01-29 10:25:34 -0800861
Felipe Leme4753bb02017-03-22 20:24:00 -0700862 /** @hide */
863 protected final boolean getAllowScrollingAnchorParent() {
864 return mAllowScrollingAnchorParent;
865 }
866
Adam Powell56c2d332010-11-05 20:03:03 -0700867 /**
Jeff Brown01ce2e92010-09-26 22:20:12 -0700868 * <p>Indicates whether the popup window supports splitting touches.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800869 *
Jeff Brown01ce2e92010-09-26 22:20:12 -0700870 * @return true if the touch splitting is enabled, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800871 *
Jeff Brown01ce2e92010-09-26 22:20:12 -0700872 * @see #setSplitTouchEnabled(boolean)
Jeff Brown01ce2e92010-09-26 22:20:12 -0700873 */
874 public boolean isSplitTouchEnabled() {
Jeff Brown46e75292010-11-10 16:53:45 -0800875 if (mSplitTouchEnabled < 0 && mContext != null) {
876 return mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB;
877 }
878 return mSplitTouchEnabled == 1;
Jeff Brown01ce2e92010-09-26 22:20:12 -0700879 }
880
881 /**
882 * <p>Allows the popup window to split touches across other windows that also
Jeff Brown46e75292010-11-10 16:53:45 -0800883 * support split touch. When this flag is false, the first pointer
Jeff Brown01ce2e92010-09-26 22:20:12 -0700884 * that goes down determines the window to which all subsequent touches
Jeff Brown46e75292010-11-10 16:53:45 -0800885 * go until all pointers go up. When this flag is true, each pointer
Jeff Brown01ce2e92010-09-26 22:20:12 -0700886 * (not necessarily the first) that goes down determines the window
887 * to which all subsequent touches of that pointer will go until that
888 * pointer goes up thereby enabling touches with multiple pointers
889 * to be split across multiple windows.</p>
890 *
891 * @param enabled true if the split touches should be enabled, false otherwise
892 * @see #isSplitTouchEnabled()
Jeff Brown01ce2e92010-09-26 22:20:12 -0700893 */
894 public void setSplitTouchEnabled(boolean enabled) {
Jeff Brown46e75292010-11-10 16:53:45 -0800895 mSplitTouchEnabled = enabled ? 1 : 0;
Jeff Brown01ce2e92010-09-26 22:20:12 -0700896 }
897
898 /**
Adam Powellba0a2c32010-09-28 17:41:23 -0700899 * <p>Indicates whether the popup window will be forced into using absolute screen coordinates
900 * for positioning.</p>
901 *
902 * @return true if the window will always be positioned in screen coordinates.
903 * @hide
904 */
905 public boolean isLayoutInScreenEnabled() {
906 return mLayoutInScreen;
907 }
908
909 /**
910 * <p>Allows the popup window to force the flag
911 * {@link WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN}, overriding default behavior.
912 * This will cause the popup to be positioned in absolute screen coordinates.</p>
913 *
914 * @param enabled true if the popup should always be positioned in screen coordinates
915 * @hide
916 */
917 public void setLayoutInScreenEnabled(boolean enabled) {
918 mLayoutInScreen = enabled;
919 }
920
921 /**
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700922 * <p>Indicates whether the popup window will be attached in the decor frame of its parent
923 * window.
924 *
925 * @return true if the window will be attached to the decor frame of its parent window.
926 *
927 * @see #setAttachedInDecor(boolean)
928 * @see WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR
929 */
930 public boolean isAttachedInDecor() {
931 return mAttachedInDecor;
932 }
933
934 /**
935 * <p>This will attach the popup window to the decor frame of the parent window to avoid
936 * overlaping with screen decorations like the navigation bar. Overrides the default behavior of
937 * the flag {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR}.
938 *
939 * <p>By default the flag is set on SDK version {@link Build.VERSION_CODES#LOLLIPOP_MR1} or
940 * greater and cleared on lesser SDK versions.
941 *
942 * @param enabled true if the popup should be attached to the decor frame of its parent window.
943 *
944 * @see WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR
945 */
946 public void setAttachedInDecor(boolean enabled) {
947 mAttachedInDecor = enabled;
948 mAttachedInDecorSet = true;
949 }
950
951 /**
Adam Powell0bd1d0a2011-07-22 19:35:06 -0700952 * Allows the popup window to force the flag
953 * {@link WindowManager.LayoutParams#FLAG_LAYOUT_INSET_DECOR}, overriding default behavior.
954 * This will cause the popup to inset its content to account for system windows overlaying
955 * the screen, such as the status bar.
956 *
957 * <p>This will often be combined with {@link #setLayoutInScreenEnabled(boolean)}.
958 *
959 * @param enabled true if the popup's views should inset content to account for system windows,
960 * the way that decor views behave for full-screen windows.
961 * @hide
962 */
963 public void setLayoutInsetDecor(boolean enabled) {
964 mLayoutInsetDecor = enabled;
965 }
966
Felipe Leme4753bb02017-03-22 20:24:00 -0700967 /** @hide */
968 protected final boolean isLayoutInsetDecor() {
969 return mLayoutInsetDecor;
970 }
971
Adam Powell0bd1d0a2011-07-22 19:35:06 -0700972 /**
Alan Viverette80ebe0d2015-04-30 15:53:11 -0700973 * Set the layout type for this window.
974 * <p>
975 * See {@link WindowManager.LayoutParams#type} for possible values.
Adam Powell574b37e2010-10-07 11:15:19 -0700976 *
977 * @param layoutType Layout type for this window.
Chris Banes36344a92015-04-14 10:43:16 +0100978 *
979 * @see WindowManager.LayoutParams#type
Adam Powell574b37e2010-10-07 11:15:19 -0700980 */
981 public void setWindowLayoutType(int layoutType) {
982 mWindowLayoutType = layoutType;
983 }
984
985 /**
Chris Banes36344a92015-04-14 10:43:16 +0100986 * Returns the layout type for this window.
987 *
988 * @see #setWindowLayoutType(int)
Adam Powell574b37e2010-10-07 11:15:19 -0700989 */
990 public int getWindowLayoutType() {
991 return mWindowLayoutType;
992 }
993
994 /**
Adam Powelle0b6cd12011-09-28 22:06:11 -0700995 * Set whether this window is touch modal or if outside touches will be sent to
996 * other windows behind it.
997 * @hide
998 */
999 public void setTouchModal(boolean touchModal) {
1000 mNotTouchModal = !touchModal;
1001 }
1002
1003 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 * <p>Change the width and height measure specs that are given to the
1005 * window manager by the popup. By default these are 0, meaning that
1006 * the current width or height is requested as an explicit size from
1007 * the window manager. You can supply
Alan Viverette5435a302015-01-29 10:25:34 -08001008 * {@link ViewGroup.LayoutParams#WRAP_CONTENT} or
Romain Guy980a9382010-01-08 15:06:28 -08001009 * {@link ViewGroup.LayoutParams#MATCH_PARENT} to have that measure
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001010 * spec supplied instead, replacing the absolute width and height that
1011 * has been set in the popup.</p>
1012 *
1013 * <p>If the popup is showing, calling this method will take effect only
1014 * the next time the popup is shown.</p>
1015 *
1016 * @param widthSpec an explicit width measure spec mode, either
1017 * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
Romain Guy980a9382010-01-08 15:06:28 -08001018 * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019 * width.
1020 * @param heightSpec an explicit height measure spec mode, either
1021 * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
Romain Guy980a9382010-01-08 15:06:28 -08001022 * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001023 * height.
Alan Viverette259c2842015-03-22 17:39:39 -07001024 *
1025 * @deprecated Use {@link #setWidth(int)} and {@link #setHeight(int)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001026 */
Alan Viverette259c2842015-03-22 17:39:39 -07001027 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001028 public void setWindowLayoutMode(int widthSpec, int heightSpec) {
1029 mWidthMode = widthSpec;
1030 mHeightMode = heightSpec;
1031 }
Alan Viverette5435a302015-01-29 10:25:34 -08001032
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001033 /**
Alan Viverettec129b582016-06-21 11:09:03 -04001034 * Returns the popup's requested height. May be a layout constant such as
1035 * {@link LayoutParams#WRAP_CONTENT} or {@link LayoutParams#MATCH_PARENT}.
1036 * <p>
1037 * The actual size of the popup may depend on other factors such as
1038 * clipping and window layout.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 *
Alan Viverettec129b582016-06-21 11:09:03 -04001040 * @return the popup height in pixels or a layout constant
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001041 * @see #setHeight(int)
1042 */
1043 public int getHeight() {
1044 return mHeight;
1045 }
1046
1047 /**
Alan Viverettec129b582016-06-21 11:09:03 -04001048 * Sets the popup's requested height. May be a layout constant such as
1049 * {@link LayoutParams#WRAP_CONTENT} or {@link LayoutParams#MATCH_PARENT}.
1050 * <p>
1051 * The actual size of the popup may depend on other factors such as
1052 * clipping and window layout.
Alan Viverette259c2842015-03-22 17:39:39 -07001053 * <p>
1054 * If the popup is showing, calling this method will take effect the next
1055 * time the popup is shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 *
Alan Viverettec129b582016-06-21 11:09:03 -04001057 * @param height the popup height in pixels or a layout constant
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001058 * @see #getHeight()
Alan Viverette5435a302015-01-29 10:25:34 -08001059 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001060 */
1061 public void setHeight(int height) {
1062 mHeight = height;
1063 }
1064
1065 /**
Alan Viverettec129b582016-06-21 11:09:03 -04001066 * Returns the popup's requested width. May be a layout constant such as
1067 * {@link LayoutParams#WRAP_CONTENT} or {@link LayoutParams#MATCH_PARENT}.
1068 * <p>
1069 * The actual size of the popup may depend on other factors such as
1070 * clipping and window layout.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001071 *
Alan Viverettec129b582016-06-21 11:09:03 -04001072 * @return the popup width in pixels or a layout constant
Alan Viverette5435a302015-01-29 10:25:34 -08001073 * @see #setWidth(int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001074 */
1075 public int getWidth() {
1076 return mWidth;
1077 }
1078
1079 /**
Alan Viverettec129b582016-06-21 11:09:03 -04001080 * Sets the popup's requested width. May be a layout constant such as
1081 * {@link LayoutParams#WRAP_CONTENT} or {@link LayoutParams#MATCH_PARENT}.
1082 * <p>
1083 * The actual size of the popup may depend on other factors such as
1084 * clipping and window layout.
Alan Viverette259c2842015-03-22 17:39:39 -07001085 * <p>
1086 * If the popup is showing, calling this method will take effect the next
1087 * time the popup is shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001088 *
Alan Viverettec129b582016-06-21 11:09:03 -04001089 * @param width the popup width in pixels or a layout constant
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001090 * @see #getWidth()
1091 * @see #isShowing()
1092 */
1093 public void setWidth(int width) {
1094 mWidth = width;
1095 }
1096
1097 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001098 * Sets whether the popup window should overlap its anchor view when
1099 * displayed as a drop-down.
1100 * <p>
1101 * If the popup is showing, calling this method will take effect only
1102 * the next time the popup is shown.
1103 *
1104 * @param overlapAnchor Whether the popup should overlap its anchor.
1105 *
1106 * @see #getOverlapAnchor()
1107 * @see #isShowing()
1108 */
1109 public void setOverlapAnchor(boolean overlapAnchor) {
1110 mOverlapAnchor = overlapAnchor;
1111 }
1112
1113 /**
1114 * Returns whether the popup window should overlap its anchor view when
1115 * displayed as a drop-down.
1116 *
1117 * @return Whether the popup should overlap its anchor.
1118 *
1119 * @see #setOverlapAnchor(boolean)
1120 */
1121 public boolean getOverlapAnchor() {
1122 return mOverlapAnchor;
1123 }
1124
1125 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126 * <p>Indicate whether this popup window is showing on screen.</p>
1127 *
1128 * @return true if the popup is showing, false otherwise
1129 */
1130 public boolean isShowing() {
1131 return mIsShowing;
1132 }
1133
Felipe Leme4753bb02017-03-22 20:24:00 -07001134 /** @hide */
1135 protected final void setShowing(boolean isShowing) {
1136 mIsShowing = isShowing;
1137 }
1138
1139 /** @hide */
1140 protected final void setDropDown(boolean isDropDown) {
1141 mIsDropdown = isDropDown;
1142 }
1143
1144 /** @hide */
1145 protected final void setTransitioningToDismiss(boolean transitioningToDismiss) {
1146 mIsTransitioningToDismiss = transitioningToDismiss;
1147 }
1148
1149 /** @hide */
1150 protected final boolean isTransitioningToDismiss() {
1151 return mIsTransitioningToDismiss;
1152 }
1153
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001154 /**
1155 * <p>
1156 * Display the content view in a popup window at the specified location. If the popup window
1157 * cannot fit on screen, it will be clipped. See {@link android.view.WindowManager.LayoutParams}
1158 * for more information on how gravity and the x and y parameters are related. Specifying
1159 * a gravity of {@link android.view.Gravity#NO_GRAVITY} is similar to specifying
1160 * <code>Gravity.LEFT | Gravity.TOP</code>.
1161 * </p>
Alan Viverette5435a302015-01-29 10:25:34 -08001162 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001163 * @param parent a parent view to get the {@link android.view.View#getWindowToken()} token from
1164 * @param gravity the gravity which controls the placement of the popup window
1165 * @param x the popup's x location offset
1166 * @param y the popup's y location offset
1167 */
1168 public void showAtLocation(View parent, int gravity, int x, int y) {
Peeyush Agarwal50db7312016-11-30 15:54:32 +00001169 mParentRootView = new WeakReference<>(parent.getRootView());
Adam Powell8ee6d7c2011-09-18 14:59:28 -07001170 showAtLocation(parent.getWindowToken(), gravity, x, y);
1171 }
1172
1173 /**
1174 * Display the content view in a popup window at the specified location.
1175 *
1176 * @param token Window token to use for creating the new window
1177 * @param gravity the gravity which controls the placement of the popup window
1178 * @param x the popup's x location offset
1179 * @param y the popup's y location offset
1180 *
1181 * @hide Internal use only. Applications should use
1182 * {@link #showAtLocation(View, int, int, int)} instead.
1183 */
1184 public void showAtLocation(IBinder token, int gravity, int x, int y) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001185 if (isShowing() || mContentView == null) {
1186 return;
1187 }
1188
Alan Viverettee025ed22015-02-02 11:27:21 -08001189 TransitionManager.endTransitions(mDecorView);
1190
Alan Viverettef50df432016-03-24 14:08:24 -04001191 detachFromAnchor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001192
1193 mIsShowing = true;
1194 mIsDropdown = false;
Robert Carr08516062016-08-23 10:17:54 -07001195 mGravity = gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001196
Alan Viverettee025ed22015-02-02 11:27:21 -08001197 final WindowManager.LayoutParams p = createPopupLayoutParams(token);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001198 preparePopup(p);
Alan Viverettee025ed22015-02-02 11:27:21 -08001199
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001200 p.x = x;
1201 p.y = y;
Alan Viverettee025ed22015-02-02 11:27:21 -08001202
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001203 invokePopup(p);
1204 }
1205
1206 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001207 * Display the content view in a popup window anchored to the bottom-left
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001208 * corner of the anchor view. If there is not enough room on screen to show
1209 * the popup in its entirety, this method tries to find a parent scroll
Alan Viverette75d83792015-01-07 15:51:54 -08001210 * view to scroll. If no parent scroll view can be scrolled, the
1211 * bottom-left corner of the popup is pinned at the top left corner of the
1212 * anchor view.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001213 *
1214 * @param anchor the view on which to pin the popup window
1215 *
1216 * @see #dismiss()
1217 */
1218 public void showAsDropDown(View anchor) {
1219 showAsDropDown(anchor, 0, 0);
1220 }
1221
1222 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001223 * Display the content view in a popup window anchored to the bottom-left
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001224 * corner of the anchor view offset by the specified x and y coordinates.
Alan Viverette75d83792015-01-07 15:51:54 -08001225 * If there is not enough room on screen to show the popup in its entirety,
1226 * this method tries to find a parent scroll view to scroll. If no parent
1227 * scroll view can be scrolled, the bottom-left corner of the popup is
1228 * pinned at the top left corner of the anchor view.
1229 * <p>
1230 * If the view later scrolls to move <code>anchor</code> to a different
1231 * location, the popup will be moved correspondingly.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001232 *
1233 * @param anchor the view on which to pin the popup window
Adam Powell54c94de2013-09-26 15:36:34 -07001234 * @param xoff A horizontal offset from the anchor in pixels
1235 * @param yoff A vertical offset from the anchor in pixels
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001236 *
1237 * @see #dismiss()
1238 */
1239 public void showAsDropDown(View anchor, int xoff, int yoff) {
Adam Powell54c94de2013-09-26 15:36:34 -07001240 showAsDropDown(anchor, xoff, yoff, DEFAULT_ANCHORED_GRAVITY);
1241 }
1242
1243 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001244 * Displays the content view in a popup window anchored to the corner of
1245 * another view. The window is positioned according to the specified
1246 * gravity and offset by the specified x and y coordinates.
1247 * <p>
1248 * If there is not enough room on screen to show the popup in its entirety,
1249 * this method tries to find a parent scroll view to scroll. If no parent
1250 * view can be scrolled, the specified vertical gravity will be ignored and
1251 * the popup will anchor itself such that it is visible.
1252 * <p>
1253 * If the view later scrolls to move <code>anchor</code> to a different
1254 * location, the popup will be moved correspondingly.
Adam Powell54c94de2013-09-26 15:36:34 -07001255 *
1256 * @param anchor the view on which to pin the popup window
1257 * @param xoff A horizontal offset from the anchor in pixels
1258 * @param yoff A vertical offset from the anchor in pixels
1259 * @param gravity Alignment of the popup relative to the anchor
1260 *
1261 * @see #dismiss()
1262 */
1263 public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001264 if (isShowing() || !hasContentView()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001265 return;
1266 }
1267
Alan Viverettee025ed22015-02-02 11:27:21 -08001268 TransitionManager.endTransitions(mDecorView);
1269
Alan Viverettef50df432016-03-24 14:08:24 -04001270 attachToAnchor(anchor, xoff, yoff, gravity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001271
1272 mIsShowing = true;
1273 mIsDropdown = true;
1274
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -08001275 final WindowManager.LayoutParams p =
1276 createPopupLayoutParams(anchor.getApplicationWindowToken());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001277 preparePopup(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001278
Alan Viverettef50df432016-03-24 14:08:24 -04001279 final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff,
Alan Viverette50df07a2017-01-06 14:40:21 -05001280 p.width, p.height, gravity, mAllowScrollingAnchorParent);
Alan Viverettee025ed22015-02-02 11:27:21 -08001281 updateAboveAnchor(aboveAnchor);
Phil Weaver396d5492016-03-22 17:53:50 -07001282 p.accessibilityIdOfAnchor = (anchor != null) ? anchor.getAccessibilityViewId() : -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001283
1284 invokePopup(p);
1285 }
1286
Felipe Leme4753bb02017-03-22 20:24:00 -07001287 /** @hide */
1288 protected final void updateAboveAnchor(boolean aboveAnchor) {
Romain Guy3e141682010-03-08 17:44:40 -08001289 if (aboveAnchor != mAboveAnchor) {
1290 mAboveAnchor = aboveAnchor;
1291
Alan Viverette697804e2015-08-06 12:36:47 -04001292 if (mBackground != null && mBackgroundView != null) {
1293 // If the background drawable provided was a StateListDrawable
1294 // with above-anchor and below-anchor states, use those.
1295 // Otherwise, rely on refreshDrawableState to do the job.
Romain Guy3e141682010-03-08 17:44:40 -08001296 if (mAboveAnchorBackgroundDrawable != null) {
1297 if (mAboveAnchor) {
Alan Viverette697804e2015-08-06 12:36:47 -04001298 mBackgroundView.setBackground(mAboveAnchorBackgroundDrawable);
Romain Guy3e141682010-03-08 17:44:40 -08001299 } else {
Alan Viverette697804e2015-08-06 12:36:47 -04001300 mBackgroundView.setBackground(mBelowAnchorBackgroundDrawable);
Romain Guy3e141682010-03-08 17:44:40 -08001301 }
1302 } else {
Alan Viverette697804e2015-08-06 12:36:47 -04001303 mBackgroundView.refreshDrawableState();
Romain Guy3e141682010-03-08 17:44:40 -08001304 }
1305 }
1306 }
1307 }
1308
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001309 /**
1310 * Indicates whether the popup is showing above (the y coordinate of the popup's bottom
1311 * is less than the y coordinate of the anchor) or below the anchor view (the y coordinate
1312 * of the popup is greater than y coordinate of the anchor's bottom).
1313 *
1314 * The value returned
1315 * by this method is meaningful only after {@link #showAsDropDown(android.view.View)}
1316 * or {@link #showAsDropDown(android.view.View, int, int)} was invoked.
1317 *
1318 * @return True if this popup is showing above the anchor view, false otherwise.
1319 */
1320 public boolean isAboveAnchor() {
1321 return mAboveAnchor;
1322 }
1323
1324 /**
Alan Viverettee025ed22015-02-02 11:27:21 -08001325 * Prepare the popup by embedding it into a new ViewGroup if the background
1326 * drawable is not null. If embedding is required, the layout parameters'
1327 * height is modified to take into account the background's padding.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001328 *
1329 * @param p the layout parameters of the popup's content view
1330 */
1331 private void preparePopup(WindowManager.LayoutParams p) {
Romain Guy448ecf52009-05-14 16:03:42 -07001332 if (mContentView == null || mContext == null || mWindowManager == null) {
1333 throw new IllegalStateException("You must specify a valid content view by "
1334 + "calling setContentView() before attempting to show the popup.");
1335 }
1336
Phil Weaverdd697852017-06-16 15:50:16 -07001337 if (p.accessibilityTitle == null) {
1338 p.accessibilityTitle = mContext.getString(R.string.popup_window_default_title);
1339 }
1340
Alan Viverette8fd949e2015-03-11 12:21:30 -07001341 // The old decor view may be transitioning out. Make sure it finishes
1342 // and cleans up before we try to create another one.
1343 if (mDecorView != null) {
1344 mDecorView.cancelTransitions();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001345 }
Alan Viveretteccb11e12014-07-08 16:04:02 -07001346
Alan Viverette8fd949e2015-03-11 12:21:30 -07001347 // When a background is available, we embed the content view within
1348 // another view that owns the background drawable.
Alan Viverette8fd949e2015-03-11 12:21:30 -07001349 if (mBackground != null) {
Alan Viverette697804e2015-08-06 12:36:47 -04001350 mBackgroundView = createBackgroundView(mContentView);
1351 mBackgroundView.setBackground(mBackground);
Alan Viverette8fd949e2015-03-11 12:21:30 -07001352 } else {
Alan Viverette697804e2015-08-06 12:36:47 -04001353 mBackgroundView = mContentView;
Alan Viverette8fd949e2015-03-11 12:21:30 -07001354 }
1355
Alan Viverette697804e2015-08-06 12:36:47 -04001356 mDecorView = createDecorView(mBackgroundView);
Evan Roskya7f94072017-05-05 18:28:34 -07001357 mDecorView.setIsRootNamespace(true);
Alan Viverette5435a302015-01-29 10:25:34 -08001358
1359 // The background owner should be elevated so that it casts a shadow.
Alan Viverette697804e2015-08-06 12:36:47 -04001360 mBackgroundView.setElevation(mElevation);
Alan Viverette5435a302015-01-29 10:25:34 -08001361
1362 // We may wrap that in another view, so we'll need to manually specify
1363 // the surface insets.
Wale Ogunwale246c2092016-04-07 14:12:44 -07001364 p.setSurfaceInsets(mBackgroundView, true /*manual*/, true /*preservePrevious*/);
Alan Viverette5435a302015-01-29 10:25:34 -08001365
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001366 mPopupViewInitialLayoutDirectionInherited =
Alan Viverette5435a302015-01-29 10:25:34 -08001367 (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001368 }
1369
1370 /**
Alan Viverette5435a302015-01-29 10:25:34 -08001371 * Wraps a content view in a PopupViewContainer.
1372 *
1373 * @param contentView the content view to wrap
1374 * @return a PopupViewContainer that wraps the content view
1375 */
1376 private PopupBackgroundView createBackgroundView(View contentView) {
1377 final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
1378 final int height;
Robert Carr26656042016-04-19 19:10:44 -07001379 if (layoutParams != null && layoutParams.height == WRAP_CONTENT) {
1380 height = WRAP_CONTENT;
Alan Viverette5435a302015-01-29 10:25:34 -08001381 } else {
Robert Carr26656042016-04-19 19:10:44 -07001382 height = MATCH_PARENT;
Alan Viverette5435a302015-01-29 10:25:34 -08001383 }
1384
1385 final PopupBackgroundView backgroundView = new PopupBackgroundView(mContext);
1386 final PopupBackgroundView.LayoutParams listParams = new PopupBackgroundView.LayoutParams(
Robert Carr26656042016-04-19 19:10:44 -07001387 MATCH_PARENT, height);
Alan Viverette5435a302015-01-29 10:25:34 -08001388 backgroundView.addView(contentView, listParams);
1389
1390 return backgroundView;
1391 }
1392
1393 /**
1394 * Wraps a content view in a FrameLayout.
1395 *
1396 * @param contentView the content view to wrap
1397 * @return a FrameLayout that wraps the content view
1398 */
1399 private PopupDecorView createDecorView(View contentView) {
1400 final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
1401 final int height;
Robert Carr26656042016-04-19 19:10:44 -07001402 if (layoutParams != null && layoutParams.height == WRAP_CONTENT) {
1403 height = WRAP_CONTENT;
Alan Viverette5435a302015-01-29 10:25:34 -08001404 } else {
Robert Carr26656042016-04-19 19:10:44 -07001405 height = MATCH_PARENT;
Alan Viverette5435a302015-01-29 10:25:34 -08001406 }
1407
1408 final PopupDecorView decorView = new PopupDecorView(mContext);
Robert Carr26656042016-04-19 19:10:44 -07001409 decorView.addView(contentView, MATCH_PARENT, height);
Alan Viverette5435a302015-01-29 10:25:34 -08001410 decorView.setClipChildren(false);
1411 decorView.setClipToPadding(false);
1412
1413 return decorView;
1414 }
1415
1416 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001417 * <p>Invoke the popup window by adding the content view to the window
1418 * manager.</p>
1419 *
1420 * <p>The content view must be non-null when this method is invoked.</p>
1421 *
1422 * @param p the layout parameters of the popup's content view
1423 */
1424 private void invokePopup(WindowManager.LayoutParams p) {
Romain Guy0c0b7682011-05-16 11:54:09 -07001425 if (mContext != null) {
1426 p.packageName = mContext.getPackageName();
1427 }
Alan Viverette5435a302015-01-29 10:25:34 -08001428
Alan Viverette8fd949e2015-03-11 12:21:30 -07001429 final PopupDecorView decorView = mDecorView;
1430 decorView.setFitsSystemWindows(mLayoutInsetDecor);
Alan Viverette8fd949e2015-03-11 12:21:30 -07001431
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001432 setLayoutDirectionFromAnchor();
Alan Viverette5435a302015-01-29 10:25:34 -08001433
Alan Viverette8fd949e2015-03-11 12:21:30 -07001434 mWindowManager.addView(decorView, p);
Alan Viverette95888c02015-04-16 13:27:50 -07001435
1436 if (mEnterTransition != null) {
1437 decorView.requestEnterTransition(mEnterTransition);
1438 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001439 }
1440
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001441 private void setLayoutDirectionFromAnchor() {
1442 if (mAnchor != null) {
1443 View anchor = mAnchor.get();
1444 if (anchor != null && mPopupViewInitialLayoutDirectionInherited) {
Alan Viverette5435a302015-01-29 10:25:34 -08001445 mDecorView.setLayoutDirection(anchor.getLayoutDirection());
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001446 }
1447 }
1448 }
1449
Robert Carr489c39d2016-06-24 13:58:16 -07001450 private int computeGravity() {
Robert Carr08516062016-08-23 10:17:54 -07001451 int gravity = mGravity == Gravity.NO_GRAVITY ? Gravity.START | Gravity.TOP : mGravity;
1452 if (mIsDropdown && (mClipToScreen || mClippingEnabled)) {
Alan Viverette9705fa062016-06-30 13:17:26 -04001453 gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
Robert Carr489c39d2016-06-24 13:58:16 -07001454 }
1455 return gravity;
1456 }
1457
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001458 /**
1459 * <p>Generate the layout parameters for the popup window.</p>
1460 *
1461 * @param token the window token used to bind the popup's window
1462 *
1463 * @return the layout parameters to pass to the window manager
Felipe Leme4753bb02017-03-22 20:24:00 -07001464 *
1465 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001466 */
Felipe Leme4753bb02017-03-22 20:24:00 -07001467 protected final WindowManager.LayoutParams createPopupLayoutParams(IBinder token) {
Alan Viverettee025ed22015-02-02 11:27:21 -08001468 final WindowManager.LayoutParams p = new WindowManager.LayoutParams();
1469
1470 // These gravity settings put the view at the top left corner of the
1471 // screen. The view is then positioned to the appropriate location by
1472 // setting the x and y offsets to match the anchor's bottom-left
1473 // corner.
Robert Carr489c39d2016-06-24 13:58:16 -07001474 p.gravity = computeGravity();
Alan Viverettee025ed22015-02-02 11:27:21 -08001475 p.flags = computeFlags(p.flags);
1476 p.type = mWindowLayoutType;
1477 p.token = token;
1478 p.softInputMode = mSoftInputMode;
1479 p.windowAnimations = computeAnimationResource();
1480
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001481 if (mBackground != null) {
1482 p.format = mBackground.getOpacity();
1483 } else {
1484 p.format = PixelFormat.TRANSLUCENT;
1485 }
Alan Viverettee025ed22015-02-02 11:27:21 -08001486
1487 if (mHeightMode < 0) {
1488 p.height = mLastHeight = mHeightMode;
1489 } else {
1490 p.height = mLastHeight = mHeight;
1491 }
1492
1493 if (mWidthMode < 0) {
1494 p.width = mLastWidth = mWidthMode;
1495 } else {
1496 p.width = mLastWidth = mWidth;
1497 }
1498
Wale Ogunwale8216eb22015-12-18 10:42:42 -08001499 p.privateFlags = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH
1500 | PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
Robert Carra1eb4392015-12-10 12:43:51 -08001501
Alan Viverettee025ed22015-02-02 11:27:21 -08001502 // Used for debugging.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001503 p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
1504
1505 return p;
1506 }
1507
1508 private int computeFlags(int curFlags) {
1509 curFlags &= ~(
1510 WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES |
1511 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
1512 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
1513 WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
1514 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
Adam Powellba0a2c32010-09-28 17:41:23 -07001515 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
1516 WindowManager.LayoutParams.FLAG_SPLIT_TOUCH);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001517 if(mIgnoreCheekPress) {
1518 curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
1519 }
1520 if (!mFocusable) {
1521 curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1522 if (mInputMethodMode == INPUT_METHOD_NEEDED) {
1523 curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1524 }
1525 } else if (mInputMethodMode == INPUT_METHOD_NOT_NEEDED) {
1526 curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1527 }
1528 if (!mTouchable) {
1529 curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
1530 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001531 if (mOutsideTouchable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001532 curFlags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
1533 }
Robert Carrcb8dcec2016-06-13 19:12:13 -07001534 if (!mClippingEnabled || mClipToScreen) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001535 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
1536 }
Jeff Brown46e75292010-11-10 16:53:45 -08001537 if (isSplitTouchEnabled()) {
Jeff Brown01ce2e92010-09-26 22:20:12 -07001538 curFlags |= WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
1539 }
Adam Powellba0a2c32010-09-28 17:41:23 -07001540 if (mLayoutInScreen) {
1541 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
1542 }
Adam Powell0bd1d0a2011-07-22 19:35:06 -07001543 if (mLayoutInsetDecor) {
1544 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
1545 }
Adam Powelle0b6cd12011-09-28 22:06:11 -07001546 if (mNotTouchModal) {
1547 curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
1548 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -07001549 if (mAttachedInDecor) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001550 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR;
Wale Ogunwale393b1c12014-10-18 16:22:01 -07001551 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001552 return curFlags;
1553 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -07001554
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001555 private int computeAnimationResource() {
Alan Viverette5435a302015-01-29 10:25:34 -08001556 if (mAnimationStyle == ANIMATION_STYLE_DEFAULT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001557 if (mIsDropdown) {
1558 return mAboveAnchor
1559 ? com.android.internal.R.style.Animation_DropDownUp
1560 : com.android.internal.R.style.Animation_DropDownDown;
1561 }
1562 return 0;
1563 }
1564 return mAnimationStyle;
1565 }
Alan Viverette560f1702014-05-05 14:40:07 -07001566
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001567 /**
Alan Viverette560f1702014-05-05 14:40:07 -07001568 * Positions the popup window on screen. When the popup window is too tall
1569 * to fit under the anchor, a parent scroll view is seeked and scrolled up
1570 * to reclaim space. If scrolling is not possible or not enough, the popup
1571 * window gets moved on top of the anchor.
1572 * <p>
Alan Viverettef50df432016-03-24 14:08:24 -04001573 * The results of positioning are placed in {@code outParams}.
Alan Viverette5435a302015-01-29 10:25:34 -08001574 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001575 * @param anchor the view on which the popup window must be anchored
Alan Viverettef50df432016-03-24 14:08:24 -04001576 * @param outParams the layout parameters used to display the drop down
Alan Viverette9125fba2016-04-04 15:07:18 -04001577 * @param xOffset absolute horizontal offset from the left of the anchor
Alan Viverette6acf2f92016-03-24 17:11:37 -04001578 * @param yOffset absolute vertical offset from the top of the anchor
Alan Viverette560f1702014-05-05 14:40:07 -07001579 * @param gravity horizontal gravity specifying popup alignment
Alan Viverette50df07a2017-01-06 14:40:21 -05001580 * @param allowScroll whether the anchor view's parent may be scrolled
1581 * when the popup window doesn't fit on screen
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001582 * @return true if the popup is translated upwards to fit on screen
Felipe Leme4753bb02017-03-22 20:24:00 -07001583 *
1584 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001585 */
Dake Gu9f879a22018-01-25 18:08:08 -08001586 protected boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams,
Alan Viverette50df07a2017-01-06 14:40:21 -05001587 int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) {
Adam Powell62e2bde2011-08-15 15:50:05 -07001588 final int anchorHeight = anchor.getHeight();
Alan Viverette560f1702014-05-05 14:40:07 -07001589 final int anchorWidth = anchor.getWidth();
1590 if (mOverlapAnchor) {
Alan Viverettef50df432016-03-24 14:08:24 -04001591 yOffset -= anchorHeight;
Alan Viverette560f1702014-05-05 14:40:07 -07001592 }
1593
Alan Viverette6acf2f92016-03-24 17:11:37 -04001594 // Initially, align to the bottom-left corner of the anchor plus offsets.
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -08001595 final int[] appScreenLocation = mTmpAppLocation;
1596 final View appRootView = getAppRootView(anchor);
1597 appRootView.getLocationOnScreen(appScreenLocation);
1598
1599 final int[] screenLocation = mTmpScreenLocation;
1600 anchor.getLocationOnScreen(screenLocation);
1601
Alan Viverettef50df432016-03-24 14:08:24 -04001602 final int[] drawingLocation = mTmpDrawingLocation;
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -08001603 drawingLocation[0] = screenLocation[0] - appScreenLocation[0];
1604 drawingLocation[1] = screenLocation[1] - appScreenLocation[1];
Alan Viverettef50df432016-03-24 14:08:24 -04001605 outParams.x = drawingLocation[0] + xOffset;
1606 outParams.y = drawingLocation[1] + anchorHeight + yOffset;
Adam Powell54c94de2013-09-26 15:36:34 -07001607
Robert Carrf6e801d2016-04-12 14:33:18 -07001608 final Rect displayFrame = new Rect();
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -08001609 appRootView.getWindowVisibleDisplayFrame(displayFrame);
Robert Carr26656042016-04-19 19:10:44 -07001610 if (width == MATCH_PARENT) {
Robert Carrf6e801d2016-04-12 14:33:18 -07001611 width = displayFrame.right - displayFrame.left;
1612 }
Robert Carr26656042016-04-19 19:10:44 -07001613 if (height == MATCH_PARENT) {
Robert Carrf6e801d2016-04-12 14:33:18 -07001614 height = displayFrame.bottom - displayFrame.top;
1615 }
1616
Robert Carrcb8dcec2016-06-13 19:12:13 -07001617 // Let the window manager know to align the top to y.
Robert Carr8367c502016-07-12 15:48:53 -07001618 outParams.gravity = computeGravity();
Robert Carrcb8dcec2016-06-13 19:12:13 -07001619 outParams.width = width;
1620 outParams.height = height;
1621
Alan Viverette6acf2f92016-03-24 17:11:37 -04001622 // If we need to adjust for gravity RIGHT, align to the bottom-right
1623 // corner of the anchor (still accounting for offsets).
Alan Viverette560f1702014-05-05 14:40:07 -07001624 final int hgrav = Gravity.getAbsoluteGravity(gravity, anchor.getLayoutDirection())
1625 & Gravity.HORIZONTAL_GRAVITY_MASK;
Adam Powell54c94de2013-09-26 15:36:34 -07001626 if (hgrav == Gravity.RIGHT) {
Alan Viverettef50df432016-03-24 14:08:24 -04001627 outParams.x -= width - anchorWidth;
Adam Powell54c94de2013-09-26 15:36:34 -07001628 }
Alan Viverette560f1702014-05-05 14:40:07 -07001629
Alan Viverette9125fba2016-04-04 15:07:18 -04001630 // First, attempt to fit the popup vertically without resizing.
1631 final boolean fitsVertical = tryFitVertical(outParams, yOffset, height,
1632 anchorHeight, drawingLocation[1], screenLocation[1], displayFrame.top,
1633 displayFrame.bottom, false);
Alan Viverette6acf2f92016-03-24 17:11:37 -04001634
Alan Viverette9125fba2016-04-04 15:07:18 -04001635 // Next, attempt to fit the popup horizontally without resizing.
1636 final boolean fitsHorizontal = tryFitHorizontal(outParams, xOffset, width,
1637 anchorWidth, drawingLocation[0], screenLocation[0], displayFrame.left,
1638 displayFrame.right, false);
Romain Guy3e141682010-03-08 17:44:40 -08001639
Alan Viverette9125fba2016-04-04 15:07:18 -04001640 // If the popup still doesn't fit, attempt to scroll the parent.
1641 if (!fitsVertical || !fitsHorizontal) {
1642 final int scrollX = anchor.getScrollX();
1643 final int scrollY = anchor.getScrollY();
1644 final Rect r = new Rect(scrollX, scrollY, scrollX + width + xOffset,
1645 scrollY + height + anchorHeight + yOffset);
Alan Viverette50df07a2017-01-06 14:40:21 -05001646 if (allowScroll && anchor.requestRectangleOnScreen(r, true)) {
Alan Viverette9125fba2016-04-04 15:07:18 -04001647 // Reset for the new anchor position.
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -08001648 anchor.getLocationOnScreen(screenLocation);
1649 drawingLocation[0] = screenLocation[0] - appScreenLocation[0];
1650 drawingLocation[1] = screenLocation[1] - appScreenLocation[1];
Alan Viverette9125fba2016-04-04 15:07:18 -04001651 outParams.x = drawingLocation[0] + xOffset;
1652 outParams.y = drawingLocation[1] + anchorHeight + yOffset;
Adam Powell54c94de2013-09-26 15:36:34 -07001653
Alan Viverette9125fba2016-04-04 15:07:18 -04001654 // Preserve the gravity adjustment.
1655 if (hgrav == Gravity.RIGHT) {
1656 outParams.x -= width - anchorWidth;
Oren Blasberged391262015-09-01 12:12:51 -07001657 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001658 }
Alan Viverette9125fba2016-04-04 15:07:18 -04001659
1660 // Try to fit the popup again and allowing resizing.
1661 tryFitVertical(outParams, yOffset, height, anchorHeight, drawingLocation[1],
1662 screenLocation[1], displayFrame.top, displayFrame.bottom, mClipToScreen);
1663 tryFitHorizontal(outParams, xOffset, width, anchorWidth, drawingLocation[0],
1664 screenLocation[0], displayFrame.left, displayFrame.right, mClipToScreen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001665 }
1666
Alan Viverette9125fba2016-04-04 15:07:18 -04001667 // Return whether the popup's top edge is above the anchor's top edge.
1668 return outParams.y < drawingLocation[1];
1669 }
Alan Viverettef50df432016-03-24 14:08:24 -04001670
Alan Viverette9125fba2016-04-04 15:07:18 -04001671 private boolean tryFitVertical(@NonNull LayoutParams outParams, int yOffset, int height,
1672 int anchorHeight, int drawingLocationY, int screenLocationY, int displayFrameTop,
1673 int displayFrameBottom, boolean allowResize) {
Alan Viverette55f0a992016-04-19 10:17:00 -04001674 final int winOffsetY = screenLocationY - drawingLocationY;
Vladislav Kaznacheev3b105d92017-05-11 23:49:52 +00001675 final int anchorTopInScreen = outParams.y + winOffsetY;
1676 final int spaceBelow = displayFrameBottom - anchorTopInScreen;
1677 if (anchorTopInScreen >= 0 && height <= spaceBelow) {
Alan Viverette9125fba2016-04-04 15:07:18 -04001678 return true;
Adam Powell56c2d332010-11-05 20:03:03 -07001679 }
1680
Vladislav Kaznacheev3b105d92017-05-11 23:49:52 +00001681 final int spaceAbove = anchorTopInScreen - anchorHeight - displayFrameTop;
Alan Viverette9125fba2016-04-04 15:07:18 -04001682 if (height <= spaceAbove) {
1683 // Move everything up.
1684 if (mOverlapAnchor) {
Vladislav Kaznacheev3b105d92017-05-11 23:49:52 +00001685 yOffset += anchorHeight;
Alan Viverette9125fba2016-04-04 15:07:18 -04001686 }
1687 outParams.y = drawingLocationY - height + yOffset;
1688
1689 return true;
1690 }
1691
1692 if (positionInDisplayVertical(outParams, height, drawingLocationY, screenLocationY,
1693 displayFrameTop, displayFrameBottom, allowResize)) {
1694 return true;
1695 }
1696
1697 return false;
1698 }
1699
1700 private boolean positionInDisplayVertical(@NonNull LayoutParams outParams, int height,
1701 int drawingLocationY, int screenLocationY, int displayFrameTop, int displayFrameBottom,
1702 boolean canResize) {
1703 boolean fitsInDisplay = true;
1704
1705 final int winOffsetY = screenLocationY - drawingLocationY;
1706 outParams.y += winOffsetY;
Alan Viverettef50df432016-03-24 14:08:24 -04001707 outParams.height = height;
Alan Viverette560f1702014-05-05 14:40:07 -07001708
Alan Viverette9125fba2016-04-04 15:07:18 -04001709 final int bottom = outParams.y + height;
1710 if (bottom > displayFrameBottom) {
1711 // The popup is too far down, move it back in.
1712 outParams.y -= bottom - displayFrameBottom;
1713 }
1714
1715 if (outParams.y < displayFrameTop) {
1716 // The popup is too far up, move it back in and clip if
1717 // it's still too large.
1718 outParams.y = displayFrameTop;
1719
1720 final int displayFrameHeight = displayFrameBottom - displayFrameTop;
1721 if (canResize && height > displayFrameHeight) {
1722 outParams.height = displayFrameHeight;
1723 } else {
1724 fitsInDisplay = false;
1725 }
1726 }
1727
1728 outParams.y -= winOffsetY;
1729
1730 return fitsInDisplay;
1731 }
1732
1733 private boolean tryFitHorizontal(@NonNull LayoutParams outParams, int xOffset, int width,
1734 int anchorWidth, int drawingLocationX, int screenLocationX, int displayFrameLeft,
1735 int displayFrameRight, boolean allowResize) {
Alan Viverette55f0a992016-04-19 10:17:00 -04001736 final int winOffsetX = screenLocationX - drawingLocationX;
1737 final int anchorLeftInScreen = outParams.x + winOffsetX;
Alan Viverette9125fba2016-04-04 15:07:18 -04001738 final int spaceRight = displayFrameRight - anchorLeftInScreen;
Alan Viverette55f0a992016-04-19 10:17:00 -04001739 if (anchorLeftInScreen >= 0 && width <= spaceRight) {
Alan Viverette9125fba2016-04-04 15:07:18 -04001740 return true;
1741 }
1742
1743 if (positionInDisplayHorizontal(outParams, width, drawingLocationX, screenLocationX,
1744 displayFrameLeft, displayFrameRight, allowResize)) {
1745 return true;
1746 }
1747
1748 return false;
1749 }
1750
1751 private boolean positionInDisplayHorizontal(@NonNull LayoutParams outParams, int width,
1752 int drawingLocationX, int screenLocationX, int displayFrameLeft, int displayFrameRight,
1753 boolean canResize) {
1754 boolean fitsInDisplay = true;
1755
1756 // Use screen coordinates for comparison against display frame.
1757 final int winOffsetX = screenLocationX - drawingLocationX;
1758 outParams.x += winOffsetX;
1759
1760 final int right = outParams.x + width;
1761 if (right > displayFrameRight) {
1762 // The popup is too far right, move it back in.
1763 outParams.x -= right - displayFrameRight;
1764 }
1765
1766 if (outParams.x < displayFrameLeft) {
1767 // The popup is too far left, move it back in and clip if it's
1768 // still too large.
1769 outParams.x = displayFrameLeft;
1770
1771 final int displayFrameWidth = displayFrameRight - displayFrameLeft;
1772 if (canResize && width > displayFrameWidth) {
1773 outParams.width = displayFrameWidth;
1774 } else {
1775 fitsInDisplay = false;
1776 }
1777 }
1778
1779 outParams.x -= winOffsetX;
1780
1781 return fitsInDisplay;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001782 }
Alan Viverette5435a302015-01-29 10:25:34 -08001783
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001784 /**
1785 * Returns the maximum height that is available for the popup to be
1786 * completely shown. It is recommended that this height be the maximum for
1787 * the popup's height, otherwise it is possible that the popup will be
1788 * clipped.
Alan Viverette5435a302015-01-29 10:25:34 -08001789 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001790 * @param anchor The view on which the popup window must be anchored.
1791 * @return The maximum available height for the popup to be completely
1792 * shown.
1793 */
Alan Viveretteb854d072015-09-28 16:12:18 -04001794 public int getMaxAvailableHeight(@NonNull View anchor) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001795 return getMaxAvailableHeight(anchor, 0);
1796 }
1797
1798 /**
1799 * Returns the maximum height that is available for the popup to be
1800 * completely shown. It is recommended that this height be the maximum for
1801 * the popup's height, otherwise it is possible that the popup will be
1802 * clipped.
1803 *
1804 * @param anchor The view on which the popup window must be anchored.
1805 * @param yOffset y offset from the view's bottom edge
1806 * @return The maximum available height for the popup to be completely
1807 * shown.
1808 */
Alan Viveretteb854d072015-09-28 16:12:18 -04001809 public int getMaxAvailableHeight(@NonNull View anchor, int yOffset) {
Mike LeBeau98acd542009-05-07 19:04:39 -07001810 return getMaxAvailableHeight(anchor, yOffset, false);
1811 }
Alan Viverette5435a302015-01-29 10:25:34 -08001812
Mike LeBeau98acd542009-05-07 19:04:39 -07001813 /**
1814 * Returns the maximum height that is available for the popup to be
1815 * completely shown, optionally ignoring any bottom decorations such as
1816 * the input method. It is recommended that this height be the maximum for
1817 * the popup's height, otherwise it is possible that the popup will be
1818 * clipped.
Alan Viverette5435a302015-01-29 10:25:34 -08001819 *
Mike LeBeau98acd542009-05-07 19:04:39 -07001820 * @param anchor The view on which the popup window must be anchored.
1821 * @param yOffset y offset from the view's bottom edge
1822 * @param ignoreBottomDecorations if true, the height returned will be
1823 * all the way to the bottom of the display, ignoring any
1824 * bottom decorations
1825 * @return The maximum available height for the popup to be completely
1826 * shown.
Mike LeBeau98acd542009-05-07 19:04:39 -07001827 */
Alan Viveretteb854d072015-09-28 16:12:18 -04001828 public int getMaxAvailableHeight(
1829 @NonNull View anchor, int yOffset, boolean ignoreBottomDecorations) {
Robert Carr701d7302016-09-06 19:07:39 -07001830 Rect displayFrame = null;
1831 final Rect visibleDisplayFrame = new Rect();
1832
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -08001833 final View appView = getAppRootView(anchor);
1834 appView.getWindowVisibleDisplayFrame(visibleDisplayFrame);
Jorim Jaggi81758462016-02-29 14:41:09 +01001835 if (ignoreBottomDecorations) {
Robert Carr701d7302016-09-06 19:07:39 -07001836 // In the ignore bottom decorations case we want to
1837 // still respect all other decorations so we use the inset visible
1838 // frame on the top right and left and take the bottom
1839 // value from the full frame.
1840 displayFrame = new Rect();
Jorim Jaggi81758462016-02-29 14:41:09 +01001841 anchor.getWindowDisplayFrame(displayFrame);
Robert Carr701d7302016-09-06 19:07:39 -07001842 displayFrame.top = visibleDisplayFrame.top;
1843 displayFrame.right = visibleDisplayFrame.right;
1844 displayFrame.left = visibleDisplayFrame.left;
Jorim Jaggi81758462016-02-29 14:41:09 +01001845 } else {
Robert Carr701d7302016-09-06 19:07:39 -07001846 displayFrame = visibleDisplayFrame;
Jorim Jaggi81758462016-02-29 14:41:09 +01001847 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001848
Alan Viverettef50df432016-03-24 14:08:24 -04001849 final int[] anchorPos = mTmpDrawingLocation;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001850 anchor.getLocationOnScreen(anchorPos);
Alan Viverette5435a302015-01-29 10:25:34 -08001851
Jorim Jaggi81758462016-02-29 14:41:09 +01001852 final int bottomEdge = displayFrame.bottom;
Oren Blasberged391262015-09-01 12:12:51 -07001853
1854 final int distanceToBottom;
1855 if (mOverlapAnchor) {
1856 distanceToBottom = bottomEdge - anchorPos[1] - yOffset;
1857 } else {
1858 distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
1859 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001860 final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
1861
1862 // anchorPos[1] is distance from anchor to top of screen
1863 int returnedHeight = Math.max(distanceToBottom, distanceToTop);
1864 if (mBackground != null) {
1865 mBackground.getPadding(mTempRect);
Alan Viverette5435a302015-01-29 10:25:34 -08001866 returnedHeight -= mTempRect.top + mTempRect.bottom;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001867 }
Alan Viverette5435a302015-01-29 10:25:34 -08001868
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001869 return returnedHeight;
1870 }
Alan Viverette5435a302015-01-29 10:25:34 -08001871
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001872 /**
Alan Viverette7878edf2015-02-03 15:49:18 -08001873 * Disposes of the popup window. This method can be invoked only after
1874 * {@link #showAsDropDown(android.view.View)} has been executed. Failing
1875 * that, calling this method will have no effect.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001876 *
Alan Viverette5435a302015-01-29 10:25:34 -08001877 * @see #showAsDropDown(android.view.View)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001878 */
1879 public void dismiss() {
Felipe Leme4753bb02017-03-22 20:24:00 -07001880 if (!isShowing() || isTransitioningToDismiss()) {
Alan Viverettee025ed22015-02-02 11:27:21 -08001881 return;
1882 }
Svetoslav Ganov06f938e2011-11-10 14:31:37 -08001883
Alan Viverette8fd949e2015-03-11 12:21:30 -07001884 final PopupDecorView decorView = mDecorView;
1885 final View contentView = mContentView;
1886
1887 final ViewGroup contentHolder;
1888 final ViewParent contentParent = contentView.getParent();
1889 if (contentParent instanceof ViewGroup) {
1890 contentHolder = ((ViewGroup) contentParent);
1891 } else {
1892 contentHolder = null;
1893 }
1894
1895 // Ensure any ongoing or pending transitions are canceled.
1896 decorView.cancelTransitions();
1897
Alan Viverettee025ed22015-02-02 11:27:21 -08001898 mIsShowing = false;
Alan Viverette8fd949e2015-03-11 12:21:30 -07001899 mIsTransitioningToDismiss = true;
Craig Mautnerb82d0742012-05-23 08:48:39 -07001900
Alan Viverette634a8082016-02-03 14:22:41 -05001901 // This method may be called as part of window detachment, in which
1902 // case the anchor view (and its root) will still return true from
1903 // isAttachedToWindow() during execution of this method; however, we
1904 // can expect the OnAttachStateChangeListener to have been called prior
1905 // to executing this method, so we can rely on that instead.
Alan Viverette95888c02015-04-16 13:27:50 -07001906 final Transition exitTransition = mExitTransition;
Alan Viverette054c1722016-12-01 13:33:44 -05001907 if (exitTransition != null && decorView.isLaidOut()
1908 && (mIsAnchorRootAttached || mAnchorRoot == null)) {
Yohei Yukawadbd299d2016-03-06 22:27:40 -08001909 // The decor view is non-interactive and non-IME-focusable during exit transitions.
Alan Viverette95888c02015-04-16 13:27:50 -07001910 final LayoutParams p = (LayoutParams) decorView.getLayoutParams();
1911 p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
1912 p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
Yohei Yukawadbd299d2016-03-06 22:27:40 -08001913 p.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
Alan Viverette95888c02015-04-16 13:27:50 -07001914 mWindowManager.updateViewLayout(decorView, p);
1915
Alan Viverette054c1722016-12-01 13:33:44 -05001916 final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
1917 final Rect epicenter = getTransitionEpicenter();
1918
Alan Viverette634a8082016-02-03 14:22:41 -05001919 // Once we start dismissing the decor view, all state (including
1920 // the anchor root) needs to be moved to the decor view since we
1921 // may open another popup while it's busy exiting.
Alan Viverette054c1722016-12-01 13:33:44 -05001922 decorView.startExitTransition(exitTransition, anchorRoot, epicenter,
Alan Viverette634a8082016-02-03 14:22:41 -05001923 new TransitionListenerAdapter() {
1924 @Override
1925 public void onTransitionEnd(Transition transition) {
Alan Viverette79708942016-02-25 16:57:08 -05001926 dismissImmediate(decorView, contentHolder, contentView);
Alan Viverette634a8082016-02-03 14:22:41 -05001927 }
1928 });
Alan Viverettee025ed22015-02-02 11:27:21 -08001929 } else {
Alan Viverette79708942016-02-25 16:57:08 -05001930 dismissImmediate(decorView, contentHolder, contentView);
Alan Viverette7878edf2015-02-03 15:49:18 -08001931 }
1932
Alan Viverette95888c02015-04-16 13:27:50 -07001933 // Clears the anchor view.
Alan Viverettef50df432016-03-24 14:08:24 -04001934 detachFromAnchor();
Alan Viverette79708942016-02-25 16:57:08 -05001935
1936 if (mOnDismissListener != null) {
1937 mOnDismissListener.onDismiss();
1938 }
Alan Viverette5435a302015-01-29 10:25:34 -08001939 }
1940
Alan Viverette91098572016-01-19 14:07:31 -05001941 /**
1942 * Returns the window-relative epicenter bounds to be used by enter and
1943 * exit transitions.
1944 * <p>
1945 * <strong>Note:</strong> This is distinct from the rect passed to
1946 * {@link #setEpicenterBounds(Rect)}, which is anchor-relative.
1947 *
1948 * @return the window-relative epicenter bounds to be used by enter and
1949 * exit transitions
Felipe Leme4753bb02017-03-22 20:24:00 -07001950 *
1951 * @hide
Alan Viverette91098572016-01-19 14:07:31 -05001952 */
Felipe Leme4753bb02017-03-22 20:24:00 -07001953 protected final Rect getTransitionEpicenter() {
Alan Viverette95888c02015-04-16 13:27:50 -07001954 final View anchor = mAnchor != null ? mAnchor.get() : null;
1955 final View decor = mDecorView;
1956 if (anchor == null || decor == null) {
1957 return null;
1958 }
1959
1960 final int[] anchorLocation = anchor.getLocationOnScreen();
1961 final int[] popupLocation = mDecorView.getLocationOnScreen();
1962
1963 // Compute the position of the anchor relative to the popup.
1964 final Rect bounds = new Rect(0, 0, anchor.getWidth(), anchor.getHeight());
1965 bounds.offset(anchorLocation[0] - popupLocation[0], anchorLocation[1] - popupLocation[1]);
Alan Viverette91098572016-01-19 14:07:31 -05001966
1967 // Use anchor-relative epicenter, if specified.
1968 if (mEpicenterBounds != null) {
1969 final int offsetX = bounds.left;
1970 final int offsetY = bounds.top;
1971 bounds.set(mEpicenterBounds);
1972 bounds.offset(offsetX, offsetY);
1973 }
1974
Alan Viverette95888c02015-04-16 13:27:50 -07001975 return bounds;
1976 }
1977
Alan Viverette5435a302015-01-29 10:25:34 -08001978 /**
1979 * Removes the popup from the window manager and tears down the supporting
1980 * view hierarchy, if necessary.
1981 */
Alan Viverette79708942016-02-25 16:57:08 -05001982 private void dismissImmediate(View decorView, ViewGroup contentHolder, View contentView) {
Alan Viverette8fd949e2015-03-11 12:21:30 -07001983 // If this method gets called and the decor view doesn't have a parent,
1984 // then it was either never added or was already removed. That should
1985 // never happen, but it's worth checking to avoid potential crashes.
1986 if (decorView.getParent() != null) {
1987 mWindowManager.removeViewImmediate(decorView);
Alan Viverettedf4639a02015-03-02 12:40:34 -08001988 }
1989
Alan Viverette8fd949e2015-03-11 12:21:30 -07001990 if (contentHolder != null) {
1991 contentHolder.removeView(contentView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001992 }
Alan Viverette8fd949e2015-03-11 12:21:30 -07001993
1994 // This needs to stay until after all transitions have ended since we
1995 // need the reference to cancel transitions in preparePopup().
1996 mDecorView = null;
Alan Viverette697804e2015-08-06 12:36:47 -04001997 mBackgroundView = null;
Alan Viverette8fd949e2015-03-11 12:21:30 -07001998 mIsTransitioningToDismiss = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001999 }
2000
2001 /**
2002 * Sets the listener to be called when the window is dismissed.
Alan Viverette5435a302015-01-29 10:25:34 -08002003 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002004 * @param onDismissListener The listener.
2005 */
2006 public void setOnDismissListener(OnDismissListener onDismissListener) {
2007 mOnDismissListener = onDismissListener;
2008 }
Alan Viverette5435a302015-01-29 10:25:34 -08002009
Felipe Leme4753bb02017-03-22 20:24:00 -07002010 /** @hide */
2011 protected final OnDismissListener getOnDismissListener() {
2012 return mOnDismissListener;
2013 }
2014
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002015 /**
2016 * Updates the state of the popup window, if it is currently being displayed,
Alan Viverette259c2842015-03-22 17:39:39 -07002017 * from the currently set state.
2018 * <p>
2019 * This includes:
2020 * <ul>
2021 * <li>{@link #setClippingEnabled(boolean)}</li>
2022 * <li>{@link #setFocusable(boolean)}</li>
2023 * <li>{@link #setIgnoreCheekPress()}</li>
2024 * <li>{@link #setInputMethodMode(int)}</li>
2025 * <li>{@link #setTouchable(boolean)}</li>
2026 * <li>{@link #setAnimationStyle(int)}</li>
2027 * </ul>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002028 */
2029 public void update() {
Felipe Leme4753bb02017-03-22 20:24:00 -07002030 if (!isShowing() || !hasContentView()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002031 return;
2032 }
Alan Viverette5435a302015-01-29 10:25:34 -08002033
Felipe Leme4753bb02017-03-22 20:24:00 -07002034 final WindowManager.LayoutParams p = getDecorViewLayoutParams();
Alan Viverette5435a302015-01-29 10:25:34 -08002035
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002036 boolean update = false;
Alan Viverette5435a302015-01-29 10:25:34 -08002037
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002038 final int newAnim = computeAnimationResource();
2039 if (newAnim != p.windowAnimations) {
2040 p.windowAnimations = newAnim;
2041 update = true;
2042 }
2043
2044 final int newFlags = computeFlags(p.flags);
2045 if (newFlags != p.flags) {
2046 p.flags = newFlags;
2047 update = true;
2048 }
Fabrice Di Megliob003e282012-10-17 17:20:19 -07002049
Robert Carr489c39d2016-06-24 13:58:16 -07002050 final int newGravity = computeGravity();
2051 if (newGravity != p.gravity) {
2052 p.gravity = newGravity;
2053 update = true;
2054 }
2055
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002056 if (update) {
Felipe Leme6f797172017-03-27 15:05:35 -07002057 update(mAnchor != null ? mAnchor.get() : null, p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002058 }
2059 }
Romain Guyd6a463a2009-05-21 23:10:10 -07002060
Felipe Leme4753bb02017-03-22 20:24:00 -07002061 /** @hide */
2062 protected void update(View anchor, WindowManager.LayoutParams params) {
2063 setLayoutDirectionFromAnchor();
2064 mWindowManager.updateViewLayout(mDecorView, params);
2065 }
2066
Romain Guyd6a463a2009-05-21 23:10:10 -07002067 /**
Alan Viverette259c2842015-03-22 17:39:39 -07002068 * Updates the dimension of the popup window.
2069 * <p>
2070 * Calling this function also updates the window with the current popup
2071 * state as described for {@link #update()}.
Romain Guyd6a463a2009-05-21 23:10:10 -07002072 *
Alan Viverettec129b582016-06-21 11:09:03 -04002073 * @param width the new width in pixels, must be >= 0 or -1 to ignore
2074 * @param height the new height in pixels, must be >= 0 or -1 to ignore
Romain Guyd6a463a2009-05-21 23:10:10 -07002075 */
2076 public void update(int width, int height) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002077 final WindowManager.LayoutParams p = getDecorViewLayoutParams();
Romain Guyd6a463a2009-05-21 23:10:10 -07002078 update(p.x, p.y, width, height, false);
2079 }
Alan Viverette5435a302015-01-29 10:25:34 -08002080
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002081 /**
Alan Viverette259c2842015-03-22 17:39:39 -07002082 * Updates the position and the dimension of the popup window.
2083 * <p>
2084 * Width and height can be set to -1 to update location only. Calling this
2085 * function also updates the window with the current popup state as
2086 * described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002087 *
2088 * @param x the new x location
2089 * @param y the new y location
Alan Viverettec129b582016-06-21 11:09:03 -04002090 * @param width the new width in pixels, must be >= 0 or -1 to ignore
2091 * @param height the new height in pixels, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002092 */
2093 public void update(int x, int y, int width, int height) {
2094 update(x, y, width, height, false);
2095 }
2096
2097 /**
Alan Viverette259c2842015-03-22 17:39:39 -07002098 * Updates the position and the dimension of the popup window.
2099 * <p>
2100 * Width and height can be set to -1 to update location only. Calling this
2101 * function also updates the window with the current popup state as
2102 * described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002103 *
2104 * @param x the new x location
2105 * @param y the new y location
Alan Viverettec129b582016-06-21 11:09:03 -04002106 * @param width the new width in pixels, must be >= 0 or -1 to ignore
2107 * @param height the new height in pixels, must be >= 0 or -1 to ignore
Alan Viverette259c2842015-03-22 17:39:39 -07002108 * @param force {@code true} to reposition the window even if the specified
2109 * position already seems to correspond to the LayoutParams,
2110 * {@code false} to only reposition if needed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002111 */
2112 public void update(int x, int y, int width, int height, boolean force) {
Alan Viverette259c2842015-03-22 17:39:39 -07002113 if (width >= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002114 mLastWidth = width;
2115 setWidth(width);
2116 }
2117
Alan Viverette259c2842015-03-22 17:39:39 -07002118 if (height >= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002119 mLastHeight = height;
2120 setHeight(height);
2121 }
2122
Felipe Leme4753bb02017-03-22 20:24:00 -07002123 if (!isShowing() || !hasContentView()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002124 return;
2125 }
2126
Felipe Leme4753bb02017-03-22 20:24:00 -07002127 final WindowManager.LayoutParams p = getDecorViewLayoutParams();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002128
2129 boolean update = force;
2130
2131 final int finalWidth = mWidthMode < 0 ? mWidthMode : mLastWidth;
2132 if (width != -1 && p.width != finalWidth) {
2133 p.width = mLastWidth = finalWidth;
2134 update = true;
2135 }
2136
2137 final int finalHeight = mHeightMode < 0 ? mHeightMode : mLastHeight;
2138 if (height != -1 && p.height != finalHeight) {
2139 p.height = mLastHeight = finalHeight;
2140 update = true;
2141 }
2142
2143 if (p.x != x) {
2144 p.x = x;
2145 update = true;
2146 }
2147
2148 if (p.y != y) {
2149 p.y = y;
2150 update = true;
2151 }
2152
2153 final int newAnim = computeAnimationResource();
2154 if (newAnim != p.windowAnimations) {
2155 p.windowAnimations = newAnim;
2156 update = true;
2157 }
2158
2159 final int newFlags = computeFlags(p.flags);
2160 if (newFlags != p.flags) {
2161 p.flags = newFlags;
2162 update = true;
2163 }
Mike LeBeau98acd542009-05-07 19:04:39 -07002164
Robert Carr489c39d2016-06-24 13:58:16 -07002165 final int newGravity = computeGravity();
2166 if (newGravity != p.gravity) {
2167 p.gravity = newGravity;
2168 update = true;
2169 }
2170
Felipe Lemedd237772017-03-27 09:33:23 -07002171 View anchor = null;
2172 int newAccessibilityIdOfAnchor = -1;
2173
2174 if (mAnchor != null && mAnchor.get() != null) {
2175 anchor = mAnchor.get();
2176 newAccessibilityIdOfAnchor = anchor.getAccessibilityViewId();
2177 }
2178
Phil Weaver396d5492016-03-22 17:53:50 -07002179 if (newAccessibilityIdOfAnchor != p.accessibilityIdOfAnchor) {
2180 p.accessibilityIdOfAnchor = newAccessibilityIdOfAnchor;
2181 update = true;
2182 }
2183
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002184 if (update) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002185 update(anchor, p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002186 }
2187 }
2188
Felipe Leme4753bb02017-03-22 20:24:00 -07002189 /** @hide */
2190 protected boolean hasContentView() {
2191 return mContentView != null;
2192 }
2193
2194 /** @hide */
2195 protected boolean hasDecorView() {
2196 return mDecorView != null;
2197 }
2198
2199 /** @hide */
2200 protected WindowManager.LayoutParams getDecorViewLayoutParams() {
2201 return (WindowManager.LayoutParams) mDecorView.getLayoutParams();
2202 }
2203
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002204 /**
Alan Viverette259c2842015-03-22 17:39:39 -07002205 * Updates the position and the dimension of the popup window.
2206 * <p>
2207 * Calling this function also updates the window with the current popup
2208 * state as described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002209 *
2210 * @param anchor the popup's anchor view
Alan Viverettec129b582016-06-21 11:09:03 -04002211 * @param width the new width in pixels, must be >= 0 or -1 to ignore
2212 * @param height the new height in pixels, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002213 */
2214 public void update(View anchor, int width, int height) {
Alan Viveretteb91d6d02016-03-29 14:55:04 -04002215 update(anchor, false, 0, 0, width, height);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002216 }
2217
2218 /**
Alan Viverette259c2842015-03-22 17:39:39 -07002219 * Updates the position and the dimension of the popup window.
2220 * <p>
2221 * Width and height can be set to -1 to update location only. Calling this
2222 * function also updates the window with the current popup state as
2223 * described for {@link #update()}.
2224 * <p>
2225 * If the view later scrolls to move {@code anchor} to a different
2226 * location, the popup will be moved correspondingly.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002227 *
2228 * @param anchor the popup's anchor view
2229 * @param xoff x offset from the view's left edge
2230 * @param yoff y offset from the view's bottom edge
Alan Viverettec129b582016-06-21 11:09:03 -04002231 * @param width the new width in pixels, must be >= 0 or -1 to ignore
2232 * @param height the new height in pixels, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002233 */
2234 public void update(View anchor, int xoff, int yoff, int width, int height) {
Alan Viveretteb91d6d02016-03-29 14:55:04 -04002235 update(anchor, true, xoff, yoff, width, height);
The Android Open Source Project10592532009-03-18 17:39:46 -07002236 }
2237
2238 private void update(View anchor, boolean updateLocation, int xoff, int yoff,
Alan Viveretteb91d6d02016-03-29 14:55:04 -04002239 int width, int height) {
The Android Open Source Project10592532009-03-18 17:39:46 -07002240
Felipe Leme4753bb02017-03-22 20:24:00 -07002241 if (!isShowing() || !hasContentView()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002242 return;
2243 }
2244
Alan Viverette75d83792015-01-07 15:51:54 -08002245 final WeakReference<View> oldAnchor = mAnchor;
Alan Viverettef50df432016-03-24 14:08:24 -04002246 final int gravity = mAnchoredGravity;
2247
Alan Viverette75d83792015-01-07 15:51:54 -08002248 final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff);
Gilles Debunne81f08082011-02-17 14:07:19 -08002249 if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) {
Alan Viverettef50df432016-03-24 14:08:24 -04002250 attachToAnchor(anchor, xoff, yoff, gravity);
Gilles Debunne81f08082011-02-17 14:07:19 -08002251 } else if (needsUpdate) {
2252 // No need to register again if this is a DropDown, showAsDropDown already did.
2253 mAnchorXoff = xoff;
2254 mAnchorYoff = yoff;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002255 }
2256
Felipe Leme4753bb02017-03-22 20:24:00 -07002257 final WindowManager.LayoutParams p = getDecorViewLayoutParams();
Alan Viverettef50df432016-03-24 14:08:24 -04002258 final int oldGravity = p.gravity;
2259 final int oldWidth = p.width;
2260 final int oldHeight = p.height;
2261 final int oldX = p.x;
2262 final int oldY = p.y;
Alan Viverettef95b2d92016-03-22 11:23:05 -04002263
Alan Viveretteb91d6d02016-03-29 14:55:04 -04002264 // If an explicit width/height has not specified, use the most recent
2265 // explicitly specified value (either from setWidth/Height or update).
Robert Carrf6e801d2016-04-12 14:33:18 -07002266 if (width < 0) {
Alan Viveretteb91d6d02016-03-29 14:55:04 -04002267 width = mWidth;
2268 }
Robert Carrf6e801d2016-04-12 14:33:18 -07002269 if (height < 0) {
Alan Viveretteb91d6d02016-03-29 14:55:04 -04002270 height = mHeight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002271 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002272
Alan Viverettef50df432016-03-24 14:08:24 -04002273 final boolean aboveAnchor = findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
Alan Viverette50df07a2017-01-06 14:40:21 -05002274 width, height, gravity, mAllowScrollingAnchorParent);
Alan Viverettef50df432016-03-24 14:08:24 -04002275 updateAboveAnchor(aboveAnchor);
Fabrice Di Megliob003e282012-10-17 17:20:19 -07002276
Alan Viverettef50df432016-03-24 14:08:24 -04002277 final boolean paramsChanged = oldGravity != p.gravity || oldX != p.x || oldY != p.y
2278 || oldWidth != p.width || oldHeight != p.height;
Alan Viverette50df07a2017-01-06 14:40:21 -05002279
2280 // If width and mWidth were both < 0 then we have a MATCH_PARENT or
2281 // WRAP_CONTENT case. findDropDownPosition will have resolved this to
2282 // absolute values, but we don't want to update mWidth/mHeight to these
2283 // absolute values.
2284 final int newWidth = width < 0 ? width : p.width;
2285 final int newHeight = height < 0 ? height : p.height;
2286 update(p.x, p.y, newWidth, newHeight, paramsChanged);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002287 }
2288
2289 /**
2290 * Listener that is called when this popup window is dismissed.
2291 */
2292 public interface OnDismissListener {
2293 /**
2294 * Called when this popup window is dismissed.
2295 */
2296 public void onDismiss();
2297 }
2298
Felipe Leme4753bb02017-03-22 20:24:00 -07002299 /** @hide */
Svet Ganov77150c52017-09-22 17:19:04 -07002300 protected void detachFromAnchor() {
2301 final View anchor = getAnchor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002302 if (anchor != null) {
Alan Viverettee025ed22015-02-02 11:27:21 -08002303 final ViewTreeObserver vto = anchor.getViewTreeObserver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002304 vto.removeOnScrollChangedListener(mOnScrollChangedListener);
Vladislav Kaznacheevafaa9322017-02-09 11:43:28 -08002305 anchor.removeOnAttachStateChangeListener(mOnAnchorDetachedListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002306 }
Alan Viverettee025ed22015-02-02 11:27:21 -08002307
Alan Viverette634a8082016-02-03 14:22:41 -05002308 final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
2309 if (anchorRoot != null) {
2310 anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -08002311 anchorRoot.removeOnLayoutChangeListener(mOnLayoutChangeListener);
Alan Viverette634a8082016-02-03 14:22:41 -05002312 }
2313
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002314 mAnchor = null;
Alan Viverette634a8082016-02-03 14:22:41 -05002315 mAnchorRoot = null;
2316 mIsAnchorRootAttached = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002317 }
2318
Felipe Leme4753bb02017-03-22 20:24:00 -07002319 /** @hide */
Svet Ganov77150c52017-09-22 17:19:04 -07002320 protected void attachToAnchor(View anchor, int xoff, int yoff, int gravity) {
Alan Viverettef50df432016-03-24 14:08:24 -04002321 detachFromAnchor();
Alan Viverettee025ed22015-02-02 11:27:21 -08002322
2323 final ViewTreeObserver vto = anchor.getViewTreeObserver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002324 if (vto != null) {
2325 vto.addOnScrollChangedListener(mOnScrollChangedListener);
2326 }
Vladislav Kaznacheevafaa9322017-02-09 11:43:28 -08002327 anchor.addOnAttachStateChangeListener(mOnAnchorDetachedListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002328
Alan Viverette634a8082016-02-03 14:22:41 -05002329 final View anchorRoot = anchor.getRootView();
2330 anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -08002331 anchorRoot.addOnLayoutChangeListener(mOnLayoutChangeListener);
Alan Viverette634a8082016-02-03 14:22:41 -05002332
2333 mAnchor = new WeakReference<>(anchor);
2334 mAnchorRoot = new WeakReference<>(anchorRoot);
2335 mIsAnchorRootAttached = anchorRoot.isAttachedToWindow();
Peeyush Agarwal50db7312016-11-30 15:54:32 +00002336 mParentRootView = mAnchorRoot;
Alan Viverette634a8082016-02-03 14:22:41 -05002337
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002338 mAnchorXoff = xoff;
2339 mAnchorYoff = yoff;
Adam Powell54c94de2013-09-26 15:36:34 -07002340 mAnchoredGravity = gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002341 }
2342
Svet Ganov77150c52017-09-22 17:19:04 -07002343 /** @hide */
2344 protected @Nullable View getAnchor() {
2345 return mAnchor != null ? mAnchor.get() : null;
2346 }
2347
Vladislav Kaznacheevafaa9322017-02-09 11:43:28 -08002348 private void alignToAnchor() {
2349 final View anchor = mAnchor != null ? mAnchor.get() : null;
Felipe Leme4753bb02017-03-22 20:24:00 -07002350 if (anchor != null && anchor.isAttachedToWindow() && hasDecorView()) {
2351 final WindowManager.LayoutParams p = getDecorViewLayoutParams();
Vladislav Kaznacheevafaa9322017-02-09 11:43:28 -08002352
2353 updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
2354 p.width, p.height, mAnchoredGravity, false));
2355 update(p.x, p.y, -1, -1, true);
2356 }
2357 }
2358
Vladislav Kaznacheevb40e61b2017-03-07 11:03:35 -08002359 private View getAppRootView(View anchor) {
2360 final View appWindowView = WindowManagerGlobal.getInstance().getWindowView(
2361 anchor.getApplicationWindowToken());
2362 if (appWindowView != null) {
2363 return appWindowView;
2364 }
2365 return anchor.getRootView();
2366 }
2367
Alan Viverette5435a302015-01-29 10:25:34 -08002368 private class PopupDecorView extends FrameLayout {
Alan Viverette7e1aeb72017-03-22 11:06:59 -04002369 /** Runnable used to clean up listeners after exit transition. */
2370 private Runnable mCleanupAfterExit;
Alan Viverette8fd949e2015-03-11 12:21:30 -07002371
Alan Viverette5435a302015-01-29 10:25:34 -08002372 public PopupDecorView(Context context) {
2373 super(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002374 }
2375
2376 @Override
2377 public boolean dispatchKeyEvent(KeyEvent event) {
2378 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
Per Andersson4ae02b32011-01-17 11:16:23 +01002379 if (getKeyDispatcherState() == null) {
2380 return super.dispatchKeyEvent(event);
2381 }
2382
Alan Viverette5435a302015-01-29 10:25:34 -08002383 if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
2384 final KeyEvent.DispatcherState state = getKeyDispatcherState();
Jeff Brownb3ea9222011-01-10 16:26:36 -08002385 if (state != null) {
2386 state.startTracking(event, this);
2387 }
Dianne Hackborn8d374262009-09-14 21:21:52 -07002388 return true;
Jeff Brownb3ea9222011-01-10 16:26:36 -08002389 } else if (event.getAction() == KeyEvent.ACTION_UP) {
Alan Viverette5435a302015-01-29 10:25:34 -08002390 final KeyEvent.DispatcherState state = getKeyDispatcherState();
Jeff Brownb3ea9222011-01-10 16:26:36 -08002391 if (state != null && state.isTracking(event) && !event.isCanceled()) {
2392 dismiss();
2393 return true;
2394 }
Dianne Hackborn8d374262009-09-14 21:21:52 -07002395 }
2396 return super.dispatchKeyEvent(event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002397 } else {
2398 return super.dispatchKeyEvent(event);
2399 }
2400 }
2401
2402 @Override
2403 public boolean dispatchTouchEvent(MotionEvent ev) {
2404 if (mTouchInterceptor != null && mTouchInterceptor.onTouch(this, ev)) {
2405 return true;
2406 }
2407 return super.dispatchTouchEvent(ev);
2408 }
2409
2410 @Override
2411 public boolean onTouchEvent(MotionEvent event) {
2412 final int x = (int) event.getX();
2413 final int y = (int) event.getY();
Alan Viverette5435a302015-01-29 10:25:34 -08002414
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002415 if ((event.getAction() == MotionEvent.ACTION_DOWN)
2416 && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
2417 dismiss();
2418 return true;
2419 } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
2420 dismiss();
2421 return true;
2422 } else {
2423 return super.onTouchEvent(event);
2424 }
2425 }
Alan Viverette8fd949e2015-03-11 12:21:30 -07002426
2427 /**
2428 * Requests that an enter transition run after the next layout pass.
2429 */
2430 public void requestEnterTransition(Transition transition) {
2431 final ViewTreeObserver observer = getViewTreeObserver();
2432 if (observer != null && transition != null) {
2433 final Transition enterTransition = transition.clone();
2434
2435 // Postpone the enter transition after the first layout pass.
2436 observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
2437 @Override
2438 public void onGlobalLayout() {
2439 final ViewTreeObserver observer = getViewTreeObserver();
2440 if (observer != null) {
2441 observer.removeOnGlobalLayoutListener(this);
2442 }
2443
Alan Viverette91098572016-01-19 14:07:31 -05002444 final Rect epicenter = getTransitionEpicenter();
Alan Viverette95888c02015-04-16 13:27:50 -07002445 enterTransition.setEpicenterCallback(new EpicenterCallback() {
2446 @Override
2447 public Rect onGetEpicenter(Transition transition) {
2448 return epicenter;
2449 }
2450 });
Alan Viverette8fd949e2015-03-11 12:21:30 -07002451 startEnterTransition(enterTransition);
2452 }
2453 });
2454 }
2455 }
2456
2457 /**
2458 * Starts the pending enter transition, if one is set.
2459 */
2460 private void startEnterTransition(Transition enterTransition) {
2461 final int count = getChildCount();
2462 for (int i = 0; i < count; i++) {
2463 final View child = getChildAt(i);
2464 enterTransition.addTarget(child);
Evan Roskyadf5bec2017-10-19 10:24:07 -07002465 child.setTransitionVisibility(View.INVISIBLE);
Alan Viverette8fd949e2015-03-11 12:21:30 -07002466 }
2467
2468 TransitionManager.beginDelayedTransition(this, enterTransition);
2469
2470 for (int i = 0; i < count; i++) {
2471 final View child = getChildAt(i);
Evan Roskyadf5bec2017-10-19 10:24:07 -07002472 child.setTransitionVisibility(View.VISIBLE);
Alan Viverette8fd949e2015-03-11 12:21:30 -07002473 }
2474 }
2475
2476 /**
2477 * Starts an exit transition immediately.
2478 * <p>
2479 * <strong>Note:</strong> The transition listener is guaranteed to have
2480 * its {@code onTransitionEnd} method called even if the transition
Alan Viverette7e1aeb72017-03-22 11:06:59 -04002481 * never starts.
Alan Viverette8fd949e2015-03-11 12:21:30 -07002482 */
Alan Viverette054c1722016-12-01 13:33:44 -05002483 public void startExitTransition(@NonNull Transition transition,
2484 @Nullable final View anchorRoot, @Nullable final Rect epicenter,
2485 @NonNull final TransitionListener listener) {
Alan Viverette8fd949e2015-03-11 12:21:30 -07002486 if (transition == null) {
2487 return;
2488 }
2489
Alan Viverette634a8082016-02-03 14:22:41 -05002490 // The anchor view's window may go away while we're executing our
2491 // transition, in which case we need to end the transition
2492 // immediately and execute the listener to remove the popup.
Alan Viverette054c1722016-12-01 13:33:44 -05002493 if (anchorRoot != null) {
2494 anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
2495 }
Alan Viverette634a8082016-02-03 14:22:41 -05002496
Alan Viverette7e1aeb72017-03-22 11:06:59 -04002497 // The cleanup runnable MUST be called even if the transition is
2498 // canceled before it starts (and thus can't call onTransitionEnd).
2499 mCleanupAfterExit = () -> {
2500 listener.onTransitionEnd(transition);
Alan Viverette054c1722016-12-01 13:33:44 -05002501
Alan Viverette7e1aeb72017-03-22 11:06:59 -04002502 if (anchorRoot != null) {
2503 anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
Alan Viverette8fd949e2015-03-11 12:21:30 -07002504 }
Alan Viverette7e1aeb72017-03-22 11:06:59 -04002505
2506 // The listener was called. Our job here is done.
2507 mCleanupAfterExit = null;
Alan Viverette8fd949e2015-03-11 12:21:30 -07002508 };
2509
2510 final Transition exitTransition = transition.clone();
Alan Viverette7e1aeb72017-03-22 11:06:59 -04002511 exitTransition.addListener(new TransitionListenerAdapter() {
2512 @Override
2513 public void onTransitionEnd(Transition t) {
2514 t.removeListener(this);
2515
2516 // This null check shouldn't be necessary, but it's easier
2517 // to check here than it is to test every possible case.
2518 if (mCleanupAfterExit != null) {
2519 mCleanupAfterExit.run();
2520 }
2521 }
2522 });
Alan Viverette054c1722016-12-01 13:33:44 -05002523 exitTransition.setEpicenterCallback(new EpicenterCallback() {
2524 @Override
2525 public Rect onGetEpicenter(Transition transition) {
2526 return epicenter;
2527 }
2528 });
Alan Viverette8fd949e2015-03-11 12:21:30 -07002529
2530 final int count = getChildCount();
2531 for (int i = 0; i < count; i++) {
2532 final View child = getChildAt(i);
2533 exitTransition.addTarget(child);
2534 }
2535
2536 TransitionManager.beginDelayedTransition(this, exitTransition);
2537
2538 for (int i = 0; i < count; i++) {
2539 final View child = getChildAt(i);
2540 child.setVisibility(View.INVISIBLE);
2541 }
2542 }
2543
2544 /**
2545 * Cancels all pending or current transitions.
2546 */
2547 public void cancelTransitions() {
2548 TransitionManager.endTransitions(this);
2549
Alan Viverette7e1aeb72017-03-22 11:06:59 -04002550 // If the cleanup runnable is still around, that means the
2551 // transition never started. We should run it now to clean up.
2552 if (mCleanupAfterExit != null) {
2553 mCleanupAfterExit.run();
Alan Viverette8fd949e2015-03-11 12:21:30 -07002554 }
2555 }
Alan Viverette634a8082016-02-03 14:22:41 -05002556
2557 private final OnAttachStateChangeListener mOnAnchorRootDetachedListener =
2558 new OnAttachStateChangeListener() {
2559 @Override
2560 public void onViewAttachedToWindow(View v) {}
2561
2562 @Override
2563 public void onViewDetachedFromWindow(View v) {
2564 v.removeOnAttachStateChangeListener(this);
2565
2566 TransitionManager.endTransitions(PopupDecorView.this);
2567 }
2568 };
Peeyush Agarwal50db7312016-11-30 15:54:32 +00002569
2570 @Override
2571 public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> list, int deviceId) {
2572 if (mParentRootView != null) {
2573 View parentRoot = mParentRootView.get();
2574 if (parentRoot != null) {
2575 parentRoot.requestKeyboardShortcuts(list, deviceId);
2576 }
2577 }
2578 }
Alan Viverette5435a302015-01-29 10:25:34 -08002579 }
svetoslavganov75986cf2009-05-14 22:28:01 -07002580
Alan Viverette5435a302015-01-29 10:25:34 -08002581 private class PopupBackgroundView extends FrameLayout {
2582 public PopupBackgroundView(Context context) {
2583 super(context);
2584 }
2585
svetoslavganov75986cf2009-05-14 22:28:01 -07002586 @Override
Alan Viverette5435a302015-01-29 10:25:34 -08002587 protected int[] onCreateDrawableState(int extraSpace) {
2588 if (mAboveAnchor) {
2589 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
2590 View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET);
2591 return drawableState;
svetoslavganov75986cf2009-05-14 22:28:01 -07002592 } else {
Alan Viverette5435a302015-01-29 10:25:34 -08002593 return super.onCreateDrawableState(extraSpace);
svetoslavganov75986cf2009-05-14 22:28:01 -07002594 }
2595 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002596 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002597}