blob: c471cf3d74a128ec5c313f7af65bbd7d943a9c68 [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
Adam Powella7287f42010-08-17 21:17:04 -070019import com.android.internal.R;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020
Alan Viveretteb854d072015-09-28 16:12:18 -040021import android.annotation.NonNull;
Alan Viverette1e940dc2016-03-18 09:55:10 -040022import android.annotation.Nullable;
svetoslavganov75986cf2009-05-14 22:28:01 -070023import android.content.Context;
24import android.content.res.TypedArray;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.graphics.PixelFormat;
26import android.graphics.Rect;
27import android.graphics.drawable.Drawable;
28import android.graphics.drawable.StateListDrawable;
Jeff Brown46e75292010-11-10 16:53:45 -080029import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.os.IBinder;
Alan Viverette5435a302015-01-29 10:25:34 -080031import android.transition.Transition;
32import android.transition.Transition.EpicenterCallback;
Alan Viverette8fd949e2015-03-11 12:21:30 -070033import android.transition.Transition.TransitionListener;
34import android.transition.Transition.TransitionListenerAdapter;
Alan Viverette5435a302015-01-29 10:25:34 -080035import android.transition.TransitionInflater;
36import android.transition.TransitionManager;
37import android.transition.TransitionSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.util.AttributeSet;
Adam Powellc3fa6302010-05-18 11:36:27 -070039import android.view.Gravity;
40import android.view.KeyEvent;
41import android.view.MotionEvent;
42import android.view.View;
Alan Viverette634a8082016-02-03 14:22:41 -050043import android.view.View.OnAttachStateChangeListener;
Adam Powella7287f42010-08-17 21:17:04 -070044import android.view.View.OnTouchListener;
Adam Powellc3fa6302010-05-18 11:36:27 -070045import android.view.ViewGroup;
Alan Viverette8fd949e2015-03-11 12:21:30 -070046import android.view.ViewParent;
Adam Powellc3fa6302010-05-18 11:36:27 -070047import android.view.ViewTreeObserver;
Alan Viverette8fd949e2015-03-11 12:21:30 -070048import android.view.ViewTreeObserver.OnGlobalLayoutListener;
Adam Powellc3fa6302010-05-18 11:36:27 -070049import android.view.ViewTreeObserver.OnScrollChangedListener;
Adam Powella7287f42010-08-17 21:17:04 -070050import android.view.WindowManager;
Alan Viverette259c2842015-03-22 17:39:39 -070051import android.view.WindowManager.LayoutParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052
Adam Powella7287f42010-08-17 21:17:04 -070053import java.lang.ref.WeakReference;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054
Alan Viverette1e940dc2016-03-18 09:55:10 -040055import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
56import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
57
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058/**
Alan Viverette1e940dc2016-03-18 09:55:10 -040059 * <p>
60 * This class represents a popup window that can be used to display an
61 * arbitrary view. The popup window is a floating container that appears on top
62 * of the current activity.
63 * </p>
64 * <a name="Animation"></a>
65 * <h3>Animation</h3>
66 * <p>
67 * On all versions of Android, popup window enter and exit animations may be
68 * specified by calling {@link #setAnimationStyle(int)} and passing the
69 * resource ID for an animation style that defines {@code windowEnterAnimation}
70 * and {@code windowExitAnimation}. For example, passing
71 * {@link android.R.style#Animation_Dialog} will give a scale and alpha
72 * animation.
73 * </br>
74 * A window animation style may also be specified in the popup window's style
75 * XML via the {@link android.R.styleable#PopupWindow_popupAnimationStyle popupAnimationStyle}
76 * attribute.
77 * </p>
78 * <p>
79 * Starting with API 23, more complex popup window enter and exit transitions
80 * may be specified by calling either {@link #setEnterTransition(Transition)}
81 * or {@link #setExitTransition(Transition)} and passing a {@link Transition}.
82 * </br>
83 * Popup enter and exit transitions may also be specified in the popup window's
84 * style XML via the {@link android.R.styleable#PopupWindow_popupEnterTransition popupEnterTransition}
85 * and {@link android.R.styleable#PopupWindow_popupExitTransition popupExitTransition}
86 * attributes, respectively.
87 * </p>
88 *
89 * @attr ref android.R.styleable#PopupWindow_overlapAnchor
90 * @attr ref android.R.styleable#PopupWindow_popupAnimationStyle
91 * @attr ref android.R.styleable#PopupWindow_popupBackground
92 * @attr ref android.R.styleable#PopupWindow_popupElevation
93 * @attr ref android.R.styleable#PopupWindow_popupEnterTransition
94 * @attr ref android.R.styleable#PopupWindow_popupExitTransition
Alan Viverette5435a302015-01-29 10:25:34 -080095 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 * @see android.widget.AutoCompleteTextView
97 * @see android.widget.Spinner
98 */
99public class PopupWindow {
100 /**
Romain Guye29f0642009-06-23 21:27:02 -0700101 * Mode for {@link #setInputMethodMode(int)}: the requirements for the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 * input method should be based on the focusability of the popup. That is
103 * if it is focusable than it needs to work with the input method, else
104 * it doesn't.
105 */
106 public static final int INPUT_METHOD_FROM_FOCUSABLE = 0;
Alan Viverette5435a302015-01-29 10:25:34 -0800107
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 /**
Romain Guye29f0642009-06-23 21:27:02 -0700109 * Mode for {@link #setInputMethodMode(int)}: this popup always needs to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 * work with an input method, regardless of whether it is focusable. This
111 * means that it will always be displayed so that the user can also operate
112 * the input method while it is shown.
113 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 public static final int INPUT_METHOD_NEEDED = 1;
Alan Viverette5435a302015-01-29 10:25:34 -0800115
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 /**
Romain Guye29f0642009-06-23 21:27:02 -0700117 * Mode for {@link #setInputMethodMode(int)}: this popup never needs to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 * work with an input method, regardless of whether it is focusable. This
119 * means that it will always be displayed to use as much space on the
120 * screen as needed, regardless of whether this covers the input method.
121 */
122 public static final int INPUT_METHOD_NOT_NEEDED = 2;
Adam Powell54c94de2013-09-26 15:36:34 -0700123
124 private static final int DEFAULT_ANCHORED_GRAVITY = Gravity.TOP | Gravity.START;
125
Alan Viverette5435a302015-01-29 10:25:34 -0800126 /**
127 * Default animation style indicating that separate animations should be
128 * used for top/bottom anchoring states.
129 */
130 private static final int ANIMATION_STYLE_DEFAULT = -1;
131
Alan Viverettef50df432016-03-24 14:08:24 -0400132 private final int[] mTmpDrawingLocation = new int[2];
133 private final int[] mTmpScreenLocation = new int[2];
Alan Viverette5435a302015-01-29 10:25:34 -0800134 private final Rect mTempRect = new Rect();
Alan Viverette5435a302015-01-29 10:25:34 -0800135
Romain Guy448ecf52009-05-14 16:03:42 -0700136 private Context mContext;
137 private WindowManager mWindowManager;
Alan Viverette5435a302015-01-29 10:25:34 -0800138
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 private boolean mIsShowing;
Alan Viverette8fd949e2015-03-11 12:21:30 -0700140 private boolean mIsTransitioningToDismiss;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 private boolean mIsDropdown;
142
Alan Viverette5435a302015-01-29 10:25:34 -0800143 /** View that handles event dispatch and content transitions. */
144 private PopupDecorView mDecorView;
145
Alan Viverette697804e2015-08-06 12:36:47 -0400146 /** View that holds the background and may animate during a transition. */
147 private View mBackgroundView;
148
149 /** The contents of the popup. May be identical to the background view. */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 private View mContentView;
Alan Viverette5435a302015-01-29 10:25:34 -0800151
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 private boolean mFocusable;
153 private int mInputMethodMode = INPUT_METHOD_FROM_FOCUSABLE;
Dianne Hackborn7eab0942011-01-01 13:21:50 -0800154 private int mSoftInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 private boolean mTouchable = true;
156 private boolean mOutsideTouchable = false;
157 private boolean mClippingEnabled = true;
Jeff Brown46e75292010-11-10 16:53:45 -0800158 private int mSplitTouchEnabled = -1;
Adam Powellba0a2c32010-09-28 17:41:23 -0700159 private boolean mLayoutInScreen;
Adam Powell56c2d332010-11-05 20:03:03 -0700160 private boolean mClipToScreen;
Adam Powell348e69c2011-02-16 16:49:50 -0800161 private boolean mAllowScrollingAnchorParent = true;
Adam Powell0bd1d0a2011-07-22 19:35:06 -0700162 private boolean mLayoutInsetDecor = false;
Adam Powelle0b6cd12011-09-28 22:06:11 -0700163 private boolean mNotTouchModal;
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700164 private boolean mAttachedInDecor = true;
165 private boolean mAttachedInDecorSet = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166
167 private OnTouchListener mTouchInterceptor;
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700168
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 private int mWidthMode;
Alan Viverette259c2842015-03-22 17:39:39 -0700170 private int mWidth = LayoutParams.WRAP_CONTENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 private int mLastWidth;
172 private int mHeightMode;
Alan Viverette259c2842015-03-22 17:39:39 -0700173 private int mHeight = LayoutParams.WRAP_CONTENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 private int mLastHeight;
175
Alan Viveretteccb11e12014-07-08 16:04:02 -0700176 private float mElevation;
177
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 private Drawable mBackground;
179 private Drawable mAboveAnchorBackgroundDrawable;
180 private Drawable mBelowAnchorBackgroundDrawable;
181
Alan Viverette5435a302015-01-29 10:25:34 -0800182 private Transition mEnterTransition;
183 private Transition mExitTransition;
Alan Viverette91098572016-01-19 14:07:31 -0500184 private Rect mEpicenterBounds;
Alan Viverette560f1702014-05-05 14:40:07 -0700185
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 private boolean mAboveAnchor;
Adam Powell574b37e2010-10-07 11:15:19 -0700187 private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
Alan Viverette5435a302015-01-29 10:25:34 -0800188
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 private OnDismissListener mOnDismissListener;
190 private boolean mIgnoreCheekPress = false;
191
Alan Viverette5435a302015-01-29 10:25:34 -0800192 private int mAnimationStyle = ANIMATION_STYLE_DEFAULT;
193
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 private static final int[] ABOVE_ANCHOR_STATE_SET = new int[] {
195 com.android.internal.R.attr.state_above_anchor
196 };
197
Alan Viverette634a8082016-02-03 14:22:41 -0500198 private final OnAttachStateChangeListener mOnAnchorRootDetachedListener =
199 new OnAttachStateChangeListener() {
200 @Override
201 public void onViewAttachedToWindow(View v) {}
202
203 @Override
204 public void onViewDetachedFromWindow(View v) {
205 mIsAnchorRootAttached = false;
206 }
207 };
208
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 private WeakReference<View> mAnchor;
Alan Viverette634a8082016-02-03 14:22:41 -0500210 private WeakReference<View> mAnchorRoot;
211 private boolean mIsAnchorRootAttached;
Alan Viverette560f1702014-05-05 14:40:07 -0700212
Alan Viverette5435a302015-01-29 10:25:34 -0800213 private final OnScrollChangedListener mOnScrollChangedListener = new OnScrollChangedListener() {
214 @Override
215 public void onScrollChanged() {
216 final View anchor = mAnchor != null ? mAnchor.get() : null;
217 if (anchor != null && mDecorView != null) {
218 final WindowManager.LayoutParams p = (WindowManager.LayoutParams)
219 mDecorView.getLayoutParams();
220
221 updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
Alan Viverettef50df432016-03-24 14:08:24 -0400222 p.width, p.height, mAnchoredGravity));
Alan Viverette5435a302015-01-29 10:25:34 -0800223 update(p.x, p.y, -1, -1, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 }
Alan Viverette5435a302015-01-29 10:25:34 -0800225 }
226 };
Alan Viverette560f1702014-05-05 14:40:07 -0700227
Alan Viverette5435a302015-01-29 10:25:34 -0800228 private int mAnchorXoff;
229 private int mAnchorYoff;
230 private int mAnchoredGravity;
Alan Viverette560f1702014-05-05 14:40:07 -0700231 private boolean mOverlapAnchor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232
Fabrice Di Megliob003e282012-10-17 17:20:19 -0700233 private boolean mPopupViewInitialLayoutDirectionInherited;
234
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 /**
236 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
237 *
238 * <p>The popup does provide a background.</p>
239 */
240 public PopupWindow(Context context) {
241 this(context, null);
242 }
243
244 /**
245 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
246 *
247 * <p>The popup does provide a background.</p>
248 */
249 public PopupWindow(Context context, AttributeSet attrs) {
250 this(context, attrs, com.android.internal.R.attr.popupWindowStyle);
251 }
252
253 /**
254 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
255 *
256 * <p>The popup does provide a background.</p>
257 */
Alan Viverette617feb92013-09-09 18:09:13 -0700258 public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
259 this(context, attrs, defStyleAttr, 0);
Adam Powellc3fa6302010-05-18 11:36:27 -0700260 }
Alan Viverette5435a302015-01-29 10:25:34 -0800261
Adam Powellc3fa6302010-05-18 11:36:27 -0700262 /**
263 * <p>Create a new, empty, non focusable popup window of dimension (0,0).</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800264 *
Adam Powellc3fa6302010-05-18 11:36:27 -0700265 * <p>The popup does not provide a background.</p>
266 */
267 public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 mContext = context;
Alan Viverette75d83792015-01-07 15:51:54 -0800269 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270
Alan Viverette617feb92013-09-09 18:09:13 -0700271 final TypedArray a = context.obtainStyledAttributes(
Alan Viverettece8c3582014-11-07 13:19:38 -0800272 attrs, R.styleable.PopupWindow, defStyleAttr, defStyleRes);
273 final Drawable bg = a.getDrawable(R.styleable.PopupWindow_popupBackground);
Alan Viveretteccb11e12014-07-08 16:04:02 -0700274 mElevation = a.getDimension(R.styleable.PopupWindow_popupElevation, 0);
Alan Viverette560f1702014-05-05 14:40:07 -0700275 mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false);
Adam Powellc3808b52010-10-04 10:06:59 -0700276
Alan Viverette5435a302015-01-29 10:25:34 -0800277 // Preserve default behavior from Gingerbread. If the animation is
278 // undefined or explicitly specifies the Gingerbread animation style,
279 // use a sentinel value.
280 if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupAnimationStyle)) {
281 final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, 0);
282 if (animStyle == R.style.Animation_PopupWindow) {
283 mAnimationStyle = ANIMATION_STYLE_DEFAULT;
284 } else {
285 mAnimationStyle = animStyle;
286 }
287 } else {
288 mAnimationStyle = ANIMATION_STYLE_DEFAULT;
289 }
290
291 final Transition enterTransition = getTransition(a.getResourceId(
292 R.styleable.PopupWindow_popupEnterTransition, 0));
293 final Transition exitTransition;
294 if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupExitTransition)) {
295 exitTransition = getTransition(a.getResourceId(
296 R.styleable.PopupWindow_popupExitTransition, 0));
297 } else {
298 exitTransition = enterTransition == null ? null : enterTransition.clone();
299 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 a.recycle();
Alan Viverettece8c3582014-11-07 13:19:38 -0800302
Alan Viverette5435a302015-01-29 10:25:34 -0800303 setEnterTransition(enterTransition);
304 setExitTransition(exitTransition);
Alan Viverettece8c3582014-11-07 13:19:38 -0800305 setBackgroundDrawable(bg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 }
307
308 /**
309 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
310 *
311 * <p>The popup does not provide any background. This should be handled
312 * by the content view.</p>
313 */
314 public PopupWindow() {
315 this(null, 0, 0);
316 }
317
318 /**
319 * <p>Create a new non focusable popup window which can display the
320 * <tt>contentView</tt>. The dimension of the window are (0,0).</p>
321 *
322 * <p>The popup does not provide any background. This should be handled
323 * by the content view.</p>
324 *
325 * @param contentView the popup's content
326 */
327 public PopupWindow(View contentView) {
328 this(contentView, 0, 0);
329 }
330
331 /**
332 * <p>Create a new empty, non focusable popup window. The dimension of the
333 * window must be passed to this constructor.</p>
334 *
335 * <p>The popup does not provide any background. This should be handled
336 * by the content view.</p>
337 *
338 * @param width the popup's width
339 * @param height the popup's height
340 */
341 public PopupWindow(int width, int height) {
342 this(null, width, height);
343 }
344
345 /**
346 * <p>Create a new non focusable popup window which can display the
347 * <tt>contentView</tt>. The dimension of the window must be passed to
348 * this constructor.</p>
349 *
350 * <p>The popup does not provide any background. This should be handled
351 * by the content view.</p>
352 *
353 * @param contentView the popup's content
354 * @param width the popup's width
355 * @param height the popup's height
356 */
357 public PopupWindow(View contentView, int width, int height) {
358 this(contentView, width, height, false);
359 }
360
361 /**
362 * <p>Create a new popup window which can display the <tt>contentView</tt>.
363 * The dimension of the window must be passed to this constructor.</p>
364 *
365 * <p>The popup does not provide any background. This should be handled
366 * by the content view.</p>
367 *
368 * @param contentView the popup's content
369 * @param width the popup's width
370 * @param height the popup's height
371 * @param focusable true if the popup can be focused, false otherwise
372 */
Romain Guy448ecf52009-05-14 16:03:42 -0700373 public PopupWindow(View contentView, int width, int height, boolean focusable) {
374 if (contentView != null) {
375 mContext = contentView.getContext();
376 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
377 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700378
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 setContentView(contentView);
380 setWidth(width);
381 setHeight(height);
382 setFocusable(focusable);
383 }
384
Alan Viverette1e940dc2016-03-18 09:55:10 -0400385 /**
386 * Sets the enter transition to be used when the popup window is shown.
387 *
388 * @param enterTransition the enter transition, or {@code null} to clear
389 * @see #getEnterTransition()
390 * @attr ref android.R.styleable#PopupWindow_popupEnterTransition
391 */
392 public void setEnterTransition(@Nullable Transition enterTransition) {
Alan Viverette5435a302015-01-29 10:25:34 -0800393 mEnterTransition = enterTransition;
Alan Viverette5435a302015-01-29 10:25:34 -0800394 }
395
Alan Viverette1e940dc2016-03-18 09:55:10 -0400396 /**
397 * Returns the enter transition to be used when the popup window is shown.
398 *
399 * @return the enter transition, or {@code null} if not set
400 * @see #setEnterTransition(Transition)
401 * @attr ref android.R.styleable#PopupWindow_popupEnterTransition
402 */
403 @Nullable
404 public Transition getEnterTransition() {
405 return mEnterTransition;
406 }
407
408 /**
409 * Sets the exit transition to be used when the popup window is dismissed.
410 *
411 * @param exitTransition the exit transition, or {@code null} to clear
412 * @see #getExitTransition()
413 * @attr ref android.R.styleable#PopupWindow_popupExitTransition
414 */
415 public void setExitTransition(@Nullable Transition exitTransition) {
Alan Viverette5435a302015-01-29 10:25:34 -0800416 mExitTransition = exitTransition;
Alan Viverette5435a302015-01-29 10:25:34 -0800417 }
418
Alan Viverette91098572016-01-19 14:07:31 -0500419 /**
Alan Viverette1e940dc2016-03-18 09:55:10 -0400420 * Returns the exit transition to be used when the popup window is
421 * dismissed.
422 *
423 * @return the exit transition, or {@code null} if not set
424 * @see #setExitTransition(Transition)
425 * @attr ref android.R.styleable#PopupWindow_popupExitTransition
426 */
427 @Nullable
428 public Transition getExitTransition() {
429 return mExitTransition;
430 }
431
432 /**
Alan Viverette91098572016-01-19 14:07:31 -0500433 * Sets the bounds used as the epicenter of the enter and exit transitions.
434 * <p>
435 * Transitions use a point or Rect, referred to as the epicenter, to orient
436 * the direction of travel. For popup windows, the anchor view bounds are
437 * used as the default epicenter.
438 * <p>
439 * See {@link Transition#setEpicenterCallback(EpicenterCallback)} for more
440 * information about how transition epicenters.
441 *
442 * @param bounds the epicenter bounds relative to the anchor view, or
443 * {@code null} to use the default epicenter
444 * @see #getTransitionEpicenter()
445 * @hide
446 */
447 public void setEpicenterBounds(Rect bounds) {
448 mEpicenterBounds = bounds;
449 }
450
Alan Viverette5435a302015-01-29 10:25:34 -0800451 private Transition getTransition(int resId) {
452 if (resId != 0 && resId != R.transition.no_transition) {
453 final TransitionInflater inflater = TransitionInflater.from(mContext);
454 final Transition transition = inflater.inflateTransition(resId);
455 if (transition != null) {
456 final boolean isEmpty = transition instanceof TransitionSet
457 && ((TransitionSet) transition).getTransitionCount() == 0;
458 if (!isEmpty) {
459 return transition;
460 }
461 }
462 }
463 return null;
464 }
465
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700467 * Return the drawable used as the popup window's background.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 *
Alan Viveretteccb11e12014-07-08 16:04:02 -0700469 * @return the background drawable or {@code null} if not set
470 * @see #setBackgroundDrawable(Drawable)
471 * @attr ref android.R.styleable#PopupWindow_popupBackground
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 */
473 public Drawable getBackground() {
474 return mBackground;
475 }
476
477 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700478 * Specifies the background drawable for this popup window. The background
479 * can be set to {@code null}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 *
481 * @param background the popup's background
Alan Viveretteccb11e12014-07-08 16:04:02 -0700482 * @see #getBackground()
483 * @attr ref android.R.styleable#PopupWindow_popupBackground
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 */
485 public void setBackgroundDrawable(Drawable background) {
486 mBackground = background;
Alan Viverettece8c3582014-11-07 13:19:38 -0800487
488 // If this is a StateListDrawable, try to find and store the drawable to be
489 // used when the drop-down is placed above its anchor view, and the one to be
490 // used when the drop-down is placed below its anchor view. We extract
491 // the drawables ourselves to work around a problem with using refreshDrawableState
492 // that it will take into account the padding of all drawables specified in a
493 // StateListDrawable, thus adding superfluous padding to drop-down views.
494 //
495 // We assume a StateListDrawable will have a drawable for ABOVE_ANCHOR_STATE_SET and
496 // at least one other drawable, intended for the 'below-anchor state'.
497 if (mBackground instanceof StateListDrawable) {
498 StateListDrawable stateList = (StateListDrawable) mBackground;
499
500 // Find the above-anchor view - this one's easy, it should be labeled as such.
501 int aboveAnchorStateIndex = stateList.getStateDrawableIndex(ABOVE_ANCHOR_STATE_SET);
502
503 // Now, for the below-anchor view, look for any other drawable specified in the
504 // StateListDrawable which is not for the above-anchor state and use that.
505 int count = stateList.getStateCount();
506 int belowAnchorStateIndex = -1;
507 for (int i = 0; i < count; i++) {
508 if (i != aboveAnchorStateIndex) {
509 belowAnchorStateIndex = i;
510 break;
511 }
512 }
513
514 // Store the drawables we found, if we found them. Otherwise, set them both
515 // to null so that we'll just use refreshDrawableState.
516 if (aboveAnchorStateIndex != -1 && belowAnchorStateIndex != -1) {
517 mAboveAnchorBackgroundDrawable = stateList.getStateDrawable(aboveAnchorStateIndex);
518 mBelowAnchorBackgroundDrawable = stateList.getStateDrawable(belowAnchorStateIndex);
519 } else {
520 mBelowAnchorBackgroundDrawable = null;
521 mAboveAnchorBackgroundDrawable = null;
522 }
523 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 }
525
526 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700527 * @return the elevation for this popup window in pixels
528 * @see #setElevation(float)
529 * @attr ref android.R.styleable#PopupWindow_popupElevation
530 */
531 public float getElevation() {
532 return mElevation;
533 }
534
535 /**
536 * Specifies the elevation for this popup window.
537 *
538 * @param elevation the popup's elevation in pixels
539 * @see #getElevation()
540 * @attr ref android.R.styleable#PopupWindow_popupElevation
541 */
542 public void setElevation(float elevation) {
543 mElevation = elevation;
544 }
545
546 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 * <p>Return the animation style to use the popup appears and disappears</p>
548 *
549 * @return the animation style to use the popup appears and disappears
550 */
551 public int getAnimationStyle() {
552 return mAnimationStyle;
553 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700554
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 /**
Shuhrat Dehkanov54ec76d2014-08-28 17:12:23 +0900556 * Set the flag on popup to ignore cheek press events; by default this flag
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 * is set to false
Shuhrat Dehkanov54ec76d2014-08-28 17:12:23 +0900558 * which means the popup will not ignore cheek press dispatch events.
Alan Viverette5435a302015-01-29 10:25:34 -0800559 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 * <p>If the popup is showing, calling this method will take effect only
561 * the next time the popup is shown or through a manual call to one of
562 * the {@link #update()} methods.</p>
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700563 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 * @see #update()
565 */
566 public void setIgnoreCheekPress() {
567 mIgnoreCheekPress = true;
568 }
Alan Viverette5435a302015-01-29 10:25:34 -0800569
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570
571 /**
572 * <p>Change the animation style resource for this popup.</p>
573 *
574 * <p>If the popup is showing, calling this method will take effect only
575 * the next time the popup is shown or through a manual call to one of
576 * the {@link #update()} methods.</p>
577 *
578 * @param animationStyle animation style to use when the popup appears
579 * and disappears. Set to -1 for the default animation, 0 for no
580 * animation, or a resource identifier for an explicit animation.
Alan Viverette5435a302015-01-29 10:25:34 -0800581 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582 * @see #update()
583 */
584 public void setAnimationStyle(int animationStyle) {
585 mAnimationStyle = animationStyle;
586 }
Alan Viverette5435a302015-01-29 10:25:34 -0800587
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 /**
589 * <p>Return the view used as the content of the popup window.</p>
590 *
591 * @return a {@link android.view.View} representing the popup's content
592 *
593 * @see #setContentView(android.view.View)
594 */
595 public View getContentView() {
596 return mContentView;
597 }
598
599 /**
600 * <p>Change the popup's content. The content is represented by an instance
601 * of {@link android.view.View}.</p>
602 *
Gilles Debunne81f08082011-02-17 14:07:19 -0800603 * <p>This method has no effect if called when the popup is showing.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800604 *
605 * @param contentView the new content for the popup
606 *
607 * @see #getContentView()
608 * @see #isShowing()
609 */
610 public void setContentView(View contentView) {
611 if (isShowing()) {
612 return;
613 }
614
615 mContentView = contentView;
Romain Guy448ecf52009-05-14 16:03:42 -0700616
Romain Guy0c0b7682011-05-16 11:54:09 -0700617 if (mContext == null && mContentView != null) {
Romain Guy448ecf52009-05-14 16:03:42 -0700618 mContext = mContentView.getContext();
619 }
620
Romain Guy0c0b7682011-05-16 11:54:09 -0700621 if (mWindowManager == null && mContentView != null) {
Romain Guy448ecf52009-05-14 16:03:42 -0700622 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
623 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700624
625 // Setting the default for attachedInDecor based on SDK version here
626 // instead of in the constructor since we might not have the context
627 // object in the constructor. We only want to set default here if the
628 // app hasn't already set the attachedInDecor.
629 if (mContext != null && !mAttachedInDecorSet) {
630 // Attach popup window in decor frame of parent window by default for
631 // {@link Build.VERSION_CODES.LOLLIPOP_MR1} or greater. Keep current
632 // behavior of not attaching to decor frame for older SDKs.
633 setAttachedInDecor(mContext.getApplicationInfo().targetSdkVersion
634 >= Build.VERSION_CODES.LOLLIPOP_MR1);
635 }
636
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637 }
638
639 /**
640 * Set a callback for all touch events being dispatched to the popup
641 * window.
642 */
643 public void setTouchInterceptor(OnTouchListener l) {
644 mTouchInterceptor = l;
645 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700646
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800647 /**
648 * <p>Indicate whether the popup window can grab the focus.</p>
649 *
650 * @return true if the popup is focusable, false otherwise
651 *
652 * @see #setFocusable(boolean)
653 */
654 public boolean isFocusable() {
655 return mFocusable;
656 }
657
658 /**
659 * <p>Changes the focusability of the popup window. When focusable, the
660 * window will grab the focus from the current focused widget if the popup
661 * contains a focusable {@link android.view.View}. By default a popup
662 * window is not focusable.</p>
663 *
664 * <p>If the popup is showing, calling this method will take effect only
665 * the next time the popup is shown or through a manual call to one of
666 * the {@link #update()} methods.</p>
667 *
668 * @param focusable true if the popup should grab focus, false otherwise.
669 *
670 * @see #isFocusable()
Alan Viverette5435a302015-01-29 10:25:34 -0800671 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800672 * @see #update()
673 */
674 public void setFocusable(boolean focusable) {
675 mFocusable = focusable;
676 }
677
678 /**
679 * Return the current value in {@link #setInputMethodMode(int)}.
Alan Viverette5435a302015-01-29 10:25:34 -0800680 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800681 * @see #setInputMethodMode(int)
682 */
683 public int getInputMethodMode() {
684 return mInputMethodMode;
Alan Viverette5435a302015-01-29 10:25:34 -0800685
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686 }
Alan Viverette5435a302015-01-29 10:25:34 -0800687
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800688 /**
689 * Control how the popup operates with an input method: one of
690 * {@link #INPUT_METHOD_FROM_FOCUSABLE}, {@link #INPUT_METHOD_NEEDED},
691 * or {@link #INPUT_METHOD_NOT_NEEDED}.
Alan Viverette5435a302015-01-29 10:25:34 -0800692 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693 * <p>If the popup is showing, calling this method will take effect only
694 * the next time the popup is shown or through a manual call to one of
695 * the {@link #update()} methods.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800696 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800697 * @see #getInputMethodMode()
698 * @see #update()
699 */
700 public void setInputMethodMode(int mode) {
701 mInputMethodMode = mode;
702 }
Romain Guy374aaaed32009-07-14 15:11:59 -0700703
704 /**
705 * Sets the operating mode for the soft input area.
706 *
707 * @param mode The desired mode, see
708 * {@link android.view.WindowManager.LayoutParams#softInputMode}
709 * for the full list
710 *
711 * @see android.view.WindowManager.LayoutParams#softInputMode
712 * @see #getSoftInputMode()
713 */
714 public void setSoftInputMode(int mode) {
715 mSoftInputMode = mode;
716 }
717
718 /**
719 * Returns the current value in {@link #setSoftInputMode(int)}.
720 *
721 * @see #setSoftInputMode(int)
722 * @see android.view.WindowManager.LayoutParams#softInputMode
723 */
724 public int getSoftInputMode() {
725 return mSoftInputMode;
726 }
Alan Viverette5435a302015-01-29 10:25:34 -0800727
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800728 /**
729 * <p>Indicates whether the popup window receives touch events.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800730 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800731 * @return true if the popup is touchable, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800732 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800733 * @see #setTouchable(boolean)
734 */
735 public boolean isTouchable() {
736 return mTouchable;
737 }
738
739 /**
740 * <p>Changes the touchability of the popup window. When touchable, the
741 * window will receive touch events, otherwise touch events will go to the
742 * window below it. By default the window is touchable.</p>
743 *
744 * <p>If the popup is showing, calling this method will take effect only
745 * the next time the popup is shown or through a manual call to one of
746 * the {@link #update()} methods.</p>
747 *
748 * @param touchable true if the popup should receive touch events, false otherwise
749 *
750 * @see #isTouchable()
Alan Viverette5435a302015-01-29 10:25:34 -0800751 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800752 * @see #update()
753 */
754 public void setTouchable(boolean touchable) {
755 mTouchable = touchable;
756 }
757
758 /**
759 * <p>Indicates whether the popup window will be informed of touch events
760 * outside of its window.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800761 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800762 * @return true if the popup is outside touchable, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800763 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 * @see #setOutsideTouchable(boolean)
765 */
766 public boolean isOutsideTouchable() {
767 return mOutsideTouchable;
768 }
769
770 /**
771 * <p>Controls whether the pop-up will be informed of touch events outside
772 * of its window. This only makes sense for pop-ups that are touchable
773 * but not focusable, which means touches outside of the window will
774 * be delivered to the window behind. The default is false.</p>
775 *
776 * <p>If the popup is showing, calling this method will take effect only
777 * the next time the popup is shown or through a manual call to one of
778 * the {@link #update()} methods.</p>
779 *
780 * @param touchable true if the popup should receive outside
781 * touch events, false otherwise
782 *
783 * @see #isOutsideTouchable()
Alan Viverette5435a302015-01-29 10:25:34 -0800784 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 * @see #update()
786 */
787 public void setOutsideTouchable(boolean touchable) {
788 mOutsideTouchable = touchable;
789 }
790
791 /**
792 * <p>Indicates whether clipping of the popup window is enabled.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800793 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800794 * @return true if the clipping is enabled, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800795 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 * @see #setClippingEnabled(boolean)
797 */
798 public boolean isClippingEnabled() {
799 return mClippingEnabled;
800 }
801
802 /**
803 * <p>Allows the popup window to extend beyond the bounds of the screen. By default the
804 * window is clipped to the screen boundaries. Setting this to false will allow windows to be
805 * accurately positioned.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800806 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800807 * <p>If the popup is showing, calling this method will take effect only
808 * the next time the popup is shown or through a manual call to one of
809 * the {@link #update()} methods.</p>
810 *
811 * @param enabled false if the window should be allowed to extend outside of the screen
Alan Viverette5435a302015-01-29 10:25:34 -0800812 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800813 * @see #isClippingEnabled()
814 * @see #update()
815 */
816 public void setClippingEnabled(boolean enabled) {
817 mClippingEnabled = enabled;
818 }
819
820 /**
Adam Powell56c2d332010-11-05 20:03:03 -0700821 * Clip this popup window to the screen, but not to the containing window.
822 *
823 * @param enabled True to clip to the screen.
824 * @hide
825 */
826 public void setClipToScreenEnabled(boolean enabled) {
827 mClipToScreen = enabled;
828 setClippingEnabled(!enabled);
829 }
Adam Powell348e69c2011-02-16 16:49:50 -0800830
831 /**
832 * Allow PopupWindow to scroll the anchor's parent to provide more room
833 * for the popup. Enabled by default.
834 *
835 * @param enabled True to scroll the anchor's parent when more room is desired by the popup.
836 */
837 void setAllowScrollingAnchorParent(boolean enabled) {
838 mAllowScrollingAnchorParent = enabled;
839 }
Alan Viverette5435a302015-01-29 10:25:34 -0800840
Adam Powell56c2d332010-11-05 20:03:03 -0700841 /**
Jeff Brown01ce2e92010-09-26 22:20:12 -0700842 * <p>Indicates whether the popup window supports splitting touches.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800843 *
Jeff Brown01ce2e92010-09-26 22:20:12 -0700844 * @return true if the touch splitting is enabled, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800845 *
Jeff Brown01ce2e92010-09-26 22:20:12 -0700846 * @see #setSplitTouchEnabled(boolean)
Jeff Brown01ce2e92010-09-26 22:20:12 -0700847 */
848 public boolean isSplitTouchEnabled() {
Jeff Brown46e75292010-11-10 16:53:45 -0800849 if (mSplitTouchEnabled < 0 && mContext != null) {
850 return mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB;
851 }
852 return mSplitTouchEnabled == 1;
Jeff Brown01ce2e92010-09-26 22:20:12 -0700853 }
854
855 /**
856 * <p>Allows the popup window to split touches across other windows that also
Jeff Brown46e75292010-11-10 16:53:45 -0800857 * support split touch. When this flag is false, the first pointer
Jeff Brown01ce2e92010-09-26 22:20:12 -0700858 * that goes down determines the window to which all subsequent touches
Jeff Brown46e75292010-11-10 16:53:45 -0800859 * go until all pointers go up. When this flag is true, each pointer
Jeff Brown01ce2e92010-09-26 22:20:12 -0700860 * (not necessarily the first) that goes down determines the window
861 * to which all subsequent touches of that pointer will go until that
862 * pointer goes up thereby enabling touches with multiple pointers
863 * to be split across multiple windows.</p>
864 *
865 * @param enabled true if the split touches should be enabled, false otherwise
866 * @see #isSplitTouchEnabled()
Jeff Brown01ce2e92010-09-26 22:20:12 -0700867 */
868 public void setSplitTouchEnabled(boolean enabled) {
Jeff Brown46e75292010-11-10 16:53:45 -0800869 mSplitTouchEnabled = enabled ? 1 : 0;
Jeff Brown01ce2e92010-09-26 22:20:12 -0700870 }
871
872 /**
Adam Powellba0a2c32010-09-28 17:41:23 -0700873 * <p>Indicates whether the popup window will be forced into using absolute screen coordinates
874 * for positioning.</p>
875 *
876 * @return true if the window will always be positioned in screen coordinates.
877 * @hide
878 */
879 public boolean isLayoutInScreenEnabled() {
880 return mLayoutInScreen;
881 }
882
883 /**
884 * <p>Allows the popup window to force the flag
885 * {@link WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN}, overriding default behavior.
886 * This will cause the popup to be positioned in absolute screen coordinates.</p>
887 *
888 * @param enabled true if the popup should always be positioned in screen coordinates
889 * @hide
890 */
891 public void setLayoutInScreenEnabled(boolean enabled) {
892 mLayoutInScreen = enabled;
893 }
894
895 /**
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700896 * <p>Indicates whether the popup window will be attached in the decor frame of its parent
897 * window.
898 *
899 * @return true if the window will be attached to the decor frame of its parent window.
900 *
901 * @see #setAttachedInDecor(boolean)
902 * @see WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR
903 */
904 public boolean isAttachedInDecor() {
905 return mAttachedInDecor;
906 }
907
908 /**
909 * <p>This will attach the popup window to the decor frame of the parent window to avoid
910 * overlaping with screen decorations like the navigation bar. Overrides the default behavior of
911 * the flag {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR}.
912 *
913 * <p>By default the flag is set on SDK version {@link Build.VERSION_CODES#LOLLIPOP_MR1} or
914 * greater and cleared on lesser SDK versions.
915 *
916 * @param enabled true if the popup should be attached to the decor frame of its parent window.
917 *
918 * @see WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR
919 */
920 public void setAttachedInDecor(boolean enabled) {
921 mAttachedInDecor = enabled;
922 mAttachedInDecorSet = true;
923 }
924
925 /**
Adam Powell0bd1d0a2011-07-22 19:35:06 -0700926 * Allows the popup window to force the flag
927 * {@link WindowManager.LayoutParams#FLAG_LAYOUT_INSET_DECOR}, overriding default behavior.
928 * This will cause the popup to inset its content to account for system windows overlaying
929 * the screen, such as the status bar.
930 *
931 * <p>This will often be combined with {@link #setLayoutInScreenEnabled(boolean)}.
932 *
933 * @param enabled true if the popup's views should inset content to account for system windows,
934 * the way that decor views behave for full-screen windows.
935 * @hide
936 */
937 public void setLayoutInsetDecor(boolean enabled) {
938 mLayoutInsetDecor = enabled;
939 }
940
941 /**
Alan Viverette80ebe0d2015-04-30 15:53:11 -0700942 * Set the layout type for this window.
943 * <p>
944 * See {@link WindowManager.LayoutParams#type} for possible values.
Adam Powell574b37e2010-10-07 11:15:19 -0700945 *
946 * @param layoutType Layout type for this window.
Chris Banes36344a92015-04-14 10:43:16 +0100947 *
948 * @see WindowManager.LayoutParams#type
Adam Powell574b37e2010-10-07 11:15:19 -0700949 */
950 public void setWindowLayoutType(int layoutType) {
951 mWindowLayoutType = layoutType;
952 }
953
954 /**
Chris Banes36344a92015-04-14 10:43:16 +0100955 * Returns the layout type for this window.
956 *
957 * @see #setWindowLayoutType(int)
Adam Powell574b37e2010-10-07 11:15:19 -0700958 */
959 public int getWindowLayoutType() {
960 return mWindowLayoutType;
961 }
962
963 /**
Adam Powelle0b6cd12011-09-28 22:06:11 -0700964 * Set whether this window is touch modal or if outside touches will be sent to
965 * other windows behind it.
966 * @hide
967 */
968 public void setTouchModal(boolean touchModal) {
969 mNotTouchModal = !touchModal;
970 }
971
972 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800973 * <p>Change the width and height measure specs that are given to the
974 * window manager by the popup. By default these are 0, meaning that
975 * the current width or height is requested as an explicit size from
976 * the window manager. You can supply
Alan Viverette5435a302015-01-29 10:25:34 -0800977 * {@link ViewGroup.LayoutParams#WRAP_CONTENT} or
Romain Guy980a9382010-01-08 15:06:28 -0800978 * {@link ViewGroup.LayoutParams#MATCH_PARENT} to have that measure
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800979 * spec supplied instead, replacing the absolute width and height that
980 * has been set in the popup.</p>
981 *
982 * <p>If the popup is showing, calling this method will take effect only
983 * the next time the popup is shown.</p>
984 *
985 * @param widthSpec an explicit width measure spec mode, either
986 * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
Romain Guy980a9382010-01-08 15:06:28 -0800987 * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 * width.
989 * @param heightSpec an explicit height measure spec mode, either
990 * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
Romain Guy980a9382010-01-08 15:06:28 -0800991 * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800992 * height.
Alan Viverette259c2842015-03-22 17:39:39 -0700993 *
994 * @deprecated Use {@link #setWidth(int)} and {@link #setHeight(int)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 */
Alan Viverette259c2842015-03-22 17:39:39 -0700996 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 public void setWindowLayoutMode(int widthSpec, int heightSpec) {
998 mWidthMode = widthSpec;
999 mHeightMode = heightSpec;
1000 }
Alan Viverette5435a302015-01-29 10:25:34 -08001001
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001002 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001003 * Returns the popup's height MeasureSpec.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 *
1005 * @return the height MeasureSpec of the popup
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001006 * @see #setHeight(int)
1007 */
1008 public int getHeight() {
1009 return mHeight;
1010 }
1011
1012 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001013 * Sets the popup's height MeasureSpec.
1014 * <p>
1015 * If the popup is showing, calling this method will take effect the next
1016 * time the popup is shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017 *
1018 * @param height the height MeasureSpec of the popup
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019 * @see #getHeight()
Alan Viverette5435a302015-01-29 10:25:34 -08001020 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001021 */
1022 public void setHeight(int height) {
1023 mHeight = height;
1024 }
1025
1026 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001027 * Returns the popup's width MeasureSpec.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001028 *
1029 * @return the width MeasureSpec of the popup
Alan Viverette5435a302015-01-29 10:25:34 -08001030 * @see #setWidth(int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001031 */
1032 public int getWidth() {
1033 return mWidth;
1034 }
1035
1036 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001037 * Sets the popup's width MeasureSpec.
1038 * <p>
1039 * If the popup is showing, calling this method will take effect the next
1040 * time the popup is shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001041 *
1042 * @param width the width MeasureSpec of the popup
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001043 * @see #getWidth()
1044 * @see #isShowing()
1045 */
1046 public void setWidth(int width) {
1047 mWidth = width;
1048 }
1049
1050 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001051 * Sets whether the popup window should overlap its anchor view when
1052 * displayed as a drop-down.
1053 * <p>
1054 * If the popup is showing, calling this method will take effect only
1055 * the next time the popup is shown.
1056 *
1057 * @param overlapAnchor Whether the popup should overlap its anchor.
1058 *
1059 * @see #getOverlapAnchor()
1060 * @see #isShowing()
1061 */
1062 public void setOverlapAnchor(boolean overlapAnchor) {
1063 mOverlapAnchor = overlapAnchor;
1064 }
1065
1066 /**
1067 * Returns whether the popup window should overlap its anchor view when
1068 * displayed as a drop-down.
1069 *
1070 * @return Whether the popup should overlap its anchor.
1071 *
1072 * @see #setOverlapAnchor(boolean)
1073 */
1074 public boolean getOverlapAnchor() {
1075 return mOverlapAnchor;
1076 }
1077
1078 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001079 * <p>Indicate whether this popup window is showing on screen.</p>
1080 *
1081 * @return true if the popup is showing, false otherwise
1082 */
1083 public boolean isShowing() {
1084 return mIsShowing;
1085 }
1086
1087 /**
1088 * <p>
1089 * Display the content view in a popup window at the specified location. If the popup window
1090 * cannot fit on screen, it will be clipped. See {@link android.view.WindowManager.LayoutParams}
1091 * for more information on how gravity and the x and y parameters are related. Specifying
1092 * a gravity of {@link android.view.Gravity#NO_GRAVITY} is similar to specifying
1093 * <code>Gravity.LEFT | Gravity.TOP</code>.
1094 * </p>
Alan Viverette5435a302015-01-29 10:25:34 -08001095 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001096 * @param parent a parent view to get the {@link android.view.View#getWindowToken()} token from
1097 * @param gravity the gravity which controls the placement of the popup window
1098 * @param x the popup's x location offset
1099 * @param y the popup's y location offset
1100 */
1101 public void showAtLocation(View parent, int gravity, int x, int y) {
Adam Powell8ee6d7c2011-09-18 14:59:28 -07001102 showAtLocation(parent.getWindowToken(), gravity, x, y);
1103 }
1104
1105 /**
1106 * Display the content view in a popup window at the specified location.
1107 *
1108 * @param token Window token to use for creating the new window
1109 * @param gravity the gravity which controls the placement of the popup window
1110 * @param x the popup's x location offset
1111 * @param y the popup's y location offset
1112 *
1113 * @hide Internal use only. Applications should use
1114 * {@link #showAtLocation(View, int, int, int)} instead.
1115 */
1116 public void showAtLocation(IBinder token, int gravity, int x, int y) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001117 if (isShowing() || mContentView == null) {
1118 return;
1119 }
1120
Alan Viverettee025ed22015-02-02 11:27:21 -08001121 TransitionManager.endTransitions(mDecorView);
1122
Alan Viverettef50df432016-03-24 14:08:24 -04001123 detachFromAnchor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001124
1125 mIsShowing = true;
1126 mIsDropdown = false;
1127
Alan Viverettee025ed22015-02-02 11:27:21 -08001128 final WindowManager.LayoutParams p = createPopupLayoutParams(token);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001129 preparePopup(p);
Alan Viverettee025ed22015-02-02 11:27:21 -08001130
1131 // Only override the default if some gravity was specified.
1132 if (gravity != Gravity.NO_GRAVITY) {
1133 p.gravity = gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001134 }
Alan Viverettee025ed22015-02-02 11:27:21 -08001135
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001136 p.x = x;
1137 p.y = y;
Alan Viverettee025ed22015-02-02 11:27:21 -08001138
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001139 invokePopup(p);
1140 }
1141
1142 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001143 * Display the content view in a popup window anchored to the bottom-left
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001144 * corner of the anchor view. If there is not enough room on screen to show
1145 * the popup in its entirety, this method tries to find a parent scroll
Alan Viverette75d83792015-01-07 15:51:54 -08001146 * view to scroll. If no parent scroll view can be scrolled, the
1147 * bottom-left corner of the popup is pinned at the top left corner of the
1148 * anchor view.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001149 *
1150 * @param anchor the view on which to pin the popup window
1151 *
1152 * @see #dismiss()
1153 */
1154 public void showAsDropDown(View anchor) {
1155 showAsDropDown(anchor, 0, 0);
1156 }
1157
1158 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001159 * Display the content view in a popup window anchored to the bottom-left
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001160 * corner of the anchor view offset by the specified x and y coordinates.
Alan Viverette75d83792015-01-07 15:51:54 -08001161 * If there is not enough room on screen to show the popup in its entirety,
1162 * this method tries to find a parent scroll view to scroll. If no parent
1163 * scroll view can be scrolled, the bottom-left corner of the popup is
1164 * pinned at the top left corner of the anchor view.
1165 * <p>
1166 * If the view later scrolls to move <code>anchor</code> to a different
1167 * location, the popup will be moved correspondingly.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001168 *
1169 * @param anchor the view on which to pin the popup window
Adam Powell54c94de2013-09-26 15:36:34 -07001170 * @param xoff A horizontal offset from the anchor in pixels
1171 * @param yoff A vertical offset from the anchor in pixels
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001172 *
1173 * @see #dismiss()
1174 */
1175 public void showAsDropDown(View anchor, int xoff, int yoff) {
Adam Powell54c94de2013-09-26 15:36:34 -07001176 showAsDropDown(anchor, xoff, yoff, DEFAULT_ANCHORED_GRAVITY);
1177 }
1178
1179 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001180 * Displays the content view in a popup window anchored to the corner of
1181 * another view. The window is positioned according to the specified
1182 * gravity and offset by the specified x and y coordinates.
1183 * <p>
1184 * If there is not enough room on screen to show the popup in its entirety,
1185 * this method tries to find a parent scroll view to scroll. If no parent
1186 * view can be scrolled, the specified vertical gravity will be ignored and
1187 * the popup will anchor itself such that it is visible.
1188 * <p>
1189 * If the view later scrolls to move <code>anchor</code> to a different
1190 * location, the popup will be moved correspondingly.
Adam Powell54c94de2013-09-26 15:36:34 -07001191 *
1192 * @param anchor the view on which to pin the popup window
1193 * @param xoff A horizontal offset from the anchor in pixels
1194 * @param yoff A vertical offset from the anchor in pixels
1195 * @param gravity Alignment of the popup relative to the anchor
1196 *
1197 * @see #dismiss()
1198 */
1199 public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001200 if (isShowing() || mContentView == null) {
1201 return;
1202 }
1203
Alan Viverettee025ed22015-02-02 11:27:21 -08001204 TransitionManager.endTransitions(mDecorView);
1205
Alan Viverettef50df432016-03-24 14:08:24 -04001206 attachToAnchor(anchor, xoff, yoff, gravity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001207
1208 mIsShowing = true;
1209 mIsDropdown = true;
1210
Alan Viverettee025ed22015-02-02 11:27:21 -08001211 final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001212 preparePopup(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001213
Alan Viverettef50df432016-03-24 14:08:24 -04001214 final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff,
1215 p.width, p.height, gravity);
Alan Viverettee025ed22015-02-02 11:27:21 -08001216 updateAboveAnchor(aboveAnchor);
Phil Weaver396d5492016-03-22 17:53:50 -07001217 p.accessibilityIdOfAnchor = (anchor != null) ? anchor.getAccessibilityViewId() : -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001218
1219 invokePopup(p);
1220 }
1221
Romain Guy3e141682010-03-08 17:44:40 -08001222 private void updateAboveAnchor(boolean aboveAnchor) {
1223 if (aboveAnchor != mAboveAnchor) {
1224 mAboveAnchor = aboveAnchor;
1225
Alan Viverette697804e2015-08-06 12:36:47 -04001226 if (mBackground != null && mBackgroundView != null) {
1227 // If the background drawable provided was a StateListDrawable
1228 // with above-anchor and below-anchor states, use those.
1229 // Otherwise, rely on refreshDrawableState to do the job.
Romain Guy3e141682010-03-08 17:44:40 -08001230 if (mAboveAnchorBackgroundDrawable != null) {
1231 if (mAboveAnchor) {
Alan Viverette697804e2015-08-06 12:36:47 -04001232 mBackgroundView.setBackground(mAboveAnchorBackgroundDrawable);
Romain Guy3e141682010-03-08 17:44:40 -08001233 } else {
Alan Viverette697804e2015-08-06 12:36:47 -04001234 mBackgroundView.setBackground(mBelowAnchorBackgroundDrawable);
Romain Guy3e141682010-03-08 17:44:40 -08001235 }
1236 } else {
Alan Viverette697804e2015-08-06 12:36:47 -04001237 mBackgroundView.refreshDrawableState();
Romain Guy3e141682010-03-08 17:44:40 -08001238 }
1239 }
1240 }
1241 }
1242
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001243 /**
1244 * Indicates whether the popup is showing above (the y coordinate of the popup's bottom
1245 * is less than the y coordinate of the anchor) or below the anchor view (the y coordinate
1246 * of the popup is greater than y coordinate of the anchor's bottom).
1247 *
1248 * The value returned
1249 * by this method is meaningful only after {@link #showAsDropDown(android.view.View)}
1250 * or {@link #showAsDropDown(android.view.View, int, int)} was invoked.
1251 *
1252 * @return True if this popup is showing above the anchor view, false otherwise.
1253 */
1254 public boolean isAboveAnchor() {
1255 return mAboveAnchor;
1256 }
1257
1258 /**
Alan Viverettee025ed22015-02-02 11:27:21 -08001259 * Prepare the popup by embedding it into a new ViewGroup if the background
1260 * drawable is not null. If embedding is required, the layout parameters'
1261 * height is modified to take into account the background's padding.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001262 *
1263 * @param p the layout parameters of the popup's content view
1264 */
1265 private void preparePopup(WindowManager.LayoutParams p) {
Romain Guy448ecf52009-05-14 16:03:42 -07001266 if (mContentView == null || mContext == null || mWindowManager == null) {
1267 throw new IllegalStateException("You must specify a valid content view by "
1268 + "calling setContentView() before attempting to show the popup.");
1269 }
1270
Alan Viverette8fd949e2015-03-11 12:21:30 -07001271 // The old decor view may be transitioning out. Make sure it finishes
1272 // and cleans up before we try to create another one.
1273 if (mDecorView != null) {
1274 mDecorView.cancelTransitions();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001275 }
Alan Viveretteccb11e12014-07-08 16:04:02 -07001276
Alan Viverette8fd949e2015-03-11 12:21:30 -07001277 // When a background is available, we embed the content view within
1278 // another view that owns the background drawable.
Alan Viverette8fd949e2015-03-11 12:21:30 -07001279 if (mBackground != null) {
Alan Viverette697804e2015-08-06 12:36:47 -04001280 mBackgroundView = createBackgroundView(mContentView);
1281 mBackgroundView.setBackground(mBackground);
Alan Viverette8fd949e2015-03-11 12:21:30 -07001282 } else {
Alan Viverette697804e2015-08-06 12:36:47 -04001283 mBackgroundView = mContentView;
Alan Viverette8fd949e2015-03-11 12:21:30 -07001284 }
1285
Alan Viverette697804e2015-08-06 12:36:47 -04001286 mDecorView = createDecorView(mBackgroundView);
Alan Viverette5435a302015-01-29 10:25:34 -08001287
1288 // The background owner should be elevated so that it casts a shadow.
Alan Viverette697804e2015-08-06 12:36:47 -04001289 mBackgroundView.setElevation(mElevation);
Alan Viverette5435a302015-01-29 10:25:34 -08001290
1291 // We may wrap that in another view, so we'll need to manually specify
1292 // the surface insets.
Alan Viverette697804e2015-08-06 12:36:47 -04001293 final int surfaceInset = (int) Math.ceil(mBackgroundView.getZ() * 2);
Alan Viverette5435a302015-01-29 10:25:34 -08001294 p.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
1295 p.hasManualSurfaceInsets = true;
1296
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001297 mPopupViewInitialLayoutDirectionInherited =
Alan Viverette5435a302015-01-29 10:25:34 -08001298 (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001299 }
1300
1301 /**
Alan Viverette5435a302015-01-29 10:25:34 -08001302 * Wraps a content view in a PopupViewContainer.
1303 *
1304 * @param contentView the content view to wrap
1305 * @return a PopupViewContainer that wraps the content view
1306 */
1307 private PopupBackgroundView createBackgroundView(View contentView) {
1308 final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
1309 final int height;
1310 if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
1311 height = ViewGroup.LayoutParams.WRAP_CONTENT;
1312 } else {
1313 height = ViewGroup.LayoutParams.MATCH_PARENT;
1314 }
1315
1316 final PopupBackgroundView backgroundView = new PopupBackgroundView(mContext);
1317 final PopupBackgroundView.LayoutParams listParams = new PopupBackgroundView.LayoutParams(
1318 ViewGroup.LayoutParams.MATCH_PARENT, height);
1319 backgroundView.addView(contentView, listParams);
1320
1321 return backgroundView;
1322 }
1323
1324 /**
1325 * Wraps a content view in a FrameLayout.
1326 *
1327 * @param contentView the content view to wrap
1328 * @return a FrameLayout that wraps the content view
1329 */
1330 private PopupDecorView createDecorView(View contentView) {
1331 final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
1332 final int height;
1333 if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
1334 height = ViewGroup.LayoutParams.WRAP_CONTENT;
1335 } else {
1336 height = ViewGroup.LayoutParams.MATCH_PARENT;
1337 }
1338
1339 final PopupDecorView decorView = new PopupDecorView(mContext);
1340 decorView.addView(contentView, ViewGroup.LayoutParams.MATCH_PARENT, height);
1341 decorView.setClipChildren(false);
1342 decorView.setClipToPadding(false);
1343
1344 return decorView;
1345 }
1346
1347 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001348 * <p>Invoke the popup window by adding the content view to the window
1349 * manager.</p>
1350 *
1351 * <p>The content view must be non-null when this method is invoked.</p>
1352 *
1353 * @param p the layout parameters of the popup's content view
1354 */
1355 private void invokePopup(WindowManager.LayoutParams p) {
Romain Guy0c0b7682011-05-16 11:54:09 -07001356 if (mContext != null) {
1357 p.packageName = mContext.getPackageName();
1358 }
Alan Viverette5435a302015-01-29 10:25:34 -08001359
Alan Viverette8fd949e2015-03-11 12:21:30 -07001360 final PopupDecorView decorView = mDecorView;
1361 decorView.setFitsSystemWindows(mLayoutInsetDecor);
Alan Viverette8fd949e2015-03-11 12:21:30 -07001362
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001363 setLayoutDirectionFromAnchor();
Alan Viverette5435a302015-01-29 10:25:34 -08001364
Alan Viverette8fd949e2015-03-11 12:21:30 -07001365 mWindowManager.addView(decorView, p);
Alan Viverette95888c02015-04-16 13:27:50 -07001366
1367 if (mEnterTransition != null) {
1368 decorView.requestEnterTransition(mEnterTransition);
1369 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001370 }
1371
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001372 private void setLayoutDirectionFromAnchor() {
1373 if (mAnchor != null) {
1374 View anchor = mAnchor.get();
1375 if (anchor != null && mPopupViewInitialLayoutDirectionInherited) {
Alan Viverette5435a302015-01-29 10:25:34 -08001376 mDecorView.setLayoutDirection(anchor.getLayoutDirection());
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001377 }
1378 }
1379 }
1380
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001381 /**
1382 * <p>Generate the layout parameters for the popup window.</p>
1383 *
1384 * @param token the window token used to bind the popup's window
1385 *
1386 * @return the layout parameters to pass to the window manager
1387 */
Alan Viverettee025ed22015-02-02 11:27:21 -08001388 private WindowManager.LayoutParams createPopupLayoutParams(IBinder token) {
1389 final WindowManager.LayoutParams p = new WindowManager.LayoutParams();
1390
1391 // These gravity settings put the view at the top left corner of the
1392 // screen. The view is then positioned to the appropriate location by
1393 // setting the x and y offsets to match the anchor's bottom-left
1394 // corner.
Fabrice Di Meglioaac0d4e2012-07-19 19:21:26 -07001395 p.gravity = Gravity.START | Gravity.TOP;
Alan Viverettee025ed22015-02-02 11:27:21 -08001396 p.flags = computeFlags(p.flags);
1397 p.type = mWindowLayoutType;
1398 p.token = token;
1399 p.softInputMode = mSoftInputMode;
1400 p.windowAnimations = computeAnimationResource();
1401
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001402 if (mBackground != null) {
1403 p.format = mBackground.getOpacity();
1404 } else {
1405 p.format = PixelFormat.TRANSLUCENT;
1406 }
Alan Viverettee025ed22015-02-02 11:27:21 -08001407
1408 if (mHeightMode < 0) {
1409 p.height = mLastHeight = mHeightMode;
1410 } else {
1411 p.height = mLastHeight = mHeight;
1412 }
1413
1414 if (mWidthMode < 0) {
1415 p.width = mLastWidth = mWidthMode;
1416 } else {
1417 p.width = mLastWidth = mWidth;
1418 }
1419
Wale Ogunwale8216eb22015-12-18 10:42:42 -08001420 p.privateFlags = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH
1421 | PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
Robert Carra1eb4392015-12-10 12:43:51 -08001422
Alan Viverettee025ed22015-02-02 11:27:21 -08001423 // Used for debugging.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001424 p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
1425
1426 return p;
1427 }
1428
1429 private int computeFlags(int curFlags) {
1430 curFlags &= ~(
1431 WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES |
1432 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
1433 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
1434 WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
1435 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
Adam Powellba0a2c32010-09-28 17:41:23 -07001436 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
1437 WindowManager.LayoutParams.FLAG_SPLIT_TOUCH);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001438 if(mIgnoreCheekPress) {
1439 curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
1440 }
1441 if (!mFocusable) {
1442 curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1443 if (mInputMethodMode == INPUT_METHOD_NEEDED) {
1444 curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1445 }
1446 } else if (mInputMethodMode == INPUT_METHOD_NOT_NEEDED) {
1447 curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1448 }
1449 if (!mTouchable) {
1450 curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
1451 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001452 if (mOutsideTouchable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001453 curFlags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
1454 }
1455 if (!mClippingEnabled) {
1456 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
1457 }
Jeff Brown46e75292010-11-10 16:53:45 -08001458 if (isSplitTouchEnabled()) {
Jeff Brown01ce2e92010-09-26 22:20:12 -07001459 curFlags |= WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
1460 }
Adam Powellba0a2c32010-09-28 17:41:23 -07001461 if (mLayoutInScreen) {
1462 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
1463 }
Adam Powell0bd1d0a2011-07-22 19:35:06 -07001464 if (mLayoutInsetDecor) {
1465 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
1466 }
Adam Powelle0b6cd12011-09-28 22:06:11 -07001467 if (mNotTouchModal) {
1468 curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
1469 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -07001470 if (mAttachedInDecor) {
1471 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR;
1472 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001473 return curFlags;
1474 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -07001475
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001476 private int computeAnimationResource() {
Alan Viverette5435a302015-01-29 10:25:34 -08001477 if (mAnimationStyle == ANIMATION_STYLE_DEFAULT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001478 if (mIsDropdown) {
1479 return mAboveAnchor
1480 ? com.android.internal.R.style.Animation_DropDownUp
1481 : com.android.internal.R.style.Animation_DropDownDown;
1482 }
1483 return 0;
1484 }
1485 return mAnimationStyle;
1486 }
Alan Viverette560f1702014-05-05 14:40:07 -07001487
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001488 /**
Alan Viverette560f1702014-05-05 14:40:07 -07001489 * Positions the popup window on screen. When the popup window is too tall
1490 * to fit under the anchor, a parent scroll view is seeked and scrolled up
1491 * to reclaim space. If scrolling is not possible or not enough, the popup
1492 * window gets moved on top of the anchor.
1493 * <p>
Alan Viverettef50df432016-03-24 14:08:24 -04001494 * The results of positioning are placed in {@code outParams}.
Alan Viverette5435a302015-01-29 10:25:34 -08001495 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001496 * @param anchor the view on which the popup window must be anchored
Alan Viverettef50df432016-03-24 14:08:24 -04001497 * @param outParams the layout parameters used to display the drop down
Alan Viverette6acf2f92016-03-24 17:11:37 -04001498 * @param xOffset absolute horizontal offset from the top of the anchor
1499 * @param yOffset absolute vertical offset from the top of the anchor
Alan Viverette560f1702014-05-05 14:40:07 -07001500 * @param gravity horizontal gravity specifying popup alignment
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001501 * @return true if the popup is translated upwards to fit on screen
1502 */
Alan Viverettef50df432016-03-24 14:08:24 -04001503 private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams,
1504 int xOffset, int yOffset, int width, int height, int gravity) {
Adam Powell62e2bde2011-08-15 15:50:05 -07001505 final int anchorHeight = anchor.getHeight();
Alan Viverette560f1702014-05-05 14:40:07 -07001506 final int anchorWidth = anchor.getWidth();
1507 if (mOverlapAnchor) {
Alan Viverettef50df432016-03-24 14:08:24 -04001508 yOffset -= anchorHeight;
Alan Viverette560f1702014-05-05 14:40:07 -07001509 }
1510
Alan Viverette6acf2f92016-03-24 17:11:37 -04001511 // Initially, align to the bottom-left corner of the anchor plus offsets.
Alan Viverettef50df432016-03-24 14:08:24 -04001512 final int[] drawingLocation = mTmpDrawingLocation;
1513 anchor.getLocationInWindow(drawingLocation);
1514 outParams.x = drawingLocation[0] + xOffset;
1515 outParams.y = drawingLocation[1] + anchorHeight + yOffset;
Adam Powell54c94de2013-09-26 15:36:34 -07001516
Alan Viverette6acf2f92016-03-24 17:11:37 -04001517 // If we need to adjust for gravity RIGHT, align to the bottom-right
1518 // corner of the anchor (still accounting for offsets).
Alan Viverette560f1702014-05-05 14:40:07 -07001519 final int hgrav = Gravity.getAbsoluteGravity(gravity, anchor.getLayoutDirection())
1520 & Gravity.HORIZONTAL_GRAVITY_MASK;
Adam Powell54c94de2013-09-26 15:36:34 -07001521 if (hgrav == Gravity.RIGHT) {
Alan Viverettef50df432016-03-24 14:08:24 -04001522 outParams.x -= width - anchorWidth;
Adam Powell54c94de2013-09-26 15:36:34 -07001523 }
Alan Viverette560f1702014-05-05 14:40:07 -07001524
Alan Viverette6acf2f92016-03-24 17:11:37 -04001525 // Let the window manager know to align the top to y.
Alan Viverettef50df432016-03-24 14:08:24 -04001526 outParams.gravity = Gravity.LEFT | Gravity.TOP;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001527
Alan Viverettef50df432016-03-24 14:08:24 -04001528 final int[] screenLocation = mTmpScreenLocation;
1529 anchor.getLocationOnScreen(screenLocation);
Adam Powell54c94de2013-09-26 15:36:34 -07001530
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001531 final Rect displayFrame = new Rect();
1532 anchor.getWindowVisibleDisplayFrame(displayFrame);
Adam Powell62e2bde2011-08-15 15:50:05 -07001533
Alan Viverette6acf2f92016-03-24 17:11:37 -04001534 boolean onTop = false;
1535
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001536 final View root = anchor.getRootView();
Alan Viverette6acf2f92016-03-24 17:11:37 -04001537 final int screenY = screenLocation[1] + anchorHeight + yOffset;
1538 final boolean tooFarDown = screenY + height > displayFrame.bottom;
1539 final boolean tooFarRight = outParams.x + width > root.getWidth();
1540 if (tooFarDown || tooFarRight) {
1541 // If the popup extends beyond the visible area, try to scroll the
1542 // parent so that it is fully visible.
Adam Powellb7c1b202011-02-17 12:03:09 -08001543 if (mAllowScrollingAnchorParent) {
Alan Viverette560f1702014-05-05 14:40:07 -07001544 final int scrollX = anchor.getScrollX();
1545 final int scrollY = anchor.getScrollY();
Alan Viverettef50df432016-03-24 14:08:24 -04001546 final Rect r = new Rect(scrollX, scrollY, scrollX + width + xOffset,
1547 scrollY + height + anchorHeight + yOffset);
Adam Powellb7c1b202011-02-17 12:03:09 -08001548 anchor.requestRectangleOnScreen(r, true);
1549 }
Romain Guy3e141682010-03-08 17:44:40 -08001550
Alan Viverette6acf2f92016-03-24 17:11:37 -04001551 // Update for the new anchor position.
Alan Viverettef50df432016-03-24 14:08:24 -04001552 anchor.getLocationInWindow(drawingLocation);
1553 outParams.x = drawingLocation[0] + xOffset;
1554 outParams.y = drawingLocation[1] + anchorHeight + yOffset;
Adam Powell54c94de2013-09-26 15:36:34 -07001555
Alan Viverette560f1702014-05-05 14:40:07 -07001556 // Preserve the gravity adjustment.
Adam Powell54c94de2013-09-26 15:36:34 -07001557 if (hgrav == Gravity.RIGHT) {
Alan Viverettef50df432016-03-24 14:08:24 -04001558 outParams.x -= width - anchorWidth;
Adam Powell54c94de2013-09-26 15:36:34 -07001559 }
Alan Viverette560f1702014-05-05 14:40:07 -07001560
Alan Viverette6acf2f92016-03-24 17:11:37 -04001561 final int newScreenY = screenLocation[1] + anchorHeight + yOffset;
1562 final boolean stillTooFarDown = newScreenY + height > displayFrame.bottom;
1563 if (stillTooFarDown) {
1564 // If the popup is still too far down, re-evaluate the space
1565 // available and decide whether the pop-up will go above or
1566 // below the anchor.
1567 anchor.getLocationOnScreen(screenLocation);
Alan Viverettef50df432016-03-24 14:08:24 -04001568
Alan Viverette6acf2f92016-03-24 17:11:37 -04001569 final int below = displayFrame.bottom - screenLocation[1] - anchorHeight - yOffset;
1570 final int above = screenLocation[1] - displayFrame.top + yOffset;
1571 onTop = above > below;
1572
Oren Blasberged391262015-09-01 12:12:51 -07001573 if (onTop) {
Alan Viverette6acf2f92016-03-24 17:11:37 -04001574 // Move everything up.
1575 if (mOverlapAnchor) {
1576 yOffset += anchorHeight;
1577 }
1578 outParams.y = drawingLocation[1] - height + yOffset;
Oren Blasberged391262015-09-01 12:12:51 -07001579 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001580 }
1581 }
1582
Adam Powell56c2d332010-11-05 20:03:03 -07001583 if (mClipToScreen) {
Alan Viverette6acf2f92016-03-24 17:11:37 -04001584 // Use screen coordinates for comparison against display frame.
Alan Viverettef50df432016-03-24 14:08:24 -04001585 final int winOffsetX = screenLocation[0] - drawingLocation[0];
1586 final int winOffsetY = screenLocation[1] - drawingLocation[1];
1587 outParams.x += winOffsetX;
1588 outParams.y += winOffsetY;
1589
1590 final int right = outParams.x + width;
Chong Zhang7c9732d2015-12-29 14:50:05 -08001591 if (right > displayFrame.right) {
Alan Viverette6acf2f92016-03-24 17:11:37 -04001592 // The popup is too far right, move it back in.
Alan Viverettef50df432016-03-24 14:08:24 -04001593 outParams.x -= right - displayFrame.right;
Adam Powell56c2d332010-11-05 20:03:03 -07001594 }
Alan Viverette560f1702014-05-05 14:40:07 -07001595
Alan Viverettef50df432016-03-24 14:08:24 -04001596 if (outParams.x < displayFrame.left) {
Alan Viverette6acf2f92016-03-24 17:11:37 -04001597 // The popup is too far left, move it back in and clip if it's
1598 // still too large.
Alan Viverettef50df432016-03-24 14:08:24 -04001599 outParams.x = displayFrame.left;
1600
Alan Viverette6acf2f92016-03-24 17:11:37 -04001601 final int displayFrameWidth = displayFrame.width();
Alan Viverettef50df432016-03-24 14:08:24 -04001602 width = Math.min(width, displayFrameWidth);
Adam Powell56c2d332010-11-05 20:03:03 -07001603 }
1604
Alan Viverette6acf2f92016-03-24 17:11:37 -04001605 final int bottom = outParams.y + height;
1606 if (bottom > displayFrame.bottom) {
1607 // The popup is too far down, move it back in.
1608 outParams.y -= bottom - displayFrame.bottom;
1609 }
1610
1611 if (outParams.y < displayFrame.top) {
1612 // The popup is too far up, move it back in and clip if
1613 // it's still too large.
1614 outParams.y = displayFrame.top;
1615
1616 final int displayFrameHeight = displayFrame.height();
1617 height = Math.min(height, displayFrameHeight);
Adam Powell5f83a602011-01-19 17:58:04 -08001618 }
Alan Viverettef50df432016-03-24 14:08:24 -04001619
1620 outParams.x -= winOffsetX;
1621 outParams.y -= winOffsetY;
Adam Powell56c2d332010-11-05 20:03:03 -07001622 }
1623
Alan Viverettef50df432016-03-24 14:08:24 -04001624 outParams.width = width;
1625 outParams.height = height;
Alan Viverette560f1702014-05-05 14:40:07 -07001626
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001627 return onTop;
1628 }
Alan Viverette5435a302015-01-29 10:25:34 -08001629
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001630 /**
1631 * Returns the maximum height that is available for the popup to be
1632 * completely shown. It is recommended that this height be the maximum for
1633 * the popup's height, otherwise it is possible that the popup will be
1634 * clipped.
Alan Viverette5435a302015-01-29 10:25:34 -08001635 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001636 * @param anchor The view on which the popup window must be anchored.
1637 * @return The maximum available height for the popup to be completely
1638 * shown.
1639 */
Alan Viveretteb854d072015-09-28 16:12:18 -04001640 public int getMaxAvailableHeight(@NonNull View anchor) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001641 return getMaxAvailableHeight(anchor, 0);
1642 }
1643
1644 /**
1645 * Returns the maximum height that is available for the popup to be
1646 * completely shown. It is recommended that this height be the maximum for
1647 * the popup's height, otherwise it is possible that the popup will be
1648 * clipped.
1649 *
1650 * @param anchor The view on which the popup window must be anchored.
1651 * @param yOffset y offset from the view's bottom edge
1652 * @return The maximum available height for the popup to be completely
1653 * shown.
1654 */
Alan Viveretteb854d072015-09-28 16:12:18 -04001655 public int getMaxAvailableHeight(@NonNull View anchor, int yOffset) {
Mike LeBeau98acd542009-05-07 19:04:39 -07001656 return getMaxAvailableHeight(anchor, yOffset, false);
1657 }
Alan Viverette5435a302015-01-29 10:25:34 -08001658
Mike LeBeau98acd542009-05-07 19:04:39 -07001659 /**
1660 * Returns the maximum height that is available for the popup to be
1661 * completely shown, optionally ignoring any bottom decorations such as
1662 * the input method. It is recommended that this height be the maximum for
1663 * the popup's height, otherwise it is possible that the popup will be
1664 * clipped.
Alan Viverette5435a302015-01-29 10:25:34 -08001665 *
Mike LeBeau98acd542009-05-07 19:04:39 -07001666 * @param anchor The view on which the popup window must be anchored.
1667 * @param yOffset y offset from the view's bottom edge
1668 * @param ignoreBottomDecorations if true, the height returned will be
1669 * all the way to the bottom of the display, ignoring any
1670 * bottom decorations
1671 * @return The maximum available height for the popup to be completely
1672 * shown.
Mike LeBeau98acd542009-05-07 19:04:39 -07001673 */
Alan Viveretteb854d072015-09-28 16:12:18 -04001674 public int getMaxAvailableHeight(
1675 @NonNull View anchor, int yOffset, boolean ignoreBottomDecorations) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001676 final Rect displayFrame = new Rect();
Jorim Jaggi81758462016-02-29 14:41:09 +01001677 if (ignoreBottomDecorations) {
1678 anchor.getWindowDisplayFrame(displayFrame);
1679 } else {
1680 anchor.getWindowVisibleDisplayFrame(displayFrame);
1681 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001682
Alan Viverettef50df432016-03-24 14:08:24 -04001683 final int[] anchorPos = mTmpDrawingLocation;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001684 anchor.getLocationOnScreen(anchorPos);
Alan Viverette5435a302015-01-29 10:25:34 -08001685
Jorim Jaggi81758462016-02-29 14:41:09 +01001686 final int bottomEdge = displayFrame.bottom;
Oren Blasberged391262015-09-01 12:12:51 -07001687
1688 final int distanceToBottom;
1689 if (mOverlapAnchor) {
1690 distanceToBottom = bottomEdge - anchorPos[1] - yOffset;
1691 } else {
1692 distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
1693 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001694 final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
1695
1696 // anchorPos[1] is distance from anchor to top of screen
1697 int returnedHeight = Math.max(distanceToBottom, distanceToTop);
1698 if (mBackground != null) {
1699 mBackground.getPadding(mTempRect);
Alan Viverette5435a302015-01-29 10:25:34 -08001700 returnedHeight -= mTempRect.top + mTempRect.bottom;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001701 }
Alan Viverette5435a302015-01-29 10:25:34 -08001702
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001703 return returnedHeight;
1704 }
Alan Viverette5435a302015-01-29 10:25:34 -08001705
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001706 /**
Alan Viverette7878edf2015-02-03 15:49:18 -08001707 * Disposes of the popup window. This method can be invoked only after
1708 * {@link #showAsDropDown(android.view.View)} has been executed. Failing
1709 * that, calling this method will have no effect.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001710 *
Alan Viverette5435a302015-01-29 10:25:34 -08001711 * @see #showAsDropDown(android.view.View)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001712 */
1713 public void dismiss() {
Alan Viverette8fd949e2015-03-11 12:21:30 -07001714 if (!isShowing() || mIsTransitioningToDismiss) {
Alan Viverettee025ed22015-02-02 11:27:21 -08001715 return;
1716 }
Svetoslav Ganov06f938e2011-11-10 14:31:37 -08001717
Alan Viverette8fd949e2015-03-11 12:21:30 -07001718 final PopupDecorView decorView = mDecorView;
1719 final View contentView = mContentView;
1720
1721 final ViewGroup contentHolder;
1722 final ViewParent contentParent = contentView.getParent();
1723 if (contentParent instanceof ViewGroup) {
1724 contentHolder = ((ViewGroup) contentParent);
1725 } else {
1726 contentHolder = null;
1727 }
1728
1729 // Ensure any ongoing or pending transitions are canceled.
1730 decorView.cancelTransitions();
1731
Alan Viverettee025ed22015-02-02 11:27:21 -08001732 mIsShowing = false;
Alan Viverette8fd949e2015-03-11 12:21:30 -07001733 mIsTransitioningToDismiss = true;
Craig Mautnerb82d0742012-05-23 08:48:39 -07001734
Alan Viverette634a8082016-02-03 14:22:41 -05001735 // This method may be called as part of window detachment, in which
1736 // case the anchor view (and its root) will still return true from
1737 // isAttachedToWindow() during execution of this method; however, we
1738 // can expect the OnAttachStateChangeListener to have been called prior
1739 // to executing this method, so we can rely on that instead.
Alan Viverette95888c02015-04-16 13:27:50 -07001740 final Transition exitTransition = mExitTransition;
Alan Viverette21d36182016-02-24 15:34:11 -05001741 if (mIsAnchorRootAttached && exitTransition != null && decorView.isLaidOut()) {
Yohei Yukawadbd299d2016-03-06 22:27:40 -08001742 // The decor view is non-interactive and non-IME-focusable during exit transitions.
Alan Viverette95888c02015-04-16 13:27:50 -07001743 final LayoutParams p = (LayoutParams) decorView.getLayoutParams();
1744 p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
1745 p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
Yohei Yukawadbd299d2016-03-06 22:27:40 -08001746 p.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
Alan Viverette95888c02015-04-16 13:27:50 -07001747 mWindowManager.updateViewLayout(decorView, p);
1748
Alan Viverette634a8082016-02-03 14:22:41 -05001749 // Once we start dismissing the decor view, all state (including
1750 // the anchor root) needs to be moved to the decor view since we
1751 // may open another popup while it's busy exiting.
1752 final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
Alan Viverette91098572016-01-19 14:07:31 -05001753 final Rect epicenter = getTransitionEpicenter();
Alan Viverette95888c02015-04-16 13:27:50 -07001754 exitTransition.setEpicenterCallback(new EpicenterCallback() {
1755 @Override
1756 public Rect onGetEpicenter(Transition transition) {
1757 return epicenter;
1758 }
1759 });
Alan Viverette634a8082016-02-03 14:22:41 -05001760 decorView.startExitTransition(exitTransition, anchorRoot,
1761 new TransitionListenerAdapter() {
1762 @Override
1763 public void onTransitionEnd(Transition transition) {
Alan Viverette79708942016-02-25 16:57:08 -05001764 dismissImmediate(decorView, contentHolder, contentView);
Alan Viverette634a8082016-02-03 14:22:41 -05001765 }
1766 });
Alan Viverettee025ed22015-02-02 11:27:21 -08001767 } else {
Alan Viverette79708942016-02-25 16:57:08 -05001768 dismissImmediate(decorView, contentHolder, contentView);
Alan Viverette7878edf2015-02-03 15:49:18 -08001769 }
1770
Alan Viverette95888c02015-04-16 13:27:50 -07001771 // Clears the anchor view.
Alan Viverettef50df432016-03-24 14:08:24 -04001772 detachFromAnchor();
Alan Viverette79708942016-02-25 16:57:08 -05001773
1774 if (mOnDismissListener != null) {
1775 mOnDismissListener.onDismiss();
1776 }
Alan Viverette5435a302015-01-29 10:25:34 -08001777 }
1778
Alan Viverette91098572016-01-19 14:07:31 -05001779 /**
1780 * Returns the window-relative epicenter bounds to be used by enter and
1781 * exit transitions.
1782 * <p>
1783 * <strong>Note:</strong> This is distinct from the rect passed to
1784 * {@link #setEpicenterBounds(Rect)}, which is anchor-relative.
1785 *
1786 * @return the window-relative epicenter bounds to be used by enter and
1787 * exit transitions
1788 */
1789 private Rect getTransitionEpicenter() {
Alan Viverette95888c02015-04-16 13:27:50 -07001790 final View anchor = mAnchor != null ? mAnchor.get() : null;
1791 final View decor = mDecorView;
1792 if (anchor == null || decor == null) {
1793 return null;
1794 }
1795
1796 final int[] anchorLocation = anchor.getLocationOnScreen();
1797 final int[] popupLocation = mDecorView.getLocationOnScreen();
1798
1799 // Compute the position of the anchor relative to the popup.
1800 final Rect bounds = new Rect(0, 0, anchor.getWidth(), anchor.getHeight());
1801 bounds.offset(anchorLocation[0] - popupLocation[0], anchorLocation[1] - popupLocation[1]);
Alan Viverette91098572016-01-19 14:07:31 -05001802
1803 // Use anchor-relative epicenter, if specified.
1804 if (mEpicenterBounds != null) {
1805 final int offsetX = bounds.left;
1806 final int offsetY = bounds.top;
1807 bounds.set(mEpicenterBounds);
1808 bounds.offset(offsetX, offsetY);
1809 }
1810
Alan Viverette95888c02015-04-16 13:27:50 -07001811 return bounds;
1812 }
1813
Alan Viverette5435a302015-01-29 10:25:34 -08001814 /**
1815 * Removes the popup from the window manager and tears down the supporting
1816 * view hierarchy, if necessary.
1817 */
Alan Viverette79708942016-02-25 16:57:08 -05001818 private void dismissImmediate(View decorView, ViewGroup contentHolder, View contentView) {
Alan Viverette8fd949e2015-03-11 12:21:30 -07001819 // If this method gets called and the decor view doesn't have a parent,
1820 // then it was either never added or was already removed. That should
1821 // never happen, but it's worth checking to avoid potential crashes.
1822 if (decorView.getParent() != null) {
1823 mWindowManager.removeViewImmediate(decorView);
Alan Viverettedf4639a02015-03-02 12:40:34 -08001824 }
1825
Alan Viverette8fd949e2015-03-11 12:21:30 -07001826 if (contentHolder != null) {
1827 contentHolder.removeView(contentView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001828 }
Alan Viverette8fd949e2015-03-11 12:21:30 -07001829
1830 // This needs to stay until after all transitions have ended since we
1831 // need the reference to cancel transitions in preparePopup().
1832 mDecorView = null;
Alan Viverette697804e2015-08-06 12:36:47 -04001833 mBackgroundView = null;
Alan Viverette8fd949e2015-03-11 12:21:30 -07001834 mIsTransitioningToDismiss = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001835 }
1836
1837 /**
1838 * Sets the listener to be called when the window is dismissed.
Alan Viverette5435a302015-01-29 10:25:34 -08001839 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001840 * @param onDismissListener The listener.
1841 */
1842 public void setOnDismissListener(OnDismissListener onDismissListener) {
1843 mOnDismissListener = onDismissListener;
1844 }
Alan Viverette5435a302015-01-29 10:25:34 -08001845
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001846 /**
1847 * Updates the state of the popup window, if it is currently being displayed,
Alan Viverette259c2842015-03-22 17:39:39 -07001848 * from the currently set state.
1849 * <p>
1850 * This includes:
1851 * <ul>
1852 * <li>{@link #setClippingEnabled(boolean)}</li>
1853 * <li>{@link #setFocusable(boolean)}</li>
1854 * <li>{@link #setIgnoreCheekPress()}</li>
1855 * <li>{@link #setInputMethodMode(int)}</li>
1856 * <li>{@link #setTouchable(boolean)}</li>
1857 * <li>{@link #setAnimationStyle(int)}</li>
1858 * </ul>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001859 */
1860 public void update() {
1861 if (!isShowing() || mContentView == null) {
1862 return;
1863 }
Alan Viverette5435a302015-01-29 10:25:34 -08001864
1865 final WindowManager.LayoutParams p =
1866 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
1867
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001868 boolean update = false;
Alan Viverette5435a302015-01-29 10:25:34 -08001869
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001870 final int newAnim = computeAnimationResource();
1871 if (newAnim != p.windowAnimations) {
1872 p.windowAnimations = newAnim;
1873 update = true;
1874 }
1875
1876 final int newFlags = computeFlags(p.flags);
1877 if (newFlags != p.flags) {
1878 p.flags = newFlags;
1879 update = true;
1880 }
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001881
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001882 if (update) {
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001883 setLayoutDirectionFromAnchor();
Alan Viverette5435a302015-01-29 10:25:34 -08001884 mWindowManager.updateViewLayout(mDecorView, p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001885 }
1886 }
Romain Guyd6a463a2009-05-21 23:10:10 -07001887
1888 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001889 * Updates the dimension of the popup window.
1890 * <p>
1891 * Calling this function also updates the window with the current popup
1892 * state as described for {@link #update()}.
Romain Guyd6a463a2009-05-21 23:10:10 -07001893 *
Alan Viverette259c2842015-03-22 17:39:39 -07001894 * @param width the new width, must be >= 0 or -1 to ignore
1895 * @param height the new height, must be >= 0 or -1 to ignore
Romain Guyd6a463a2009-05-21 23:10:10 -07001896 */
1897 public void update(int width, int height) {
Alan Viverette5435a302015-01-29 10:25:34 -08001898 final WindowManager.LayoutParams p =
1899 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
Romain Guyd6a463a2009-05-21 23:10:10 -07001900 update(p.x, p.y, width, height, false);
1901 }
Alan Viverette5435a302015-01-29 10:25:34 -08001902
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001903 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001904 * Updates the position and the dimension of the popup window.
1905 * <p>
1906 * Width and height can be set to -1 to update location only. Calling this
1907 * function also updates the window with the current popup state as
1908 * described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001909 *
1910 * @param x the new x location
1911 * @param y the new y location
Alan Viverette259c2842015-03-22 17:39:39 -07001912 * @param width the new width, must be >= 0 or -1 to ignore
1913 * @param height the new height, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001914 */
1915 public void update(int x, int y, int width, int height) {
1916 update(x, y, width, height, false);
1917 }
1918
1919 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001920 * Updates the position and the dimension of the popup window.
1921 * <p>
1922 * Width and height can be set to -1 to update location only. Calling this
1923 * function also updates the window with the current popup state as
1924 * described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001925 *
1926 * @param x the new x location
1927 * @param y the new y location
Alan Viverette259c2842015-03-22 17:39:39 -07001928 * @param width the new width, must be >= 0 or -1 to ignore
1929 * @param height the new height, must be >= 0 or -1 to ignore
1930 * @param force {@code true} to reposition the window even if the specified
1931 * position already seems to correspond to the LayoutParams,
1932 * {@code false} to only reposition if needed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001933 */
1934 public void update(int x, int y, int width, int height, boolean force) {
Alan Viverette259c2842015-03-22 17:39:39 -07001935 if (width >= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001936 mLastWidth = width;
1937 setWidth(width);
1938 }
1939
Alan Viverette259c2842015-03-22 17:39:39 -07001940 if (height >= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001941 mLastHeight = height;
1942 setHeight(height);
1943 }
1944
1945 if (!isShowing() || mContentView == null) {
1946 return;
1947 }
1948
Alan Viverette5435a302015-01-29 10:25:34 -08001949 final WindowManager.LayoutParams p =
1950 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001951
1952 boolean update = force;
1953
1954 final int finalWidth = mWidthMode < 0 ? mWidthMode : mLastWidth;
1955 if (width != -1 && p.width != finalWidth) {
1956 p.width = mLastWidth = finalWidth;
1957 update = true;
1958 }
1959
1960 final int finalHeight = mHeightMode < 0 ? mHeightMode : mLastHeight;
1961 if (height != -1 && p.height != finalHeight) {
1962 p.height = mLastHeight = finalHeight;
1963 update = true;
1964 }
1965
1966 if (p.x != x) {
1967 p.x = x;
1968 update = true;
1969 }
1970
1971 if (p.y != y) {
1972 p.y = y;
1973 update = true;
1974 }
1975
1976 final int newAnim = computeAnimationResource();
1977 if (newAnim != p.windowAnimations) {
1978 p.windowAnimations = newAnim;
1979 update = true;
1980 }
1981
1982 final int newFlags = computeFlags(p.flags);
1983 if (newFlags != p.flags) {
1984 p.flags = newFlags;
1985 update = true;
1986 }
Mike LeBeau98acd542009-05-07 19:04:39 -07001987
Phil Weaver396d5492016-03-22 17:53:50 -07001988 int newAccessibilityIdOfAnchor =
1989 (mAnchor != null) ? mAnchor.get().getAccessibilityViewId() : -1;
1990 if (newAccessibilityIdOfAnchor != p.accessibilityIdOfAnchor) {
1991 p.accessibilityIdOfAnchor = newAccessibilityIdOfAnchor;
1992 update = true;
1993 }
1994
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001995 if (update) {
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001996 setLayoutDirectionFromAnchor();
Alan Viverette5435a302015-01-29 10:25:34 -08001997 mWindowManager.updateViewLayout(mDecorView, p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001998 }
1999 }
2000
2001 /**
Alan Viverette259c2842015-03-22 17:39:39 -07002002 * Updates the position and the dimension of the popup window.
2003 * <p>
2004 * Calling this function also updates the window with the current popup
2005 * state as described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002006 *
2007 * @param anchor the popup's anchor view
Alan Viverette259c2842015-03-22 17:39:39 -07002008 * @param width the new width, must be >= 0 or -1 to ignore
2009 * @param height the new height, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002010 */
2011 public void update(View anchor, int width, int height) {
Alan Viveretteb91d6d02016-03-29 14:55:04 -04002012 update(anchor, false, 0, 0, width, height);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002013 }
2014
2015 /**
Alan Viverette259c2842015-03-22 17:39:39 -07002016 * Updates the position and the dimension of the popup window.
2017 * <p>
2018 * Width and height can be set to -1 to update location only. Calling this
2019 * function also updates the window with the current popup state as
2020 * described for {@link #update()}.
2021 * <p>
2022 * If the view later scrolls to move {@code anchor} to a different
2023 * location, the popup will be moved correspondingly.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002024 *
2025 * @param anchor the popup's anchor view
2026 * @param xoff x offset from the view's left edge
2027 * @param yoff y offset from the view's bottom edge
Alan Viverette259c2842015-03-22 17:39:39 -07002028 * @param width the new width, must be >= 0 or -1 to ignore
2029 * @param height the new height, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002030 */
2031 public void update(View anchor, int xoff, int yoff, int width, int height) {
Alan Viveretteb91d6d02016-03-29 14:55:04 -04002032 update(anchor, true, xoff, yoff, width, height);
The Android Open Source Project10592532009-03-18 17:39:46 -07002033 }
2034
2035 private void update(View anchor, boolean updateLocation, int xoff, int yoff,
Alan Viveretteb91d6d02016-03-29 14:55:04 -04002036 int width, int height) {
The Android Open Source Project10592532009-03-18 17:39:46 -07002037
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002038 if (!isShowing() || mContentView == null) {
2039 return;
2040 }
2041
Alan Viverette75d83792015-01-07 15:51:54 -08002042 final WeakReference<View> oldAnchor = mAnchor;
Alan Viverettef50df432016-03-24 14:08:24 -04002043 final int gravity = mAnchoredGravity;
2044
Alan Viverette75d83792015-01-07 15:51:54 -08002045 final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff);
Gilles Debunne81f08082011-02-17 14:07:19 -08002046 if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) {
Alan Viverettef50df432016-03-24 14:08:24 -04002047 attachToAnchor(anchor, xoff, yoff, gravity);
Gilles Debunne81f08082011-02-17 14:07:19 -08002048 } else if (needsUpdate) {
2049 // No need to register again if this is a DropDown, showAsDropDown already did.
2050 mAnchorXoff = xoff;
2051 mAnchorYoff = yoff;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002052 }
2053
Alan Viverettef50df432016-03-24 14:08:24 -04002054 final LayoutParams p = (LayoutParams) mDecorView.getLayoutParams();
2055 final int oldGravity = p.gravity;
2056 final int oldWidth = p.width;
2057 final int oldHeight = p.height;
2058 final int oldX = p.x;
2059 final int oldY = p.y;
Alan Viverettef95b2d92016-03-22 11:23:05 -04002060
Alan Viveretteb91d6d02016-03-29 14:55:04 -04002061 // If an explicit width/height has not specified, use the most recent
2062 // explicitly specified value (either from setWidth/Height or update).
2063 if (width == -1) {
2064 width = mWidth;
2065 }
2066 if (height == -1) {
2067 height = mHeight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002068 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002069
Alan Viverettef50df432016-03-24 14:08:24 -04002070 final boolean aboveAnchor = findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
2071 width, height, gravity);
2072 updateAboveAnchor(aboveAnchor);
Fabrice Di Megliob003e282012-10-17 17:20:19 -07002073
Alan Viverettef50df432016-03-24 14:08:24 -04002074 final boolean paramsChanged = oldGravity != p.gravity || oldX != p.x || oldY != p.y
2075 || oldWidth != p.width || oldHeight != p.height;
2076 update(p.x, p.y, p.width, p.height, paramsChanged);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002077 }
2078
2079 /**
2080 * Listener that is called when this popup window is dismissed.
2081 */
2082 public interface OnDismissListener {
2083 /**
2084 * Called when this popup window is dismissed.
2085 */
2086 public void onDismiss();
2087 }
2088
Alan Viverettef50df432016-03-24 14:08:24 -04002089 private void detachFromAnchor() {
Alan Viverette634a8082016-02-03 14:22:41 -05002090 final View anchor = mAnchor != null ? mAnchor.get() : null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002091 if (anchor != null) {
Alan Viverettee025ed22015-02-02 11:27:21 -08002092 final ViewTreeObserver vto = anchor.getViewTreeObserver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002093 vto.removeOnScrollChangedListener(mOnScrollChangedListener);
2094 }
Alan Viverettee025ed22015-02-02 11:27:21 -08002095
Alan Viverette634a8082016-02-03 14:22:41 -05002096 final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
2097 if (anchorRoot != null) {
2098 anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
2099 }
2100
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002101 mAnchor = null;
Alan Viverette634a8082016-02-03 14:22:41 -05002102 mAnchorRoot = null;
2103 mIsAnchorRootAttached = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002104 }
2105
Alan Viverettef50df432016-03-24 14:08:24 -04002106 private void attachToAnchor(View anchor, int xoff, int yoff, int gravity) {
2107 detachFromAnchor();
Alan Viverettee025ed22015-02-02 11:27:21 -08002108
2109 final ViewTreeObserver vto = anchor.getViewTreeObserver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002110 if (vto != null) {
2111 vto.addOnScrollChangedListener(mOnScrollChangedListener);
2112 }
2113
Alan Viverette634a8082016-02-03 14:22:41 -05002114 final View anchorRoot = anchor.getRootView();
2115 anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
2116
2117 mAnchor = new WeakReference<>(anchor);
2118 mAnchorRoot = new WeakReference<>(anchorRoot);
2119 mIsAnchorRootAttached = anchorRoot.isAttachedToWindow();
2120
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002121 mAnchorXoff = xoff;
2122 mAnchorYoff = yoff;
Adam Powell54c94de2013-09-26 15:36:34 -07002123 mAnchoredGravity = gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002124 }
2125
Alan Viverette5435a302015-01-29 10:25:34 -08002126 private class PopupDecorView extends FrameLayout {
Alan Viverette8fd949e2015-03-11 12:21:30 -07002127 private TransitionListenerAdapter mPendingExitListener;
2128
Alan Viverette5435a302015-01-29 10:25:34 -08002129 public PopupDecorView(Context context) {
2130 super(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002131 }
2132
2133 @Override
2134 public boolean dispatchKeyEvent(KeyEvent event) {
2135 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
Per Andersson4ae02b32011-01-17 11:16:23 +01002136 if (getKeyDispatcherState() == null) {
2137 return super.dispatchKeyEvent(event);
2138 }
2139
Alan Viverette5435a302015-01-29 10:25:34 -08002140 if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
2141 final KeyEvent.DispatcherState state = getKeyDispatcherState();
Jeff Brownb3ea9222011-01-10 16:26:36 -08002142 if (state != null) {
2143 state.startTracking(event, this);
2144 }
Dianne Hackborn8d374262009-09-14 21:21:52 -07002145 return true;
Jeff Brownb3ea9222011-01-10 16:26:36 -08002146 } else if (event.getAction() == KeyEvent.ACTION_UP) {
Alan Viverette5435a302015-01-29 10:25:34 -08002147 final KeyEvent.DispatcherState state = getKeyDispatcherState();
Jeff Brownb3ea9222011-01-10 16:26:36 -08002148 if (state != null && state.isTracking(event) && !event.isCanceled()) {
2149 dismiss();
2150 return true;
2151 }
Dianne Hackborn8d374262009-09-14 21:21:52 -07002152 }
2153 return super.dispatchKeyEvent(event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002154 } else {
2155 return super.dispatchKeyEvent(event);
2156 }
2157 }
2158
2159 @Override
2160 public boolean dispatchTouchEvent(MotionEvent ev) {
2161 if (mTouchInterceptor != null && mTouchInterceptor.onTouch(this, ev)) {
2162 return true;
2163 }
2164 return super.dispatchTouchEvent(ev);
2165 }
2166
2167 @Override
2168 public boolean onTouchEvent(MotionEvent event) {
2169 final int x = (int) event.getX();
2170 final int y = (int) event.getY();
Alan Viverette5435a302015-01-29 10:25:34 -08002171
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002172 if ((event.getAction() == MotionEvent.ACTION_DOWN)
2173 && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
2174 dismiss();
2175 return true;
2176 } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
2177 dismiss();
2178 return true;
2179 } else {
2180 return super.onTouchEvent(event);
2181 }
2182 }
Alan Viverette8fd949e2015-03-11 12:21:30 -07002183
2184 /**
2185 * Requests that an enter transition run after the next layout pass.
2186 */
2187 public void requestEnterTransition(Transition transition) {
2188 final ViewTreeObserver observer = getViewTreeObserver();
2189 if (observer != null && transition != null) {
2190 final Transition enterTransition = transition.clone();
2191
2192 // Postpone the enter transition after the first layout pass.
2193 observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
2194 @Override
2195 public void onGlobalLayout() {
2196 final ViewTreeObserver observer = getViewTreeObserver();
2197 if (observer != null) {
2198 observer.removeOnGlobalLayoutListener(this);
2199 }
2200
Alan Viverette91098572016-01-19 14:07:31 -05002201 final Rect epicenter = getTransitionEpicenter();
Alan Viverette95888c02015-04-16 13:27:50 -07002202 enterTransition.setEpicenterCallback(new EpicenterCallback() {
2203 @Override
2204 public Rect onGetEpicenter(Transition transition) {
2205 return epicenter;
2206 }
2207 });
Alan Viverette8fd949e2015-03-11 12:21:30 -07002208 startEnterTransition(enterTransition);
2209 }
2210 });
2211 }
2212 }
2213
2214 /**
2215 * Starts the pending enter transition, if one is set.
2216 */
2217 private void startEnterTransition(Transition enterTransition) {
2218 final int count = getChildCount();
2219 for (int i = 0; i < count; i++) {
2220 final View child = getChildAt(i);
2221 enterTransition.addTarget(child);
2222 child.setVisibility(View.INVISIBLE);
2223 }
2224
2225 TransitionManager.beginDelayedTransition(this, enterTransition);
2226
2227 for (int i = 0; i < count; i++) {
2228 final View child = getChildAt(i);
2229 child.setVisibility(View.VISIBLE);
2230 }
2231 }
2232
2233 /**
2234 * Starts an exit transition immediately.
2235 * <p>
2236 * <strong>Note:</strong> The transition listener is guaranteed to have
2237 * its {@code onTransitionEnd} method called even if the transition
2238 * never starts; however, it may be called with a {@code null} argument.
2239 */
Alan Viverette634a8082016-02-03 14:22:41 -05002240 public void startExitTransition(Transition transition, final View anchorRoot,
2241 final TransitionListener listener) {
Alan Viverette8fd949e2015-03-11 12:21:30 -07002242 if (transition == null) {
2243 return;
2244 }
2245
Alan Viverette634a8082016-02-03 14:22:41 -05002246 // The anchor view's window may go away while we're executing our
2247 // transition, in which case we need to end the transition
2248 // immediately and execute the listener to remove the popup.
2249 anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
2250
Alan Viverette8fd949e2015-03-11 12:21:30 -07002251 // The exit listener MUST be called for cleanup, even if the
2252 // transition never starts or ends. Stash it for later.
2253 mPendingExitListener = new TransitionListenerAdapter() {
2254 @Override
2255 public void onTransitionEnd(Transition transition) {
Alan Viverette634a8082016-02-03 14:22:41 -05002256 anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
Alan Viverette8fd949e2015-03-11 12:21:30 -07002257 listener.onTransitionEnd(transition);
2258
2259 // The listener was called. Our job here is done.
2260 mPendingExitListener = null;
2261 }
2262 };
2263
2264 final Transition exitTransition = transition.clone();
2265 exitTransition.addListener(mPendingExitListener);
2266
2267 final int count = getChildCount();
2268 for (int i = 0; i < count; i++) {
2269 final View child = getChildAt(i);
2270 exitTransition.addTarget(child);
2271 }
2272
2273 TransitionManager.beginDelayedTransition(this, exitTransition);
2274
2275 for (int i = 0; i < count; i++) {
2276 final View child = getChildAt(i);
2277 child.setVisibility(View.INVISIBLE);
2278 }
2279 }
2280
2281 /**
2282 * Cancels all pending or current transitions.
2283 */
2284 public void cancelTransitions() {
2285 TransitionManager.endTransitions(this);
2286
2287 if (mPendingExitListener != null) {
2288 mPendingExitListener.onTransitionEnd(null);
2289 }
2290 }
Alan Viverette634a8082016-02-03 14:22:41 -05002291
2292 private final OnAttachStateChangeListener mOnAnchorRootDetachedListener =
2293 new OnAttachStateChangeListener() {
2294 @Override
2295 public void onViewAttachedToWindow(View v) {}
2296
2297 @Override
2298 public void onViewDetachedFromWindow(View v) {
2299 v.removeOnAttachStateChangeListener(this);
2300
2301 TransitionManager.endTransitions(PopupDecorView.this);
2302 }
2303 };
Alan Viverette5435a302015-01-29 10:25:34 -08002304 }
svetoslavganov75986cf2009-05-14 22:28:01 -07002305
Alan Viverette5435a302015-01-29 10:25:34 -08002306 private class PopupBackgroundView extends FrameLayout {
2307 public PopupBackgroundView(Context context) {
2308 super(context);
2309 }
2310
svetoslavganov75986cf2009-05-14 22:28:01 -07002311 @Override
Alan Viverette5435a302015-01-29 10:25:34 -08002312 protected int[] onCreateDrawableState(int extraSpace) {
2313 if (mAboveAnchor) {
2314 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
2315 View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET);
2316 return drawableState;
svetoslavganov75986cf2009-05-14 22:28:01 -07002317 } else {
Alan Viverette5435a302015-01-29 10:25:34 -08002318 return super.onCreateDrawableState(extraSpace);
svetoslavganov75986cf2009-05-14 22:28:01 -07002319 }
2320 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002321 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002322}