blob: df2f57589361604dcca28f03827e2892b08e7d73 [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
Wale Ogunwale8216eb22015-12-18 10:42:42 -080019import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
Robert Carra1eb4392015-12-10 12:43:51 -080020import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
21
Adam Powella7287f42010-08-17 21:17:04 -070022import com.android.internal.R;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023
Alan Viveretteb854d072015-09-28 16:12:18 -040024import android.annotation.NonNull;
svetoslavganov75986cf2009-05-14 22:28:01 -070025import android.content.Context;
26import android.content.res.TypedArray;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.graphics.PixelFormat;
28import android.graphics.Rect;
29import android.graphics.drawable.Drawable;
30import android.graphics.drawable.StateListDrawable;
Jeff Brown46e75292010-11-10 16:53:45 -080031import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.os.IBinder;
Alan Viverette5435a302015-01-29 10:25:34 -080033import android.transition.Transition;
34import android.transition.Transition.EpicenterCallback;
Alan Viverette8fd949e2015-03-11 12:21:30 -070035import android.transition.Transition.TransitionListener;
36import android.transition.Transition.TransitionListenerAdapter;
Alan Viverette5435a302015-01-29 10:25:34 -080037import android.transition.TransitionInflater;
38import android.transition.TransitionManager;
39import android.transition.TransitionSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.util.AttributeSet;
Adam Powellc3fa6302010-05-18 11:36:27 -070041import android.view.Gravity;
42import android.view.KeyEvent;
43import android.view.MotionEvent;
44import android.view.View;
Alan Viverette634a8082016-02-03 14:22:41 -050045import android.view.View.OnAttachStateChangeListener;
Adam Powella7287f42010-08-17 21:17:04 -070046import android.view.View.OnTouchListener;
Adam Powellc3fa6302010-05-18 11:36:27 -070047import android.view.ViewGroup;
Alan Viverette8fd949e2015-03-11 12:21:30 -070048import android.view.ViewParent;
Adam Powellc3fa6302010-05-18 11:36:27 -070049import android.view.ViewTreeObserver;
Alan Viverette8fd949e2015-03-11 12:21:30 -070050import android.view.ViewTreeObserver.OnGlobalLayoutListener;
Adam Powellc3fa6302010-05-18 11:36:27 -070051import android.view.ViewTreeObserver.OnScrollChangedListener;
Adam Powella7287f42010-08-17 21:17:04 -070052import android.view.WindowManager;
Alan Viverette259c2842015-03-22 17:39:39 -070053import android.view.WindowManager.LayoutParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054
Adam Powella7287f42010-08-17 21:17:04 -070055import java.lang.ref.WeakReference;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056
57/**
58 * <p>A popup window that can be used to display an arbitrary view. The popup
Scott Kennedy7ed189e2013-01-11 22:31:43 -080059 * window is a floating container that appears on top of the current
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 * activity.</p>
Alan Viverette5435a302015-01-29 10:25:34 -080061 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062 * @see android.widget.AutoCompleteTextView
63 * @see android.widget.Spinner
64 */
65public class PopupWindow {
66 /**
Romain Guye29f0642009-06-23 21:27:02 -070067 * Mode for {@link #setInputMethodMode(int)}: the requirements for the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068 * input method should be based on the focusability of the popup. That is
69 * if it is focusable than it needs to work with the input method, else
70 * it doesn't.
71 */
72 public static final int INPUT_METHOD_FROM_FOCUSABLE = 0;
Alan Viverette5435a302015-01-29 10:25:34 -080073
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 /**
Romain Guye29f0642009-06-23 21:27:02 -070075 * Mode for {@link #setInputMethodMode(int)}: this popup always needs to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 * work with an input method, regardless of whether it is focusable. This
77 * means that it will always be displayed so that the user can also operate
78 * the input method while it is shown.
79 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 public static final int INPUT_METHOD_NEEDED = 1;
Alan Viverette5435a302015-01-29 10:25:34 -080081
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 /**
Romain Guye29f0642009-06-23 21:27:02 -070083 * Mode for {@link #setInputMethodMode(int)}: this popup never needs to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 * work with an input method, regardless of whether it is focusable. This
85 * means that it will always be displayed to use as much space on the
86 * screen as needed, regardless of whether this covers the input method.
87 */
88 public static final int INPUT_METHOD_NOT_NEEDED = 2;
Adam Powell54c94de2013-09-26 15:36:34 -070089
90 private static final int DEFAULT_ANCHORED_GRAVITY = Gravity.TOP | Gravity.START;
91
Alan Viverette5435a302015-01-29 10:25:34 -080092 /**
93 * Default animation style indicating that separate animations should be
94 * used for top/bottom anchoring states.
95 */
96 private static final int ANIMATION_STYLE_DEFAULT = -1;
97
98 private final int[] mDrawingLocation = new int[2];
99 private final int[] mScreenLocation = new int[2];
100 private final Rect mTempRect = new Rect();
Alan Viverette5435a302015-01-29 10:25:34 -0800101
Romain Guy448ecf52009-05-14 16:03:42 -0700102 private Context mContext;
103 private WindowManager mWindowManager;
Alan Viverette5435a302015-01-29 10:25:34 -0800104
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 private boolean mIsShowing;
Alan Viverette8fd949e2015-03-11 12:21:30 -0700106 private boolean mIsTransitioningToDismiss;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 private boolean mIsDropdown;
108
Alan Viverette5435a302015-01-29 10:25:34 -0800109 /** View that handles event dispatch and content transitions. */
110 private PopupDecorView mDecorView;
111
Alan Viverette697804e2015-08-06 12:36:47 -0400112 /** View that holds the background and may animate during a transition. */
113 private View mBackgroundView;
114
115 /** The contents of the popup. May be identical to the background view. */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 private View mContentView;
Alan Viverette5435a302015-01-29 10:25:34 -0800117
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 private boolean mFocusable;
119 private int mInputMethodMode = INPUT_METHOD_FROM_FOCUSABLE;
Dianne Hackborn7eab0942011-01-01 13:21:50 -0800120 private int mSoftInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 private boolean mTouchable = true;
122 private boolean mOutsideTouchable = false;
123 private boolean mClippingEnabled = true;
Jeff Brown46e75292010-11-10 16:53:45 -0800124 private int mSplitTouchEnabled = -1;
Adam Powellba0a2c32010-09-28 17:41:23 -0700125 private boolean mLayoutInScreen;
Adam Powell56c2d332010-11-05 20:03:03 -0700126 private boolean mClipToScreen;
Adam Powell348e69c2011-02-16 16:49:50 -0800127 private boolean mAllowScrollingAnchorParent = true;
Adam Powell0bd1d0a2011-07-22 19:35:06 -0700128 private boolean mLayoutInsetDecor = false;
Adam Powelle0b6cd12011-09-28 22:06:11 -0700129 private boolean mNotTouchModal;
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700130 private boolean mAttachedInDecor = true;
131 private boolean mAttachedInDecorSet = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132
133 private OnTouchListener mTouchInterceptor;
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700134
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 private int mWidthMode;
Alan Viverette259c2842015-03-22 17:39:39 -0700136 private int mWidth = LayoutParams.WRAP_CONTENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 private int mLastWidth;
138 private int mHeightMode;
Alan Viverette259c2842015-03-22 17:39:39 -0700139 private int mHeight = LayoutParams.WRAP_CONTENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 private int mLastHeight;
141
142 private int mPopupWidth;
143 private int mPopupHeight;
Adam Powell56c2d332010-11-05 20:03:03 -0700144
Alan Viveretteccb11e12014-07-08 16:04:02 -0700145 private float mElevation;
146
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 private Drawable mBackground;
148 private Drawable mAboveAnchorBackgroundDrawable;
149 private Drawable mBelowAnchorBackgroundDrawable;
150
Alan Viverette5435a302015-01-29 10:25:34 -0800151 private Transition mEnterTransition;
152 private Transition mExitTransition;
Alan Viverette91098572016-01-19 14:07:31 -0500153 private Rect mEpicenterBounds;
Alan Viverette560f1702014-05-05 14:40:07 -0700154
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 private boolean mAboveAnchor;
Adam Powell574b37e2010-10-07 11:15:19 -0700156 private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
Alan Viverette5435a302015-01-29 10:25:34 -0800157
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800158 private OnDismissListener mOnDismissListener;
159 private boolean mIgnoreCheekPress = false;
160
Alan Viverette5435a302015-01-29 10:25:34 -0800161 private int mAnimationStyle = ANIMATION_STYLE_DEFAULT;
162
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 private static final int[] ABOVE_ANCHOR_STATE_SET = new int[] {
164 com.android.internal.R.attr.state_above_anchor
165 };
166
Alan Viverette634a8082016-02-03 14:22:41 -0500167 private final OnAttachStateChangeListener mOnAnchorRootDetachedListener =
168 new OnAttachStateChangeListener() {
169 @Override
170 public void onViewAttachedToWindow(View v) {}
171
172 @Override
173 public void onViewDetachedFromWindow(View v) {
174 mIsAnchorRootAttached = false;
175 }
176 };
177
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 private WeakReference<View> mAnchor;
Alan Viverette634a8082016-02-03 14:22:41 -0500179 private WeakReference<View> mAnchorRoot;
180 private boolean mIsAnchorRootAttached;
Alan Viverette560f1702014-05-05 14:40:07 -0700181
Alan Viverette5435a302015-01-29 10:25:34 -0800182 private final OnScrollChangedListener mOnScrollChangedListener = new OnScrollChangedListener() {
183 @Override
184 public void onScrollChanged() {
185 final View anchor = mAnchor != null ? mAnchor.get() : null;
186 if (anchor != null && mDecorView != null) {
187 final WindowManager.LayoutParams p = (WindowManager.LayoutParams)
188 mDecorView.getLayoutParams();
189
190 updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
191 mAnchoredGravity));
192 update(p.x, p.y, -1, -1, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 }
Alan Viverette5435a302015-01-29 10:25:34 -0800194 }
195 };
Alan Viverette560f1702014-05-05 14:40:07 -0700196
Alan Viverette5435a302015-01-29 10:25:34 -0800197 private int mAnchorXoff;
198 private int mAnchorYoff;
199 private int mAnchoredGravity;
Alan Viverette560f1702014-05-05 14:40:07 -0700200 private boolean mOverlapAnchor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201
Fabrice Di Megliob003e282012-10-17 17:20:19 -0700202 private boolean mPopupViewInitialLayoutDirectionInherited;
203
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 /**
205 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
206 *
207 * <p>The popup does provide a background.</p>
208 */
209 public PopupWindow(Context context) {
210 this(context, null);
211 }
212
213 /**
214 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
215 *
216 * <p>The popup does provide a background.</p>
217 */
218 public PopupWindow(Context context, AttributeSet attrs) {
219 this(context, attrs, com.android.internal.R.attr.popupWindowStyle);
220 }
221
222 /**
223 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
224 *
225 * <p>The popup does provide a background.</p>
226 */
Alan Viverette617feb92013-09-09 18:09:13 -0700227 public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
228 this(context, attrs, defStyleAttr, 0);
Adam Powellc3fa6302010-05-18 11:36:27 -0700229 }
Alan Viverette5435a302015-01-29 10:25:34 -0800230
Adam Powellc3fa6302010-05-18 11:36:27 -0700231 /**
232 * <p>Create a new, empty, non focusable popup window of dimension (0,0).</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800233 *
Adam Powellc3fa6302010-05-18 11:36:27 -0700234 * <p>The popup does not provide a background.</p>
235 */
236 public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 mContext = context;
Alan Viverette75d83792015-01-07 15:51:54 -0800238 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239
Alan Viverette617feb92013-09-09 18:09:13 -0700240 final TypedArray a = context.obtainStyledAttributes(
Alan Viverettece8c3582014-11-07 13:19:38 -0800241 attrs, R.styleable.PopupWindow, defStyleAttr, defStyleRes);
242 final Drawable bg = a.getDrawable(R.styleable.PopupWindow_popupBackground);
Alan Viveretteccb11e12014-07-08 16:04:02 -0700243 mElevation = a.getDimension(R.styleable.PopupWindow_popupElevation, 0);
Alan Viverette560f1702014-05-05 14:40:07 -0700244 mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false);
Adam Powellc3808b52010-10-04 10:06:59 -0700245
Alan Viverette5435a302015-01-29 10:25:34 -0800246 // Preserve default behavior from Gingerbread. If the animation is
247 // undefined or explicitly specifies the Gingerbread animation style,
248 // use a sentinel value.
249 if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupAnimationStyle)) {
250 final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, 0);
251 if (animStyle == R.style.Animation_PopupWindow) {
252 mAnimationStyle = ANIMATION_STYLE_DEFAULT;
253 } else {
254 mAnimationStyle = animStyle;
255 }
256 } else {
257 mAnimationStyle = ANIMATION_STYLE_DEFAULT;
258 }
259
260 final Transition enterTransition = getTransition(a.getResourceId(
261 R.styleable.PopupWindow_popupEnterTransition, 0));
262 final Transition exitTransition;
263 if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupExitTransition)) {
264 exitTransition = getTransition(a.getResourceId(
265 R.styleable.PopupWindow_popupExitTransition, 0));
266 } else {
267 exitTransition = enterTransition == null ? null : enterTransition.clone();
268 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 a.recycle();
Alan Viverettece8c3582014-11-07 13:19:38 -0800271
Alan Viverette5435a302015-01-29 10:25:34 -0800272 setEnterTransition(enterTransition);
273 setExitTransition(exitTransition);
Alan Viverettece8c3582014-11-07 13:19:38 -0800274 setBackgroundDrawable(bg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 }
276
277 /**
278 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
279 *
280 * <p>The popup does not provide any background. This should be handled
281 * by the content view.</p>
282 */
283 public PopupWindow() {
284 this(null, 0, 0);
285 }
286
287 /**
288 * <p>Create a new non focusable popup window which can display the
289 * <tt>contentView</tt>. The dimension of the window are (0,0).</p>
290 *
291 * <p>The popup does not provide any background. This should be handled
292 * by the content view.</p>
293 *
294 * @param contentView the popup's content
295 */
296 public PopupWindow(View contentView) {
297 this(contentView, 0, 0);
298 }
299
300 /**
301 * <p>Create a new empty, non focusable popup window. The dimension of the
302 * window must be passed to this constructor.</p>
303 *
304 * <p>The popup does not provide any background. This should be handled
305 * by the content view.</p>
306 *
307 * @param width the popup's width
308 * @param height the popup's height
309 */
310 public PopupWindow(int width, int height) {
311 this(null, width, height);
312 }
313
314 /**
315 * <p>Create a new non focusable popup window which can display the
316 * <tt>contentView</tt>. The dimension of the window must be passed to
317 * this constructor.</p>
318 *
319 * <p>The popup does not provide any background. This should be handled
320 * by the content view.</p>
321 *
322 * @param contentView the popup's content
323 * @param width the popup's width
324 * @param height the popup's height
325 */
326 public PopupWindow(View contentView, int width, int height) {
327 this(contentView, width, height, false);
328 }
329
330 /**
331 * <p>Create a new popup window which can display the <tt>contentView</tt>.
332 * The dimension of the window must be passed to this constructor.</p>
333 *
334 * <p>The popup does not provide any background. This should be handled
335 * by the content view.</p>
336 *
337 * @param contentView the popup's content
338 * @param width the popup's width
339 * @param height the popup's height
340 * @param focusable true if the popup can be focused, false otherwise
341 */
Romain Guy448ecf52009-05-14 16:03:42 -0700342 public PopupWindow(View contentView, int width, int height, boolean focusable) {
343 if (contentView != null) {
344 mContext = contentView.getContext();
345 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
346 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700347
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 setContentView(contentView);
349 setWidth(width);
350 setHeight(height);
351 setFocusable(focusable);
352 }
353
Alan Viverette5435a302015-01-29 10:25:34 -0800354 public void setEnterTransition(Transition enterTransition) {
355 mEnterTransition = enterTransition;
Alan Viverette5435a302015-01-29 10:25:34 -0800356 }
357
358 public void setExitTransition(Transition exitTransition) {
359 mExitTransition = exitTransition;
Alan Viverette5435a302015-01-29 10:25:34 -0800360 }
361
Alan Viverette91098572016-01-19 14:07:31 -0500362 /**
363 * Sets the bounds used as the epicenter of the enter and exit transitions.
364 * <p>
365 * Transitions use a point or Rect, referred to as the epicenter, to orient
366 * the direction of travel. For popup windows, the anchor view bounds are
367 * used as the default epicenter.
368 * <p>
369 * See {@link Transition#setEpicenterCallback(EpicenterCallback)} for more
370 * information about how transition epicenters.
371 *
372 * @param bounds the epicenter bounds relative to the anchor view, or
373 * {@code null} to use the default epicenter
374 * @see #getTransitionEpicenter()
375 * @hide
376 */
377 public void setEpicenterBounds(Rect bounds) {
378 mEpicenterBounds = bounds;
379 }
380
Alan Viverette5435a302015-01-29 10:25:34 -0800381 private Transition getTransition(int resId) {
382 if (resId != 0 && resId != R.transition.no_transition) {
383 final TransitionInflater inflater = TransitionInflater.from(mContext);
384 final Transition transition = inflater.inflateTransition(resId);
385 if (transition != null) {
386 final boolean isEmpty = transition instanceof TransitionSet
387 && ((TransitionSet) transition).getTransitionCount() == 0;
388 if (!isEmpty) {
389 return transition;
390 }
391 }
392 }
393 return null;
394 }
395
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700397 * Return the drawable used as the popup window's background.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 *
Alan Viveretteccb11e12014-07-08 16:04:02 -0700399 * @return the background drawable or {@code null} if not set
400 * @see #setBackgroundDrawable(Drawable)
401 * @attr ref android.R.styleable#PopupWindow_popupBackground
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 */
403 public Drawable getBackground() {
404 return mBackground;
405 }
406
407 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700408 * Specifies the background drawable for this popup window. The background
409 * can be set to {@code null}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 *
411 * @param background the popup's background
Alan Viveretteccb11e12014-07-08 16:04:02 -0700412 * @see #getBackground()
413 * @attr ref android.R.styleable#PopupWindow_popupBackground
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 */
415 public void setBackgroundDrawable(Drawable background) {
416 mBackground = background;
Alan Viverettece8c3582014-11-07 13:19:38 -0800417
418 // If this is a StateListDrawable, try to find and store the drawable to be
419 // used when the drop-down is placed above its anchor view, and the one to be
420 // used when the drop-down is placed below its anchor view. We extract
421 // the drawables ourselves to work around a problem with using refreshDrawableState
422 // that it will take into account the padding of all drawables specified in a
423 // StateListDrawable, thus adding superfluous padding to drop-down views.
424 //
425 // We assume a StateListDrawable will have a drawable for ABOVE_ANCHOR_STATE_SET and
426 // at least one other drawable, intended for the 'below-anchor state'.
427 if (mBackground instanceof StateListDrawable) {
428 StateListDrawable stateList = (StateListDrawable) mBackground;
429
430 // Find the above-anchor view - this one's easy, it should be labeled as such.
431 int aboveAnchorStateIndex = stateList.getStateDrawableIndex(ABOVE_ANCHOR_STATE_SET);
432
433 // Now, for the below-anchor view, look for any other drawable specified in the
434 // StateListDrawable which is not for the above-anchor state and use that.
435 int count = stateList.getStateCount();
436 int belowAnchorStateIndex = -1;
437 for (int i = 0; i < count; i++) {
438 if (i != aboveAnchorStateIndex) {
439 belowAnchorStateIndex = i;
440 break;
441 }
442 }
443
444 // Store the drawables we found, if we found them. Otherwise, set them both
445 // to null so that we'll just use refreshDrawableState.
446 if (aboveAnchorStateIndex != -1 && belowAnchorStateIndex != -1) {
447 mAboveAnchorBackgroundDrawable = stateList.getStateDrawable(aboveAnchorStateIndex);
448 mBelowAnchorBackgroundDrawable = stateList.getStateDrawable(belowAnchorStateIndex);
449 } else {
450 mBelowAnchorBackgroundDrawable = null;
451 mAboveAnchorBackgroundDrawable = null;
452 }
453 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 }
455
456 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700457 * @return the elevation for this popup window in pixels
458 * @see #setElevation(float)
459 * @attr ref android.R.styleable#PopupWindow_popupElevation
460 */
461 public float getElevation() {
462 return mElevation;
463 }
464
465 /**
466 * Specifies the elevation for this popup window.
467 *
468 * @param elevation the popup's elevation in pixels
469 * @see #getElevation()
470 * @attr ref android.R.styleable#PopupWindow_popupElevation
471 */
472 public void setElevation(float elevation) {
473 mElevation = elevation;
474 }
475
476 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800477 * <p>Return the animation style to use the popup appears and disappears</p>
478 *
479 * @return the animation style to use the popup appears and disappears
480 */
481 public int getAnimationStyle() {
482 return mAnimationStyle;
483 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700484
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 /**
Shuhrat Dehkanov54ec76d2014-08-28 17:12:23 +0900486 * Set the flag on popup to ignore cheek press events; by default this flag
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487 * is set to false
Shuhrat Dehkanov54ec76d2014-08-28 17:12:23 +0900488 * which means the popup will not ignore cheek press dispatch events.
Alan Viverette5435a302015-01-29 10:25:34 -0800489 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 * <p>If the popup is showing, calling this method will take effect only
491 * the next time the popup is shown or through a manual call to one of
492 * the {@link #update()} methods.</p>
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700493 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 * @see #update()
495 */
496 public void setIgnoreCheekPress() {
497 mIgnoreCheekPress = true;
498 }
Alan Viverette5435a302015-01-29 10:25:34 -0800499
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500
501 /**
502 * <p>Change the animation style resource for this popup.</p>
503 *
504 * <p>If the popup is showing, calling this method will take effect only
505 * the next time the popup is shown or through a manual call to one of
506 * the {@link #update()} methods.</p>
507 *
508 * @param animationStyle animation style to use when the popup appears
509 * and disappears. Set to -1 for the default animation, 0 for no
510 * animation, or a resource identifier for an explicit animation.
Alan Viverette5435a302015-01-29 10:25:34 -0800511 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512 * @see #update()
513 */
514 public void setAnimationStyle(int animationStyle) {
515 mAnimationStyle = animationStyle;
516 }
Alan Viverette5435a302015-01-29 10:25:34 -0800517
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800518 /**
519 * <p>Return the view used as the content of the popup window.</p>
520 *
521 * @return a {@link android.view.View} representing the popup's content
522 *
523 * @see #setContentView(android.view.View)
524 */
525 public View getContentView() {
526 return mContentView;
527 }
528
529 /**
530 * <p>Change the popup's content. The content is represented by an instance
531 * of {@link android.view.View}.</p>
532 *
Gilles Debunne81f08082011-02-17 14:07:19 -0800533 * <p>This method has no effect if called when the popup is showing.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800534 *
535 * @param contentView the new content for the popup
536 *
537 * @see #getContentView()
538 * @see #isShowing()
539 */
540 public void setContentView(View contentView) {
541 if (isShowing()) {
542 return;
543 }
544
545 mContentView = contentView;
Romain Guy448ecf52009-05-14 16:03:42 -0700546
Romain Guy0c0b7682011-05-16 11:54:09 -0700547 if (mContext == null && mContentView != null) {
Romain Guy448ecf52009-05-14 16:03:42 -0700548 mContext = mContentView.getContext();
549 }
550
Romain Guy0c0b7682011-05-16 11:54:09 -0700551 if (mWindowManager == null && mContentView != null) {
Romain Guy448ecf52009-05-14 16:03:42 -0700552 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
553 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700554
555 // Setting the default for attachedInDecor based on SDK version here
556 // instead of in the constructor since we might not have the context
557 // object in the constructor. We only want to set default here if the
558 // app hasn't already set the attachedInDecor.
559 if (mContext != null && !mAttachedInDecorSet) {
560 // Attach popup window in decor frame of parent window by default for
561 // {@link Build.VERSION_CODES.LOLLIPOP_MR1} or greater. Keep current
562 // behavior of not attaching to decor frame for older SDKs.
563 setAttachedInDecor(mContext.getApplicationInfo().targetSdkVersion
564 >= Build.VERSION_CODES.LOLLIPOP_MR1);
565 }
566
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567 }
568
569 /**
570 * Set a callback for all touch events being dispatched to the popup
571 * window.
572 */
573 public void setTouchInterceptor(OnTouchListener l) {
574 mTouchInterceptor = l;
575 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700576
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577 /**
578 * <p>Indicate whether the popup window can grab the focus.</p>
579 *
580 * @return true if the popup is focusable, false otherwise
581 *
582 * @see #setFocusable(boolean)
583 */
584 public boolean isFocusable() {
585 return mFocusable;
586 }
587
588 /**
589 * <p>Changes the focusability of the popup window. When focusable, the
590 * window will grab the focus from the current focused widget if the popup
591 * contains a focusable {@link android.view.View}. By default a popup
592 * window is not focusable.</p>
593 *
594 * <p>If the popup is showing, calling this method will take effect only
595 * the next time the popup is shown or through a manual call to one of
596 * the {@link #update()} methods.</p>
597 *
598 * @param focusable true if the popup should grab focus, false otherwise.
599 *
600 * @see #isFocusable()
Alan Viverette5435a302015-01-29 10:25:34 -0800601 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 * @see #update()
603 */
604 public void setFocusable(boolean focusable) {
605 mFocusable = focusable;
606 }
607
608 /**
609 * Return the current value in {@link #setInputMethodMode(int)}.
Alan Viverette5435a302015-01-29 10:25:34 -0800610 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800611 * @see #setInputMethodMode(int)
612 */
613 public int getInputMethodMode() {
614 return mInputMethodMode;
Alan Viverette5435a302015-01-29 10:25:34 -0800615
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 }
Alan Viverette5435a302015-01-29 10:25:34 -0800617
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 /**
619 * Control how the popup operates with an input method: one of
620 * {@link #INPUT_METHOD_FROM_FOCUSABLE}, {@link #INPUT_METHOD_NEEDED},
621 * or {@link #INPUT_METHOD_NOT_NEEDED}.
Alan Viverette5435a302015-01-29 10:25:34 -0800622 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800623 * <p>If the popup is showing, calling this method will take effect only
624 * the next time the popup is shown or through a manual call to one of
625 * the {@link #update()} methods.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800626 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800627 * @see #getInputMethodMode()
628 * @see #update()
629 */
630 public void setInputMethodMode(int mode) {
631 mInputMethodMode = mode;
632 }
Romain Guy374aaaed32009-07-14 15:11:59 -0700633
634 /**
635 * Sets the operating mode for the soft input area.
636 *
637 * @param mode The desired mode, see
638 * {@link android.view.WindowManager.LayoutParams#softInputMode}
639 * for the full list
640 *
641 * @see android.view.WindowManager.LayoutParams#softInputMode
642 * @see #getSoftInputMode()
643 */
644 public void setSoftInputMode(int mode) {
645 mSoftInputMode = mode;
646 }
647
648 /**
649 * Returns the current value in {@link #setSoftInputMode(int)}.
650 *
651 * @see #setSoftInputMode(int)
652 * @see android.view.WindowManager.LayoutParams#softInputMode
653 */
654 public int getSoftInputMode() {
655 return mSoftInputMode;
656 }
Alan Viverette5435a302015-01-29 10:25:34 -0800657
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 /**
659 * <p>Indicates whether the popup window receives touch events.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800660 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800661 * @return true if the popup is touchable, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800662 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800663 * @see #setTouchable(boolean)
664 */
665 public boolean isTouchable() {
666 return mTouchable;
667 }
668
669 /**
670 * <p>Changes the touchability of the popup window. When touchable, the
671 * window will receive touch events, otherwise touch events will go to the
672 * window below it. By default the window is touchable.</p>
673 *
674 * <p>If the popup is showing, calling this method will take effect only
675 * the next time the popup is shown or through a manual call to one of
676 * the {@link #update()} methods.</p>
677 *
678 * @param touchable true if the popup should receive touch events, false otherwise
679 *
680 * @see #isTouchable()
Alan Viverette5435a302015-01-29 10:25:34 -0800681 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682 * @see #update()
683 */
684 public void setTouchable(boolean touchable) {
685 mTouchable = touchable;
686 }
687
688 /**
689 * <p>Indicates whether the popup window will be informed of touch events
690 * outside of its window.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800691 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692 * @return true if the popup is outside touchable, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800693 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800694 * @see #setOutsideTouchable(boolean)
695 */
696 public boolean isOutsideTouchable() {
697 return mOutsideTouchable;
698 }
699
700 /**
701 * <p>Controls whether the pop-up will be informed of touch events outside
702 * of its window. This only makes sense for pop-ups that are touchable
703 * but not focusable, which means touches outside of the window will
704 * be delivered to the window behind. The default is false.</p>
705 *
706 * <p>If the popup is showing, calling this method will take effect only
707 * the next time the popup is shown or through a manual call to one of
708 * the {@link #update()} methods.</p>
709 *
710 * @param touchable true if the popup should receive outside
711 * touch events, false otherwise
712 *
713 * @see #isOutsideTouchable()
Alan Viverette5435a302015-01-29 10:25:34 -0800714 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800715 * @see #update()
716 */
717 public void setOutsideTouchable(boolean touchable) {
718 mOutsideTouchable = touchable;
719 }
720
721 /**
722 * <p>Indicates whether clipping of the popup window is enabled.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800723 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800724 * @return true if the clipping is enabled, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800725 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726 * @see #setClippingEnabled(boolean)
727 */
728 public boolean isClippingEnabled() {
729 return mClippingEnabled;
730 }
731
732 /**
733 * <p>Allows the popup window to extend beyond the bounds of the screen. By default the
734 * window is clipped to the screen boundaries. Setting this to false will allow windows to be
735 * accurately positioned.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800736 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800737 * <p>If the popup is showing, calling this method will take effect only
738 * the next time the popup is shown or through a manual call to one of
739 * the {@link #update()} methods.</p>
740 *
741 * @param enabled false if the window should be allowed to extend outside of the screen
Alan Viverette5435a302015-01-29 10:25:34 -0800742 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800743 * @see #isClippingEnabled()
744 * @see #update()
745 */
746 public void setClippingEnabled(boolean enabled) {
747 mClippingEnabled = enabled;
748 }
749
750 /**
Adam Powell56c2d332010-11-05 20:03:03 -0700751 * Clip this popup window to the screen, but not to the containing window.
752 *
753 * @param enabled True to clip to the screen.
754 * @hide
755 */
756 public void setClipToScreenEnabled(boolean enabled) {
757 mClipToScreen = enabled;
758 setClippingEnabled(!enabled);
759 }
Adam Powell348e69c2011-02-16 16:49:50 -0800760
761 /**
762 * Allow PopupWindow to scroll the anchor's parent to provide more room
763 * for the popup. Enabled by default.
764 *
765 * @param enabled True to scroll the anchor's parent when more room is desired by the popup.
766 */
767 void setAllowScrollingAnchorParent(boolean enabled) {
768 mAllowScrollingAnchorParent = enabled;
769 }
Alan Viverette5435a302015-01-29 10:25:34 -0800770
Adam Powell56c2d332010-11-05 20:03:03 -0700771 /**
Jeff Brown01ce2e92010-09-26 22:20:12 -0700772 * <p>Indicates whether the popup window supports splitting touches.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800773 *
Jeff Brown01ce2e92010-09-26 22:20:12 -0700774 * @return true if the touch splitting is enabled, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800775 *
Jeff Brown01ce2e92010-09-26 22:20:12 -0700776 * @see #setSplitTouchEnabled(boolean)
Jeff Brown01ce2e92010-09-26 22:20:12 -0700777 */
778 public boolean isSplitTouchEnabled() {
Jeff Brown46e75292010-11-10 16:53:45 -0800779 if (mSplitTouchEnabled < 0 && mContext != null) {
780 return mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB;
781 }
782 return mSplitTouchEnabled == 1;
Jeff Brown01ce2e92010-09-26 22:20:12 -0700783 }
784
785 /**
786 * <p>Allows the popup window to split touches across other windows that also
Jeff Brown46e75292010-11-10 16:53:45 -0800787 * support split touch. When this flag is false, the first pointer
Jeff Brown01ce2e92010-09-26 22:20:12 -0700788 * that goes down determines the window to which all subsequent touches
Jeff Brown46e75292010-11-10 16:53:45 -0800789 * go until all pointers go up. When this flag is true, each pointer
Jeff Brown01ce2e92010-09-26 22:20:12 -0700790 * (not necessarily the first) that goes down determines the window
791 * to which all subsequent touches of that pointer will go until that
792 * pointer goes up thereby enabling touches with multiple pointers
793 * to be split across multiple windows.</p>
794 *
795 * @param enabled true if the split touches should be enabled, false otherwise
796 * @see #isSplitTouchEnabled()
Jeff Brown01ce2e92010-09-26 22:20:12 -0700797 */
798 public void setSplitTouchEnabled(boolean enabled) {
Jeff Brown46e75292010-11-10 16:53:45 -0800799 mSplitTouchEnabled = enabled ? 1 : 0;
Jeff Brown01ce2e92010-09-26 22:20:12 -0700800 }
801
802 /**
Adam Powellba0a2c32010-09-28 17:41:23 -0700803 * <p>Indicates whether the popup window will be forced into using absolute screen coordinates
804 * for positioning.</p>
805 *
806 * @return true if the window will always be positioned in screen coordinates.
807 * @hide
808 */
809 public boolean isLayoutInScreenEnabled() {
810 return mLayoutInScreen;
811 }
812
813 /**
814 * <p>Allows the popup window to force the flag
815 * {@link WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN}, overriding default behavior.
816 * This will cause the popup to be positioned in absolute screen coordinates.</p>
817 *
818 * @param enabled true if the popup should always be positioned in screen coordinates
819 * @hide
820 */
821 public void setLayoutInScreenEnabled(boolean enabled) {
822 mLayoutInScreen = enabled;
823 }
824
825 /**
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700826 * <p>Indicates whether the popup window will be attached in the decor frame of its parent
827 * window.
828 *
829 * @return true if the window will be attached to the decor frame of its parent window.
830 *
831 * @see #setAttachedInDecor(boolean)
832 * @see WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR
833 */
834 public boolean isAttachedInDecor() {
835 return mAttachedInDecor;
836 }
837
838 /**
839 * <p>This will attach the popup window to the decor frame of the parent window to avoid
840 * overlaping with screen decorations like the navigation bar. Overrides the default behavior of
841 * the flag {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR}.
842 *
843 * <p>By default the flag is set on SDK version {@link Build.VERSION_CODES#LOLLIPOP_MR1} or
844 * greater and cleared on lesser SDK versions.
845 *
846 * @param enabled true if the popup should be attached to the decor frame of its parent window.
847 *
848 * @see WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR
849 */
850 public void setAttachedInDecor(boolean enabled) {
851 mAttachedInDecor = enabled;
852 mAttachedInDecorSet = true;
853 }
854
855 /**
Adam Powell0bd1d0a2011-07-22 19:35:06 -0700856 * Allows the popup window to force the flag
857 * {@link WindowManager.LayoutParams#FLAG_LAYOUT_INSET_DECOR}, overriding default behavior.
858 * This will cause the popup to inset its content to account for system windows overlaying
859 * the screen, such as the status bar.
860 *
861 * <p>This will often be combined with {@link #setLayoutInScreenEnabled(boolean)}.
862 *
863 * @param enabled true if the popup's views should inset content to account for system windows,
864 * the way that decor views behave for full-screen windows.
865 * @hide
866 */
867 public void setLayoutInsetDecor(boolean enabled) {
868 mLayoutInsetDecor = enabled;
869 }
870
871 /**
Alan Viverette80ebe0d2015-04-30 15:53:11 -0700872 * Set the layout type for this window.
873 * <p>
874 * See {@link WindowManager.LayoutParams#type} for possible values.
Adam Powell574b37e2010-10-07 11:15:19 -0700875 *
876 * @param layoutType Layout type for this window.
Chris Banes36344a92015-04-14 10:43:16 +0100877 *
878 * @see WindowManager.LayoutParams#type
Adam Powell574b37e2010-10-07 11:15:19 -0700879 */
880 public void setWindowLayoutType(int layoutType) {
881 mWindowLayoutType = layoutType;
882 }
883
884 /**
Chris Banes36344a92015-04-14 10:43:16 +0100885 * Returns the layout type for this window.
886 *
887 * @see #setWindowLayoutType(int)
Adam Powell574b37e2010-10-07 11:15:19 -0700888 */
889 public int getWindowLayoutType() {
890 return mWindowLayoutType;
891 }
892
893 /**
Adam Powelle0b6cd12011-09-28 22:06:11 -0700894 * Set whether this window is touch modal or if outside touches will be sent to
895 * other windows behind it.
896 * @hide
897 */
898 public void setTouchModal(boolean touchModal) {
899 mNotTouchModal = !touchModal;
900 }
901
902 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 * <p>Change the width and height measure specs that are given to the
904 * window manager by the popup. By default these are 0, meaning that
905 * the current width or height is requested as an explicit size from
906 * the window manager. You can supply
Alan Viverette5435a302015-01-29 10:25:34 -0800907 * {@link ViewGroup.LayoutParams#WRAP_CONTENT} or
Romain Guy980a9382010-01-08 15:06:28 -0800908 * {@link ViewGroup.LayoutParams#MATCH_PARENT} to have that measure
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800909 * spec supplied instead, replacing the absolute width and height that
910 * has been set in the popup.</p>
911 *
912 * <p>If the popup is showing, calling this method will take effect only
913 * the next time the popup is shown.</p>
914 *
915 * @param widthSpec an explicit width measure spec mode, either
916 * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
Romain Guy980a9382010-01-08 15:06:28 -0800917 * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800918 * width.
919 * @param heightSpec an explicit height measure spec mode, either
920 * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
Romain Guy980a9382010-01-08 15:06:28 -0800921 * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922 * height.
Alan Viverette259c2842015-03-22 17:39:39 -0700923 *
924 * @deprecated Use {@link #setWidth(int)} and {@link #setHeight(int)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800925 */
Alan Viverette259c2842015-03-22 17:39:39 -0700926 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800927 public void setWindowLayoutMode(int widthSpec, int heightSpec) {
928 mWidthMode = widthSpec;
929 mHeightMode = heightSpec;
930 }
Alan Viverette5435a302015-01-29 10:25:34 -0800931
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800932 /**
Alan Viverette259c2842015-03-22 17:39:39 -0700933 * Returns the popup's height MeasureSpec.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800934 *
935 * @return the height MeasureSpec of the popup
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800936 * @see #setHeight(int)
937 */
938 public int getHeight() {
939 return mHeight;
940 }
941
942 /**
Alan Viverette259c2842015-03-22 17:39:39 -0700943 * Sets the popup's height MeasureSpec.
944 * <p>
945 * If the popup is showing, calling this method will take effect the next
946 * time the popup is shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 *
948 * @param height the height MeasureSpec of the popup
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800949 * @see #getHeight()
Alan Viverette5435a302015-01-29 10:25:34 -0800950 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800951 */
952 public void setHeight(int height) {
953 mHeight = height;
954 }
955
956 /**
Alan Viverette259c2842015-03-22 17:39:39 -0700957 * Returns the popup's width MeasureSpec.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800958 *
959 * @return the width MeasureSpec of the popup
Alan Viverette5435a302015-01-29 10:25:34 -0800960 * @see #setWidth(int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800961 */
962 public int getWidth() {
963 return mWidth;
964 }
965
966 /**
Alan Viverette259c2842015-03-22 17:39:39 -0700967 * Sets the popup's width MeasureSpec.
968 * <p>
969 * If the popup is showing, calling this method will take effect the next
970 * time the popup is shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800971 *
972 * @param width the width MeasureSpec of the popup
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800973 * @see #getWidth()
974 * @see #isShowing()
975 */
976 public void setWidth(int width) {
977 mWidth = width;
978 }
979
980 /**
Alan Viverette75d83792015-01-07 15:51:54 -0800981 * Sets whether the popup window should overlap its anchor view when
982 * displayed as a drop-down.
983 * <p>
984 * If the popup is showing, calling this method will take effect only
985 * the next time the popup is shown.
986 *
987 * @param overlapAnchor Whether the popup should overlap its anchor.
988 *
989 * @see #getOverlapAnchor()
990 * @see #isShowing()
991 */
992 public void setOverlapAnchor(boolean overlapAnchor) {
993 mOverlapAnchor = overlapAnchor;
994 }
995
996 /**
997 * Returns whether the popup window should overlap its anchor view when
998 * displayed as a drop-down.
999 *
1000 * @return Whether the popup should overlap its anchor.
1001 *
1002 * @see #setOverlapAnchor(boolean)
1003 */
1004 public boolean getOverlapAnchor() {
1005 return mOverlapAnchor;
1006 }
1007
1008 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001009 * <p>Indicate whether this popup window is showing on screen.</p>
1010 *
1011 * @return true if the popup is showing, false otherwise
1012 */
1013 public boolean isShowing() {
1014 return mIsShowing;
1015 }
1016
1017 /**
1018 * <p>
1019 * Display the content view in a popup window at the specified location. If the popup window
1020 * cannot fit on screen, it will be clipped. See {@link android.view.WindowManager.LayoutParams}
1021 * for more information on how gravity and the x and y parameters are related. Specifying
1022 * a gravity of {@link android.view.Gravity#NO_GRAVITY} is similar to specifying
1023 * <code>Gravity.LEFT | Gravity.TOP</code>.
1024 * </p>
Alan Viverette5435a302015-01-29 10:25:34 -08001025 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001026 * @param parent a parent view to get the {@link android.view.View#getWindowToken()} token from
1027 * @param gravity the gravity which controls the placement of the popup window
1028 * @param x the popup's x location offset
1029 * @param y the popup's y location offset
1030 */
1031 public void showAtLocation(View parent, int gravity, int x, int y) {
Adam Powell8ee6d7c2011-09-18 14:59:28 -07001032 showAtLocation(parent.getWindowToken(), gravity, x, y);
1033 }
1034
1035 /**
1036 * Display the content view in a popup window at the specified location.
1037 *
1038 * @param token Window token to use for creating the new window
1039 * @param gravity the gravity which controls the placement of the popup window
1040 * @param x the popup's x location offset
1041 * @param y the popup's y location offset
1042 *
1043 * @hide Internal use only. Applications should use
1044 * {@link #showAtLocation(View, int, int, int)} instead.
1045 */
1046 public void showAtLocation(IBinder token, int gravity, int x, int y) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001047 if (isShowing() || mContentView == null) {
1048 return;
1049 }
1050
Alan Viverettee025ed22015-02-02 11:27:21 -08001051 TransitionManager.endTransitions(mDecorView);
1052
Alan Viverette634a8082016-02-03 14:22:41 -05001053 unregisterForViewTreeChanges();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001054
1055 mIsShowing = true;
1056 mIsDropdown = false;
1057
Alan Viverettee025ed22015-02-02 11:27:21 -08001058 final WindowManager.LayoutParams p = createPopupLayoutParams(token);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001059 preparePopup(p);
Alan Viverettee025ed22015-02-02 11:27:21 -08001060
1061 // Only override the default if some gravity was specified.
1062 if (gravity != Gravity.NO_GRAVITY) {
1063 p.gravity = gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001064 }
Alan Viverettee025ed22015-02-02 11:27:21 -08001065
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001066 p.x = x;
1067 p.y = y;
Alan Viverettee025ed22015-02-02 11:27:21 -08001068
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001069 invokePopup(p);
1070 }
1071
1072 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001073 * Display the content view in a popup window anchored to the bottom-left
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001074 * corner of the anchor view. If there is not enough room on screen to show
1075 * the popup in its entirety, this method tries to find a parent scroll
Alan Viverette75d83792015-01-07 15:51:54 -08001076 * view to scroll. If no parent scroll view can be scrolled, the
1077 * bottom-left corner of the popup is pinned at the top left corner of the
1078 * anchor view.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001079 *
1080 * @param anchor the view on which to pin the popup window
1081 *
1082 * @see #dismiss()
1083 */
1084 public void showAsDropDown(View anchor) {
1085 showAsDropDown(anchor, 0, 0);
1086 }
1087
1088 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001089 * Display the content view in a popup window anchored to the bottom-left
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001090 * corner of the anchor view offset by the specified x and y coordinates.
Alan Viverette75d83792015-01-07 15:51:54 -08001091 * If there is not enough room on screen to show the popup in its entirety,
1092 * this method tries to find a parent scroll view to scroll. If no parent
1093 * scroll view can be scrolled, the bottom-left corner of the popup is
1094 * pinned at the top left corner of the anchor view.
1095 * <p>
1096 * If the view later scrolls to move <code>anchor</code> to a different
1097 * location, the popup will be moved correspondingly.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001098 *
1099 * @param anchor the view on which to pin the popup window
Adam Powell54c94de2013-09-26 15:36:34 -07001100 * @param xoff A horizontal offset from the anchor in pixels
1101 * @param yoff A vertical offset from the anchor in pixels
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001102 *
1103 * @see #dismiss()
1104 */
1105 public void showAsDropDown(View anchor, int xoff, int yoff) {
Adam Powell54c94de2013-09-26 15:36:34 -07001106 showAsDropDown(anchor, xoff, yoff, DEFAULT_ANCHORED_GRAVITY);
1107 }
1108
1109 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001110 * Displays the content view in a popup window anchored to the corner of
1111 * another view. The window is positioned according to the specified
1112 * gravity and offset by the specified x and y coordinates.
1113 * <p>
1114 * If there is not enough room on screen to show the popup in its entirety,
1115 * this method tries to find a parent scroll view to scroll. If no parent
1116 * view can be scrolled, the specified vertical gravity will be ignored and
1117 * the popup will anchor itself such that it is visible.
1118 * <p>
1119 * If the view later scrolls to move <code>anchor</code> to a different
1120 * location, the popup will be moved correspondingly.
Adam Powell54c94de2013-09-26 15:36:34 -07001121 *
1122 * @param anchor the view on which to pin the popup window
1123 * @param xoff A horizontal offset from the anchor in pixels
1124 * @param yoff A vertical offset from the anchor in pixels
1125 * @param gravity Alignment of the popup relative to the anchor
1126 *
1127 * @see #dismiss()
1128 */
1129 public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001130 if (isShowing() || mContentView == null) {
1131 return;
1132 }
1133
Alan Viverettee025ed22015-02-02 11:27:21 -08001134 TransitionManager.endTransitions(mDecorView);
1135
Alan Viverette634a8082016-02-03 14:22:41 -05001136 registerForViewTreeChanges(anchor, xoff, yoff, gravity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001137
1138 mIsShowing = true;
1139 mIsDropdown = true;
1140
Alan Viverettee025ed22015-02-02 11:27:21 -08001141 final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001142 preparePopup(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001143
Alan Viverettee025ed22015-02-02 11:27:21 -08001144 final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff, gravity);
1145 updateAboveAnchor(aboveAnchor);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001146
1147 invokePopup(p);
1148 }
1149
Romain Guy3e141682010-03-08 17:44:40 -08001150 private void updateAboveAnchor(boolean aboveAnchor) {
1151 if (aboveAnchor != mAboveAnchor) {
1152 mAboveAnchor = aboveAnchor;
1153
Alan Viverette697804e2015-08-06 12:36:47 -04001154 if (mBackground != null && mBackgroundView != null) {
1155 // If the background drawable provided was a StateListDrawable
1156 // with above-anchor and below-anchor states, use those.
1157 // Otherwise, rely on refreshDrawableState to do the job.
Romain Guy3e141682010-03-08 17:44:40 -08001158 if (mAboveAnchorBackgroundDrawable != null) {
1159 if (mAboveAnchor) {
Alan Viverette697804e2015-08-06 12:36:47 -04001160 mBackgroundView.setBackground(mAboveAnchorBackgroundDrawable);
Romain Guy3e141682010-03-08 17:44:40 -08001161 } else {
Alan Viverette697804e2015-08-06 12:36:47 -04001162 mBackgroundView.setBackground(mBelowAnchorBackgroundDrawable);
Romain Guy3e141682010-03-08 17:44:40 -08001163 }
1164 } else {
Alan Viverette697804e2015-08-06 12:36:47 -04001165 mBackgroundView.refreshDrawableState();
Romain Guy3e141682010-03-08 17:44:40 -08001166 }
1167 }
1168 }
1169 }
1170
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001171 /**
1172 * Indicates whether the popup is showing above (the y coordinate of the popup's bottom
1173 * is less than the y coordinate of the anchor) or below the anchor view (the y coordinate
1174 * of the popup is greater than y coordinate of the anchor's bottom).
1175 *
1176 * The value returned
1177 * by this method is meaningful only after {@link #showAsDropDown(android.view.View)}
1178 * or {@link #showAsDropDown(android.view.View, int, int)} was invoked.
1179 *
1180 * @return True if this popup is showing above the anchor view, false otherwise.
1181 */
1182 public boolean isAboveAnchor() {
1183 return mAboveAnchor;
1184 }
1185
1186 /**
Alan Viverettee025ed22015-02-02 11:27:21 -08001187 * Prepare the popup by embedding it into a new ViewGroup if the background
1188 * drawable is not null. If embedding is required, the layout parameters'
1189 * height is modified to take into account the background's padding.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001190 *
1191 * @param p the layout parameters of the popup's content view
1192 */
1193 private void preparePopup(WindowManager.LayoutParams p) {
Romain Guy448ecf52009-05-14 16:03:42 -07001194 if (mContentView == null || mContext == null || mWindowManager == null) {
1195 throw new IllegalStateException("You must specify a valid content view by "
1196 + "calling setContentView() before attempting to show the popup.");
1197 }
1198
Alan Viverette8fd949e2015-03-11 12:21:30 -07001199 // The old decor view may be transitioning out. Make sure it finishes
1200 // and cleans up before we try to create another one.
1201 if (mDecorView != null) {
1202 mDecorView.cancelTransitions();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001203 }
Alan Viveretteccb11e12014-07-08 16:04:02 -07001204
Alan Viverette8fd949e2015-03-11 12:21:30 -07001205 // When a background is available, we embed the content view within
1206 // another view that owns the background drawable.
Alan Viverette8fd949e2015-03-11 12:21:30 -07001207 if (mBackground != null) {
Alan Viverette697804e2015-08-06 12:36:47 -04001208 mBackgroundView = createBackgroundView(mContentView);
1209 mBackgroundView.setBackground(mBackground);
Alan Viverette8fd949e2015-03-11 12:21:30 -07001210 } else {
Alan Viverette697804e2015-08-06 12:36:47 -04001211 mBackgroundView = mContentView;
Alan Viverette8fd949e2015-03-11 12:21:30 -07001212 }
1213
Alan Viverette697804e2015-08-06 12:36:47 -04001214 mDecorView = createDecorView(mBackgroundView);
Alan Viverette5435a302015-01-29 10:25:34 -08001215
1216 // The background owner should be elevated so that it casts a shadow.
Alan Viverette697804e2015-08-06 12:36:47 -04001217 mBackgroundView.setElevation(mElevation);
Alan Viverette5435a302015-01-29 10:25:34 -08001218
1219 // We may wrap that in another view, so we'll need to manually specify
1220 // the surface insets.
Alan Viverette697804e2015-08-06 12:36:47 -04001221 final int surfaceInset = (int) Math.ceil(mBackgroundView.getZ() * 2);
Alan Viverette5435a302015-01-29 10:25:34 -08001222 p.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
1223 p.hasManualSurfaceInsets = true;
1224
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001225 mPopupViewInitialLayoutDirectionInherited =
Alan Viverette5435a302015-01-29 10:25:34 -08001226 (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001227 mPopupWidth = p.width;
1228 mPopupHeight = p.height;
1229 }
1230
1231 /**
Alan Viverette5435a302015-01-29 10:25:34 -08001232 * Wraps a content view in a PopupViewContainer.
1233 *
1234 * @param contentView the content view to wrap
1235 * @return a PopupViewContainer that wraps the content view
1236 */
1237 private PopupBackgroundView createBackgroundView(View contentView) {
1238 final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
1239 final int height;
1240 if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
1241 height = ViewGroup.LayoutParams.WRAP_CONTENT;
1242 } else {
1243 height = ViewGroup.LayoutParams.MATCH_PARENT;
1244 }
1245
1246 final PopupBackgroundView backgroundView = new PopupBackgroundView(mContext);
1247 final PopupBackgroundView.LayoutParams listParams = new PopupBackgroundView.LayoutParams(
1248 ViewGroup.LayoutParams.MATCH_PARENT, height);
1249 backgroundView.addView(contentView, listParams);
1250
1251 return backgroundView;
1252 }
1253
1254 /**
1255 * Wraps a content view in a FrameLayout.
1256 *
1257 * @param contentView the content view to wrap
1258 * @return a FrameLayout that wraps the content view
1259 */
1260 private PopupDecorView createDecorView(View contentView) {
1261 final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
1262 final int height;
1263 if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
1264 height = ViewGroup.LayoutParams.WRAP_CONTENT;
1265 } else {
1266 height = ViewGroup.LayoutParams.MATCH_PARENT;
1267 }
1268
1269 final PopupDecorView decorView = new PopupDecorView(mContext);
1270 decorView.addView(contentView, ViewGroup.LayoutParams.MATCH_PARENT, height);
1271 decorView.setClipChildren(false);
1272 decorView.setClipToPadding(false);
1273
1274 return decorView;
1275 }
1276
1277 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001278 * <p>Invoke the popup window by adding the content view to the window
1279 * manager.</p>
1280 *
1281 * <p>The content view must be non-null when this method is invoked.</p>
1282 *
1283 * @param p the layout parameters of the popup's content view
1284 */
1285 private void invokePopup(WindowManager.LayoutParams p) {
Romain Guy0c0b7682011-05-16 11:54:09 -07001286 if (mContext != null) {
1287 p.packageName = mContext.getPackageName();
1288 }
Alan Viverette5435a302015-01-29 10:25:34 -08001289
Alan Viverette8fd949e2015-03-11 12:21:30 -07001290 final PopupDecorView decorView = mDecorView;
1291 decorView.setFitsSystemWindows(mLayoutInsetDecor);
Alan Viverette8fd949e2015-03-11 12:21:30 -07001292
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001293 setLayoutDirectionFromAnchor();
Alan Viverette5435a302015-01-29 10:25:34 -08001294
Alan Viverette8fd949e2015-03-11 12:21:30 -07001295 mWindowManager.addView(decorView, p);
Alan Viverette95888c02015-04-16 13:27:50 -07001296
1297 if (mEnterTransition != null) {
1298 decorView.requestEnterTransition(mEnterTransition);
1299 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001300 }
1301
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001302 private void setLayoutDirectionFromAnchor() {
1303 if (mAnchor != null) {
1304 View anchor = mAnchor.get();
1305 if (anchor != null && mPopupViewInitialLayoutDirectionInherited) {
Alan Viverette5435a302015-01-29 10:25:34 -08001306 mDecorView.setLayoutDirection(anchor.getLayoutDirection());
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001307 }
1308 }
1309 }
1310
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001311 /**
1312 * <p>Generate the layout parameters for the popup window.</p>
1313 *
1314 * @param token the window token used to bind the popup's window
1315 *
1316 * @return the layout parameters to pass to the window manager
1317 */
Alan Viverettee025ed22015-02-02 11:27:21 -08001318 private WindowManager.LayoutParams createPopupLayoutParams(IBinder token) {
1319 final WindowManager.LayoutParams p = new WindowManager.LayoutParams();
1320
1321 // These gravity settings put the view at the top left corner of the
1322 // screen. The view is then positioned to the appropriate location by
1323 // setting the x and y offsets to match the anchor's bottom-left
1324 // corner.
Fabrice Di Meglioaac0d4e2012-07-19 19:21:26 -07001325 p.gravity = Gravity.START | Gravity.TOP;
Alan Viverettee025ed22015-02-02 11:27:21 -08001326 p.flags = computeFlags(p.flags);
1327 p.type = mWindowLayoutType;
1328 p.token = token;
1329 p.softInputMode = mSoftInputMode;
1330 p.windowAnimations = computeAnimationResource();
1331
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001332 if (mBackground != null) {
1333 p.format = mBackground.getOpacity();
1334 } else {
1335 p.format = PixelFormat.TRANSLUCENT;
1336 }
Alan Viverettee025ed22015-02-02 11:27:21 -08001337
1338 if (mHeightMode < 0) {
1339 p.height = mLastHeight = mHeightMode;
1340 } else {
1341 p.height = mLastHeight = mHeight;
1342 }
1343
1344 if (mWidthMode < 0) {
1345 p.width = mLastWidth = mWidthMode;
1346 } else {
1347 p.width = mLastWidth = mWidth;
1348 }
1349
Wale Ogunwale8216eb22015-12-18 10:42:42 -08001350 p.privateFlags = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH
1351 | PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
Robert Carra1eb4392015-12-10 12:43:51 -08001352
Alan Viverettee025ed22015-02-02 11:27:21 -08001353 // Used for debugging.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001354 p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
1355
1356 return p;
1357 }
1358
1359 private int computeFlags(int curFlags) {
1360 curFlags &= ~(
1361 WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES |
1362 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
1363 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
1364 WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
1365 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
Adam Powellba0a2c32010-09-28 17:41:23 -07001366 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
1367 WindowManager.LayoutParams.FLAG_SPLIT_TOUCH);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001368 if(mIgnoreCheekPress) {
1369 curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
1370 }
1371 if (!mFocusable) {
1372 curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1373 if (mInputMethodMode == INPUT_METHOD_NEEDED) {
1374 curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1375 }
1376 } else if (mInputMethodMode == INPUT_METHOD_NOT_NEEDED) {
1377 curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1378 }
1379 if (!mTouchable) {
1380 curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
1381 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001382 if (mOutsideTouchable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001383 curFlags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
1384 }
1385 if (!mClippingEnabled) {
1386 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
1387 }
Jeff Brown46e75292010-11-10 16:53:45 -08001388 if (isSplitTouchEnabled()) {
Jeff Brown01ce2e92010-09-26 22:20:12 -07001389 curFlags |= WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
1390 }
Adam Powellba0a2c32010-09-28 17:41:23 -07001391 if (mLayoutInScreen) {
1392 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
1393 }
Adam Powell0bd1d0a2011-07-22 19:35:06 -07001394 if (mLayoutInsetDecor) {
1395 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
1396 }
Adam Powelle0b6cd12011-09-28 22:06:11 -07001397 if (mNotTouchModal) {
1398 curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
1399 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -07001400 if (mAttachedInDecor) {
1401 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR;
1402 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001403 return curFlags;
1404 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -07001405
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001406 private int computeAnimationResource() {
Alan Viverette5435a302015-01-29 10:25:34 -08001407 if (mAnimationStyle == ANIMATION_STYLE_DEFAULT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001408 if (mIsDropdown) {
1409 return mAboveAnchor
1410 ? com.android.internal.R.style.Animation_DropDownUp
1411 : com.android.internal.R.style.Animation_DropDownDown;
1412 }
1413 return 0;
1414 }
1415 return mAnimationStyle;
1416 }
Alan Viverette560f1702014-05-05 14:40:07 -07001417
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001418 /**
Alan Viverette560f1702014-05-05 14:40:07 -07001419 * Positions the popup window on screen. When the popup window is too tall
1420 * to fit under the anchor, a parent scroll view is seeked and scrolled up
1421 * to reclaim space. If scrolling is not possible or not enough, the popup
1422 * window gets moved on top of the anchor.
1423 * <p>
1424 * The height must have been set on the layout parameters prior to calling
1425 * this method.
Alan Viverette5435a302015-01-29 10:25:34 -08001426 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001427 * @param anchor the view on which the popup window must be anchored
1428 * @param p the layout parameters used to display the drop down
Alan Viverette560f1702014-05-05 14:40:07 -07001429 * @param xoff horizontal offset used to adjust for background padding
1430 * @param yoff vertical offset used to adjust for background padding
1431 * @param gravity horizontal gravity specifying popup alignment
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001432 * @return true if the popup is translated upwards to fit on screen
1433 */
Alan Viverette560f1702014-05-05 14:40:07 -07001434 private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p, int xoff,
1435 int yoff, int gravity) {
Adam Powell62e2bde2011-08-15 15:50:05 -07001436 final int anchorHeight = anchor.getHeight();
Alan Viverette560f1702014-05-05 14:40:07 -07001437 final int anchorWidth = anchor.getWidth();
1438 if (mOverlapAnchor) {
1439 yoff -= anchorHeight;
1440 }
1441
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001442 anchor.getLocationInWindow(mDrawingLocation);
1443 p.x = mDrawingLocation[0] + xoff;
Adam Powell62e2bde2011-08-15 15:50:05 -07001444 p.y = mDrawingLocation[1] + anchorHeight + yoff;
Adam Powell54c94de2013-09-26 15:36:34 -07001445
Alan Viverette560f1702014-05-05 14:40:07 -07001446 final int hgrav = Gravity.getAbsoluteGravity(gravity, anchor.getLayoutDirection())
1447 & Gravity.HORIZONTAL_GRAVITY_MASK;
Adam Powell54c94de2013-09-26 15:36:34 -07001448 if (hgrav == Gravity.RIGHT) {
Alan Viverette560f1702014-05-05 14:40:07 -07001449 // Flip the location to align the right sides of the popup and
1450 // anchor instead of left.
1451 p.x -= mPopupWidth - anchorWidth;
Adam Powell54c94de2013-09-26 15:36:34 -07001452 }
Alan Viverette560f1702014-05-05 14:40:07 -07001453
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001454 boolean onTop = false;
1455
Adam Powell54c94de2013-09-26 15:36:34 -07001456 p.gravity = Gravity.LEFT | Gravity.TOP;
1457
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001458 anchor.getLocationOnScreen(mScreenLocation);
1459 final Rect displayFrame = new Rect();
1460 anchor.getWindowVisibleDisplayFrame(displayFrame);
Adam Powell62e2bde2011-08-15 15:50:05 -07001461
Alan Viverette560f1702014-05-05 14:40:07 -07001462 final int screenY = mScreenLocation[1] + anchorHeight + yoff;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001463 final View root = anchor.getRootView();
Alan Viverette560f1702014-05-05 14:40:07 -07001464 if (screenY + mPopupHeight > displayFrame.bottom
1465 || p.x + mPopupWidth - root.getWidth() > 0) {
1466 // If the drop down disappears at the bottom of the screen, we try
1467 // to scroll a parent scrollview or move the drop down back up on
1468 // top of the edit box.
Adam Powellb7c1b202011-02-17 12:03:09 -08001469 if (mAllowScrollingAnchorParent) {
Alan Viverette560f1702014-05-05 14:40:07 -07001470 final int scrollX = anchor.getScrollX();
1471 final int scrollY = anchor.getScrollY();
1472 final Rect r = new Rect(scrollX, scrollY, scrollX + mPopupWidth + xoff,
1473 scrollY + mPopupHeight + anchorHeight + yoff);
Adam Powellb7c1b202011-02-17 12:03:09 -08001474 anchor.requestRectangleOnScreen(r, true);
1475 }
Romain Guy3e141682010-03-08 17:44:40 -08001476
Alan Viverette560f1702014-05-05 14:40:07 -07001477 // Now we re-evaluate the space available, and decide from that
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001478 // whether the pop-up will go above or below the anchor.
1479 anchor.getLocationInWindow(mDrawingLocation);
1480 p.x = mDrawingLocation[0] + xoff;
Alan Viverette560f1702014-05-05 14:40:07 -07001481 p.y = mDrawingLocation[1] + anchorHeight + yoff;
Adam Powell54c94de2013-09-26 15:36:34 -07001482
Alan Viverette560f1702014-05-05 14:40:07 -07001483 // Preserve the gravity adjustment.
Adam Powell54c94de2013-09-26 15:36:34 -07001484 if (hgrav == Gravity.RIGHT) {
Alan Viverette560f1702014-05-05 14:40:07 -07001485 p.x -= mPopupWidth - anchorWidth;
Adam Powell54c94de2013-09-26 15:36:34 -07001486 }
Alan Viverette560f1702014-05-05 14:40:07 -07001487
1488 // Determine whether there is more space above or below the anchor.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001489 anchor.getLocationOnScreen(mScreenLocation);
Alan Viverette560f1702014-05-05 14:40:07 -07001490 onTop = (displayFrame.bottom - mScreenLocation[1] - anchorHeight - yoff) <
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001491 (mScreenLocation[1] - yoff - displayFrame.top);
Oren Blasberged391262015-09-01 12:12:51 -07001492 if (!mOverlapAnchor) {
1493 if (onTop) {
1494 p.gravity = Gravity.LEFT | Gravity.BOTTOM;
1495 p.y = root.getHeight() - mDrawingLocation[1] + yoff;
1496 } else {
1497 p.y = mDrawingLocation[1] + anchorHeight + yoff;
1498 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001499 }
1500 }
1501
Adam Powell56c2d332010-11-05 20:03:03 -07001502 if (mClipToScreen) {
Chong Zhang7c9732d2015-12-29 14:50:05 -08001503 final int winOffsetX = mScreenLocation[0] - mDrawingLocation[0];
1504 final int winOffsetY = mScreenLocation[1] - mDrawingLocation[1];
1505 p.x += winOffsetX;
1506 p.y += winOffsetY;
Adam Powell56c2d332010-11-05 20:03:03 -07001507 final int displayFrameWidth = displayFrame.right - displayFrame.left;
Alan Viverette560f1702014-05-05 14:40:07 -07001508 final int right = p.x + p.width;
Chong Zhang7c9732d2015-12-29 14:50:05 -08001509 if (right > displayFrame.right) {
1510 p.x -= right - displayFrame.right;
Adam Powell56c2d332010-11-05 20:03:03 -07001511 }
Alan Viverette560f1702014-05-05 14:40:07 -07001512
Adam Powell56c2d332010-11-05 20:03:03 -07001513 if (p.x < displayFrame.left) {
1514 p.x = displayFrame.left;
1515 p.width = Math.min(p.width, displayFrameWidth);
1516 }
1517
Oren Blasberged391262015-09-01 12:12:51 -07001518 if (mOverlapAnchor) {
Oren Blasberged391262015-09-01 12:12:51 -07001519 final int bottom = p.y + p.height;
1520 if (bottom > displayFrame.bottom) {
Chong Zhang7c9732d2015-12-29 14:50:05 -08001521 p.y -= bottom - displayFrame.bottom;
Adam Powell5f83a602011-01-19 17:58:04 -08001522 }
1523 } else {
Oren Blasberged391262015-09-01 12:12:51 -07001524 if (onTop) {
1525 final int popupTop = mScreenLocation[1] + yoff - mPopupHeight;
1526 if (popupTop < 0) {
1527 p.y += popupTop;
1528 }
1529 } else {
1530 p.y = Math.max(p.y, displayFrame.top);
1531 }
Adam Powell5f83a602011-01-19 17:58:04 -08001532 }
Chong Zhang7c9732d2015-12-29 14:50:05 -08001533 p.x -= winOffsetX;
1534 p.y -= winOffsetY;
Adam Powell56c2d332010-11-05 20:03:03 -07001535 }
1536
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001537 p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
Alan Viverette560f1702014-05-05 14:40:07 -07001538
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001539 return onTop;
1540 }
Alan Viverette5435a302015-01-29 10:25:34 -08001541
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001542 /**
1543 * Returns the maximum height that is available for the popup to be
1544 * completely shown. It is recommended that this height be the maximum for
1545 * the popup's height, otherwise it is possible that the popup will be
1546 * clipped.
Alan Viverette5435a302015-01-29 10:25:34 -08001547 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001548 * @param anchor The view on which the popup window must be anchored.
1549 * @return The maximum available height for the popup to be completely
1550 * shown.
1551 */
Alan Viveretteb854d072015-09-28 16:12:18 -04001552 public int getMaxAvailableHeight(@NonNull View anchor) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001553 return getMaxAvailableHeight(anchor, 0);
1554 }
1555
1556 /**
1557 * Returns the maximum height that is available for the popup to be
1558 * completely shown. It is recommended that this height be the maximum for
1559 * the popup's height, otherwise it is possible that the popup will be
1560 * clipped.
1561 *
1562 * @param anchor The view on which the popup window must be anchored.
1563 * @param yOffset y offset from the view's bottom edge
1564 * @return The maximum available height for the popup to be completely
1565 * shown.
1566 */
Alan Viveretteb854d072015-09-28 16:12:18 -04001567 public int getMaxAvailableHeight(@NonNull View anchor, int yOffset) {
Mike LeBeau98acd542009-05-07 19:04:39 -07001568 return getMaxAvailableHeight(anchor, yOffset, false);
1569 }
Alan Viverette5435a302015-01-29 10:25:34 -08001570
Mike LeBeau98acd542009-05-07 19:04:39 -07001571 /**
1572 * Returns the maximum height that is available for the popup to be
1573 * completely shown, optionally ignoring any bottom decorations such as
1574 * the input method. It is recommended that this height be the maximum for
1575 * the popup's height, otherwise it is possible that the popup will be
1576 * clipped.
Alan Viverette5435a302015-01-29 10:25:34 -08001577 *
Mike LeBeau98acd542009-05-07 19:04:39 -07001578 * @param anchor The view on which the popup window must be anchored.
1579 * @param yOffset y offset from the view's bottom edge
1580 * @param ignoreBottomDecorations if true, the height returned will be
1581 * all the way to the bottom of the display, ignoring any
1582 * bottom decorations
1583 * @return The maximum available height for the popup to be completely
1584 * shown.
Mike LeBeau98acd542009-05-07 19:04:39 -07001585 */
Alan Viveretteb854d072015-09-28 16:12:18 -04001586 public int getMaxAvailableHeight(
1587 @NonNull View anchor, int yOffset, boolean ignoreBottomDecorations) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001588 final Rect displayFrame = new Rect();
Jorim Jaggi81758462016-02-29 14:41:09 +01001589 if (ignoreBottomDecorations) {
1590 anchor.getWindowDisplayFrame(displayFrame);
1591 } else {
1592 anchor.getWindowVisibleDisplayFrame(displayFrame);
1593 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001594
1595 final int[] anchorPos = mDrawingLocation;
1596 anchor.getLocationOnScreen(anchorPos);
Alan Viverette5435a302015-01-29 10:25:34 -08001597
Jorim Jaggi81758462016-02-29 14:41:09 +01001598 final int bottomEdge = displayFrame.bottom;
Oren Blasberged391262015-09-01 12:12:51 -07001599
1600 final int distanceToBottom;
1601 if (mOverlapAnchor) {
1602 distanceToBottom = bottomEdge - anchorPos[1] - yOffset;
1603 } else {
1604 distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
1605 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001606 final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
1607
1608 // anchorPos[1] is distance from anchor to top of screen
1609 int returnedHeight = Math.max(distanceToBottom, distanceToTop);
1610 if (mBackground != null) {
1611 mBackground.getPadding(mTempRect);
Alan Viverette5435a302015-01-29 10:25:34 -08001612 returnedHeight -= mTempRect.top + mTempRect.bottom;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001613 }
Alan Viverette5435a302015-01-29 10:25:34 -08001614
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001615 return returnedHeight;
1616 }
Alan Viverette5435a302015-01-29 10:25:34 -08001617
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001618 /**
Alan Viverette7878edf2015-02-03 15:49:18 -08001619 * Disposes of the popup window. This method can be invoked only after
1620 * {@link #showAsDropDown(android.view.View)} has been executed. Failing
1621 * that, calling this method will have no effect.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001622 *
Alan Viverette5435a302015-01-29 10:25:34 -08001623 * @see #showAsDropDown(android.view.View)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001624 */
1625 public void dismiss() {
Alan Viverette8fd949e2015-03-11 12:21:30 -07001626 if (!isShowing() || mIsTransitioningToDismiss) {
Alan Viverettee025ed22015-02-02 11:27:21 -08001627 return;
1628 }
Svetoslav Ganov06f938e2011-11-10 14:31:37 -08001629
Alan Viverette8fd949e2015-03-11 12:21:30 -07001630 final PopupDecorView decorView = mDecorView;
1631 final View contentView = mContentView;
1632
1633 final ViewGroup contentHolder;
1634 final ViewParent contentParent = contentView.getParent();
1635 if (contentParent instanceof ViewGroup) {
1636 contentHolder = ((ViewGroup) contentParent);
1637 } else {
1638 contentHolder = null;
1639 }
1640
1641 // Ensure any ongoing or pending transitions are canceled.
1642 decorView.cancelTransitions();
1643
Alan Viverettee025ed22015-02-02 11:27:21 -08001644 mIsShowing = false;
Alan Viverette8fd949e2015-03-11 12:21:30 -07001645 mIsTransitioningToDismiss = true;
Craig Mautnerb82d0742012-05-23 08:48:39 -07001646
Alan Viverette634a8082016-02-03 14:22:41 -05001647 // This method may be called as part of window detachment, in which
1648 // case the anchor view (and its root) will still return true from
1649 // isAttachedToWindow() during execution of this method; however, we
1650 // can expect the OnAttachStateChangeListener to have been called prior
1651 // to executing this method, so we can rely on that instead.
Alan Viverette95888c02015-04-16 13:27:50 -07001652 final Transition exitTransition = mExitTransition;
Alan Viverette21d36182016-02-24 15:34:11 -05001653 if (mIsAnchorRootAttached && exitTransition != null && decorView.isLaidOut()) {
Alan Viverette95888c02015-04-16 13:27:50 -07001654 // The decor view is non-interactive during exit transitions.
1655 final LayoutParams p = (LayoutParams) decorView.getLayoutParams();
1656 p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
1657 p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
1658 mWindowManager.updateViewLayout(decorView, p);
1659
Alan Viverette634a8082016-02-03 14:22:41 -05001660 // Once we start dismissing the decor view, all state (including
1661 // the anchor root) needs to be moved to the decor view since we
1662 // may open another popup while it's busy exiting.
1663 final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
Alan Viverette91098572016-01-19 14:07:31 -05001664 final Rect epicenter = getTransitionEpicenter();
Alan Viverette95888c02015-04-16 13:27:50 -07001665 exitTransition.setEpicenterCallback(new EpicenterCallback() {
1666 @Override
1667 public Rect onGetEpicenter(Transition transition) {
1668 return epicenter;
1669 }
1670 });
Alan Viverette634a8082016-02-03 14:22:41 -05001671 decorView.startExitTransition(exitTransition, anchorRoot,
1672 new TransitionListenerAdapter() {
1673 @Override
1674 public void onTransitionEnd(Transition transition) {
Alan Viverette79708942016-02-25 16:57:08 -05001675 dismissImmediate(decorView, contentHolder, contentView);
Alan Viverette634a8082016-02-03 14:22:41 -05001676 }
1677 });
Alan Viverettee025ed22015-02-02 11:27:21 -08001678 } else {
Alan Viverette79708942016-02-25 16:57:08 -05001679 dismissImmediate(decorView, contentHolder, contentView);
Alan Viverette7878edf2015-02-03 15:49:18 -08001680 }
1681
Alan Viverette95888c02015-04-16 13:27:50 -07001682 // Clears the anchor view.
Alan Viverette634a8082016-02-03 14:22:41 -05001683 unregisterForViewTreeChanges();
Alan Viverette79708942016-02-25 16:57:08 -05001684
1685 if (mOnDismissListener != null) {
1686 mOnDismissListener.onDismiss();
1687 }
Alan Viverette5435a302015-01-29 10:25:34 -08001688 }
1689
Alan Viverette91098572016-01-19 14:07:31 -05001690 /**
1691 * Returns the window-relative epicenter bounds to be used by enter and
1692 * exit transitions.
1693 * <p>
1694 * <strong>Note:</strong> This is distinct from the rect passed to
1695 * {@link #setEpicenterBounds(Rect)}, which is anchor-relative.
1696 *
1697 * @return the window-relative epicenter bounds to be used by enter and
1698 * exit transitions
1699 */
1700 private Rect getTransitionEpicenter() {
Alan Viverette95888c02015-04-16 13:27:50 -07001701 final View anchor = mAnchor != null ? mAnchor.get() : null;
1702 final View decor = mDecorView;
1703 if (anchor == null || decor == null) {
1704 return null;
1705 }
1706
1707 final int[] anchorLocation = anchor.getLocationOnScreen();
1708 final int[] popupLocation = mDecorView.getLocationOnScreen();
1709
1710 // Compute the position of the anchor relative to the popup.
1711 final Rect bounds = new Rect(0, 0, anchor.getWidth(), anchor.getHeight());
1712 bounds.offset(anchorLocation[0] - popupLocation[0], anchorLocation[1] - popupLocation[1]);
Alan Viverette91098572016-01-19 14:07:31 -05001713
1714 // Use anchor-relative epicenter, if specified.
1715 if (mEpicenterBounds != null) {
1716 final int offsetX = bounds.left;
1717 final int offsetY = bounds.top;
1718 bounds.set(mEpicenterBounds);
1719 bounds.offset(offsetX, offsetY);
1720 }
1721
Alan Viverette95888c02015-04-16 13:27:50 -07001722 return bounds;
1723 }
1724
Alan Viverette5435a302015-01-29 10:25:34 -08001725 /**
1726 * Removes the popup from the window manager and tears down the supporting
1727 * view hierarchy, if necessary.
1728 */
Alan Viverette79708942016-02-25 16:57:08 -05001729 private void dismissImmediate(View decorView, ViewGroup contentHolder, View contentView) {
Alan Viverette8fd949e2015-03-11 12:21:30 -07001730 // If this method gets called and the decor view doesn't have a parent,
1731 // then it was either never added or was already removed. That should
1732 // never happen, but it's worth checking to avoid potential crashes.
1733 if (decorView.getParent() != null) {
1734 mWindowManager.removeViewImmediate(decorView);
Alan Viverettedf4639a02015-03-02 12:40:34 -08001735 }
1736
Alan Viverette8fd949e2015-03-11 12:21:30 -07001737 if (contentHolder != null) {
1738 contentHolder.removeView(contentView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001739 }
Alan Viverette8fd949e2015-03-11 12:21:30 -07001740
1741 // This needs to stay until after all transitions have ended since we
1742 // need the reference to cancel transitions in preparePopup().
1743 mDecorView = null;
Alan Viverette697804e2015-08-06 12:36:47 -04001744 mBackgroundView = null;
Alan Viverette8fd949e2015-03-11 12:21:30 -07001745 mIsTransitioningToDismiss = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001746 }
1747
1748 /**
1749 * Sets the listener to be called when the window is dismissed.
Alan Viverette5435a302015-01-29 10:25:34 -08001750 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001751 * @param onDismissListener The listener.
1752 */
1753 public void setOnDismissListener(OnDismissListener onDismissListener) {
1754 mOnDismissListener = onDismissListener;
1755 }
Alan Viverette5435a302015-01-29 10:25:34 -08001756
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001757 /**
1758 * Updates the state of the popup window, if it is currently being displayed,
Alan Viverette259c2842015-03-22 17:39:39 -07001759 * from the currently set state.
1760 * <p>
1761 * This includes:
1762 * <ul>
1763 * <li>{@link #setClippingEnabled(boolean)}</li>
1764 * <li>{@link #setFocusable(boolean)}</li>
1765 * <li>{@link #setIgnoreCheekPress()}</li>
1766 * <li>{@link #setInputMethodMode(int)}</li>
1767 * <li>{@link #setTouchable(boolean)}</li>
1768 * <li>{@link #setAnimationStyle(int)}</li>
1769 * </ul>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001770 */
1771 public void update() {
1772 if (!isShowing() || mContentView == null) {
1773 return;
1774 }
Alan Viverette5435a302015-01-29 10:25:34 -08001775
1776 final WindowManager.LayoutParams p =
1777 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
1778
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001779 boolean update = false;
Alan Viverette5435a302015-01-29 10:25:34 -08001780
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001781 final int newAnim = computeAnimationResource();
1782 if (newAnim != p.windowAnimations) {
1783 p.windowAnimations = newAnim;
1784 update = true;
1785 }
1786
1787 final int newFlags = computeFlags(p.flags);
1788 if (newFlags != p.flags) {
1789 p.flags = newFlags;
1790 update = true;
1791 }
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001792
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001793 if (update) {
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001794 setLayoutDirectionFromAnchor();
Alan Viverette5435a302015-01-29 10:25:34 -08001795 mWindowManager.updateViewLayout(mDecorView, p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001796 }
1797 }
Romain Guyd6a463a2009-05-21 23:10:10 -07001798
1799 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001800 * Updates the dimension of the popup window.
1801 * <p>
1802 * Calling this function also updates the window with the current popup
1803 * state as described for {@link #update()}.
Romain Guyd6a463a2009-05-21 23:10:10 -07001804 *
Alan Viverette259c2842015-03-22 17:39:39 -07001805 * @param width the new width, must be >= 0 or -1 to ignore
1806 * @param height the new height, must be >= 0 or -1 to ignore
Romain Guyd6a463a2009-05-21 23:10:10 -07001807 */
1808 public void update(int width, int height) {
Alan Viverette5435a302015-01-29 10:25:34 -08001809 final WindowManager.LayoutParams p =
1810 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
Romain Guyd6a463a2009-05-21 23:10:10 -07001811 update(p.x, p.y, width, height, false);
1812 }
Alan Viverette5435a302015-01-29 10:25:34 -08001813
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001814 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001815 * Updates the position and the dimension of the popup window.
1816 * <p>
1817 * Width and height can be set to -1 to update location only. Calling this
1818 * function also updates the window with the current popup state as
1819 * described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001820 *
1821 * @param x the new x location
1822 * @param y the new y location
Alan Viverette259c2842015-03-22 17:39:39 -07001823 * @param width the new width, must be >= 0 or -1 to ignore
1824 * @param height the new height, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001825 */
1826 public void update(int x, int y, int width, int height) {
1827 update(x, y, width, height, false);
1828 }
1829
1830 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001831 * Updates the position and the dimension of the popup window.
1832 * <p>
1833 * Width and height can be set to -1 to update location only. Calling this
1834 * function also updates the window with the current popup state as
1835 * described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001836 *
1837 * @param x the new x location
1838 * @param y the new y location
Alan Viverette259c2842015-03-22 17:39:39 -07001839 * @param width the new width, must be >= 0 or -1 to ignore
1840 * @param height the new height, must be >= 0 or -1 to ignore
1841 * @param force {@code true} to reposition the window even if the specified
1842 * position already seems to correspond to the LayoutParams,
1843 * {@code false} to only reposition if needed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001844 */
1845 public void update(int x, int y, int width, int height, boolean force) {
Alan Viverette259c2842015-03-22 17:39:39 -07001846 if (width >= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001847 mLastWidth = width;
1848 setWidth(width);
1849 }
1850
Alan Viverette259c2842015-03-22 17:39:39 -07001851 if (height >= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001852 mLastHeight = height;
1853 setHeight(height);
1854 }
1855
1856 if (!isShowing() || mContentView == null) {
1857 return;
1858 }
1859
Alan Viverette5435a302015-01-29 10:25:34 -08001860 final WindowManager.LayoutParams p =
1861 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001862
1863 boolean update = force;
1864
1865 final int finalWidth = mWidthMode < 0 ? mWidthMode : mLastWidth;
1866 if (width != -1 && p.width != finalWidth) {
1867 p.width = mLastWidth = finalWidth;
1868 update = true;
1869 }
1870
1871 final int finalHeight = mHeightMode < 0 ? mHeightMode : mLastHeight;
1872 if (height != -1 && p.height != finalHeight) {
1873 p.height = mLastHeight = finalHeight;
1874 update = true;
1875 }
1876
1877 if (p.x != x) {
1878 p.x = x;
1879 update = true;
1880 }
1881
1882 if (p.y != y) {
1883 p.y = y;
1884 update = true;
1885 }
1886
1887 final int newAnim = computeAnimationResource();
1888 if (newAnim != p.windowAnimations) {
1889 p.windowAnimations = newAnim;
1890 update = true;
1891 }
1892
1893 final int newFlags = computeFlags(p.flags);
1894 if (newFlags != p.flags) {
1895 p.flags = newFlags;
1896 update = true;
1897 }
Mike LeBeau98acd542009-05-07 19:04:39 -07001898
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001899 if (update) {
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001900 setLayoutDirectionFromAnchor();
Alan Viverette5435a302015-01-29 10:25:34 -08001901 mWindowManager.updateViewLayout(mDecorView, p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001902 }
1903 }
1904
1905 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001906 * Updates the position and the dimension of the popup window.
1907 * <p>
1908 * Calling this function also updates the window with the current popup
1909 * state as described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001910 *
1911 * @param anchor the popup's anchor view
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(View anchor, int width, int height) {
Alan Viverette75d83792015-01-07 15:51:54 -08001916 update(anchor, false, 0, 0, true, width, height);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001917 }
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()}.
1925 * <p>
1926 * If the view later scrolls to move {@code anchor} to a different
1927 * location, the popup will be moved correspondingly.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001928 *
1929 * @param anchor the popup's anchor view
1930 * @param xoff x offset from the view's left edge
1931 * @param yoff y offset from the view's bottom edge
Alan Viverette259c2842015-03-22 17:39:39 -07001932 * @param width the new width, must be >= 0 or -1 to ignore
1933 * @param height the new height, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001934 */
1935 public void update(View anchor, int xoff, int yoff, int width, int height) {
Alan Viverette75d83792015-01-07 15:51:54 -08001936 update(anchor, true, xoff, yoff, true, width, height);
The Android Open Source Project10592532009-03-18 17:39:46 -07001937 }
1938
1939 private void update(View anchor, boolean updateLocation, int xoff, int yoff,
Alan Viverette75d83792015-01-07 15:51:54 -08001940 boolean updateDimension, int width, int height) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001941
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001942 if (!isShowing() || mContentView == null) {
1943 return;
1944 }
1945
Alan Viverette75d83792015-01-07 15:51:54 -08001946 final WeakReference<View> oldAnchor = mAnchor;
1947 final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff);
Gilles Debunne81f08082011-02-17 14:07:19 -08001948 if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) {
Alan Viverette634a8082016-02-03 14:22:41 -05001949 registerForViewTreeChanges(anchor, xoff, yoff, mAnchoredGravity);
Gilles Debunne81f08082011-02-17 14:07:19 -08001950 } else if (needsUpdate) {
1951 // No need to register again if this is a DropDown, showAsDropDown already did.
1952 mAnchorXoff = xoff;
1953 mAnchorYoff = yoff;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001954 }
1955
The Android Open Source Project10592532009-03-18 17:39:46 -07001956 if (updateDimension) {
1957 if (width == -1) {
1958 width = mPopupWidth;
1959 } else {
1960 mPopupWidth = width;
1961 }
1962 if (height == -1) {
1963 height = mPopupHeight;
1964 } else {
1965 mPopupHeight = height;
1966 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001967 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001968
Alan Viverette75d83792015-01-07 15:51:54 -08001969 final WindowManager.LayoutParams p =
Alan Viverette5435a302015-01-29 10:25:34 -08001970 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
Alan Viverette75d83792015-01-07 15:51:54 -08001971 final int x = p.x;
1972 final int y = p.y;
Romain Guy3e141682010-03-08 17:44:40 -08001973 if (updateLocation) {
Alan Viverette75d83792015-01-07 15:51:54 -08001974 updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff, mAnchoredGravity));
Romain Guy3e141682010-03-08 17:44:40 -08001975 } else {
Adam Powell54c94de2013-09-26 15:36:34 -07001976 updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
1977 mAnchoredGravity));
Romain Guy3e141682010-03-08 17:44:40 -08001978 }
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001979
Romain Guy3e141682010-03-08 17:44:40 -08001980 update(p.x, p.y, width, height, x != p.x || y != p.y);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001981 }
1982
1983 /**
1984 * Listener that is called when this popup window is dismissed.
1985 */
1986 public interface OnDismissListener {
1987 /**
1988 * Called when this popup window is dismissed.
1989 */
1990 public void onDismiss();
1991 }
1992
Alan Viverette634a8082016-02-03 14:22:41 -05001993 private void unregisterForViewTreeChanges() {
1994 final View anchor = mAnchor != null ? mAnchor.get() : null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001995 if (anchor != null) {
Alan Viverettee025ed22015-02-02 11:27:21 -08001996 final ViewTreeObserver vto = anchor.getViewTreeObserver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001997 vto.removeOnScrollChangedListener(mOnScrollChangedListener);
1998 }
Alan Viverettee025ed22015-02-02 11:27:21 -08001999
Alan Viverette634a8082016-02-03 14:22:41 -05002000 final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
2001 if (anchorRoot != null) {
2002 anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
2003 }
2004
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002005 mAnchor = null;
Alan Viverette634a8082016-02-03 14:22:41 -05002006 mAnchorRoot = null;
2007 mIsAnchorRootAttached = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002008 }
2009
Alan Viverette634a8082016-02-03 14:22:41 -05002010 private void registerForViewTreeChanges(View anchor, int xoff, int yoff, int gravity) {
2011 unregisterForViewTreeChanges();
Alan Viverettee025ed22015-02-02 11:27:21 -08002012
2013 final ViewTreeObserver vto = anchor.getViewTreeObserver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002014 if (vto != null) {
2015 vto.addOnScrollChangedListener(mOnScrollChangedListener);
2016 }
2017
Alan Viverette634a8082016-02-03 14:22:41 -05002018 final View anchorRoot = anchor.getRootView();
2019 anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
2020
2021 mAnchor = new WeakReference<>(anchor);
2022 mAnchorRoot = new WeakReference<>(anchorRoot);
2023 mIsAnchorRootAttached = anchorRoot.isAttachedToWindow();
2024
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002025 mAnchorXoff = xoff;
2026 mAnchorYoff = yoff;
Adam Powell54c94de2013-09-26 15:36:34 -07002027 mAnchoredGravity = gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002028 }
2029
Alan Viverette5435a302015-01-29 10:25:34 -08002030 private class PopupDecorView extends FrameLayout {
Alan Viverette8fd949e2015-03-11 12:21:30 -07002031 private TransitionListenerAdapter mPendingExitListener;
2032
Alan Viverette5435a302015-01-29 10:25:34 -08002033 public PopupDecorView(Context context) {
2034 super(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002035 }
2036
2037 @Override
2038 public boolean dispatchKeyEvent(KeyEvent event) {
2039 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
Per Andersson4ae02b32011-01-17 11:16:23 +01002040 if (getKeyDispatcherState() == null) {
2041 return super.dispatchKeyEvent(event);
2042 }
2043
Alan Viverette5435a302015-01-29 10:25:34 -08002044 if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
2045 final KeyEvent.DispatcherState state = getKeyDispatcherState();
Jeff Brownb3ea9222011-01-10 16:26:36 -08002046 if (state != null) {
2047 state.startTracking(event, this);
2048 }
Dianne Hackborn8d374262009-09-14 21:21:52 -07002049 return true;
Jeff Brownb3ea9222011-01-10 16:26:36 -08002050 } else if (event.getAction() == KeyEvent.ACTION_UP) {
Alan Viverette5435a302015-01-29 10:25:34 -08002051 final KeyEvent.DispatcherState state = getKeyDispatcherState();
Jeff Brownb3ea9222011-01-10 16:26:36 -08002052 if (state != null && state.isTracking(event) && !event.isCanceled()) {
2053 dismiss();
2054 return true;
2055 }
Dianne Hackborn8d374262009-09-14 21:21:52 -07002056 }
2057 return super.dispatchKeyEvent(event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002058 } else {
2059 return super.dispatchKeyEvent(event);
2060 }
2061 }
2062
2063 @Override
2064 public boolean dispatchTouchEvent(MotionEvent ev) {
2065 if (mTouchInterceptor != null && mTouchInterceptor.onTouch(this, ev)) {
2066 return true;
2067 }
2068 return super.dispatchTouchEvent(ev);
2069 }
2070
2071 @Override
2072 public boolean onTouchEvent(MotionEvent event) {
2073 final int x = (int) event.getX();
2074 final int y = (int) event.getY();
Alan Viverette5435a302015-01-29 10:25:34 -08002075
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002076 if ((event.getAction() == MotionEvent.ACTION_DOWN)
2077 && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
2078 dismiss();
2079 return true;
2080 } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
2081 dismiss();
2082 return true;
2083 } else {
2084 return super.onTouchEvent(event);
2085 }
2086 }
Alan Viverette8fd949e2015-03-11 12:21:30 -07002087
2088 /**
2089 * Requests that an enter transition run after the next layout pass.
2090 */
2091 public void requestEnterTransition(Transition transition) {
2092 final ViewTreeObserver observer = getViewTreeObserver();
2093 if (observer != null && transition != null) {
2094 final Transition enterTransition = transition.clone();
2095
2096 // Postpone the enter transition after the first layout pass.
2097 observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
2098 @Override
2099 public void onGlobalLayout() {
2100 final ViewTreeObserver observer = getViewTreeObserver();
2101 if (observer != null) {
2102 observer.removeOnGlobalLayoutListener(this);
2103 }
2104
Alan Viverette91098572016-01-19 14:07:31 -05002105 final Rect epicenter = getTransitionEpicenter();
Alan Viverette95888c02015-04-16 13:27:50 -07002106 enterTransition.setEpicenterCallback(new EpicenterCallback() {
2107 @Override
2108 public Rect onGetEpicenter(Transition transition) {
2109 return epicenter;
2110 }
2111 });
Alan Viverette8fd949e2015-03-11 12:21:30 -07002112 startEnterTransition(enterTransition);
2113 }
2114 });
2115 }
2116 }
2117
2118 /**
2119 * Starts the pending enter transition, if one is set.
2120 */
2121 private void startEnterTransition(Transition enterTransition) {
2122 final int count = getChildCount();
2123 for (int i = 0; i < count; i++) {
2124 final View child = getChildAt(i);
2125 enterTransition.addTarget(child);
2126 child.setVisibility(View.INVISIBLE);
2127 }
2128
2129 TransitionManager.beginDelayedTransition(this, enterTransition);
2130
2131 for (int i = 0; i < count; i++) {
2132 final View child = getChildAt(i);
2133 child.setVisibility(View.VISIBLE);
2134 }
2135 }
2136
2137 /**
2138 * Starts an exit transition immediately.
2139 * <p>
2140 * <strong>Note:</strong> The transition listener is guaranteed to have
2141 * its {@code onTransitionEnd} method called even if the transition
2142 * never starts; however, it may be called with a {@code null} argument.
2143 */
Alan Viverette634a8082016-02-03 14:22:41 -05002144 public void startExitTransition(Transition transition, final View anchorRoot,
2145 final TransitionListener listener) {
Alan Viverette8fd949e2015-03-11 12:21:30 -07002146 if (transition == null) {
2147 return;
2148 }
2149
Alan Viverette634a8082016-02-03 14:22:41 -05002150 // The anchor view's window may go away while we're executing our
2151 // transition, in which case we need to end the transition
2152 // immediately and execute the listener to remove the popup.
2153 anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
2154
Alan Viverette8fd949e2015-03-11 12:21:30 -07002155 // The exit listener MUST be called for cleanup, even if the
2156 // transition never starts or ends. Stash it for later.
2157 mPendingExitListener = new TransitionListenerAdapter() {
2158 @Override
2159 public void onTransitionEnd(Transition transition) {
Alan Viverette634a8082016-02-03 14:22:41 -05002160 anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
Alan Viverette8fd949e2015-03-11 12:21:30 -07002161 listener.onTransitionEnd(transition);
2162
2163 // The listener was called. Our job here is done.
2164 mPendingExitListener = null;
2165 }
2166 };
2167
2168 final Transition exitTransition = transition.clone();
2169 exitTransition.addListener(mPendingExitListener);
2170
2171 final int count = getChildCount();
2172 for (int i = 0; i < count; i++) {
2173 final View child = getChildAt(i);
2174 exitTransition.addTarget(child);
2175 }
2176
2177 TransitionManager.beginDelayedTransition(this, exitTransition);
2178
2179 for (int i = 0; i < count; i++) {
2180 final View child = getChildAt(i);
2181 child.setVisibility(View.INVISIBLE);
2182 }
2183 }
2184
2185 /**
2186 * Cancels all pending or current transitions.
2187 */
2188 public void cancelTransitions() {
2189 TransitionManager.endTransitions(this);
2190
2191 if (mPendingExitListener != null) {
2192 mPendingExitListener.onTransitionEnd(null);
2193 }
2194 }
Alan Viverette634a8082016-02-03 14:22:41 -05002195
2196 private final OnAttachStateChangeListener mOnAnchorRootDetachedListener =
2197 new OnAttachStateChangeListener() {
2198 @Override
2199 public void onViewAttachedToWindow(View v) {}
2200
2201 @Override
2202 public void onViewDetachedFromWindow(View v) {
2203 v.removeOnAttachStateChangeListener(this);
2204
2205 TransitionManager.endTransitions(PopupDecorView.this);
2206 }
2207 };
Alan Viverette5435a302015-01-29 10:25:34 -08002208 }
svetoslavganov75986cf2009-05-14 22:28:01 -07002209
Alan Viverette5435a302015-01-29 10:25:34 -08002210 private class PopupBackgroundView extends FrameLayout {
2211 public PopupBackgroundView(Context context) {
2212 super(context);
2213 }
2214
svetoslavganov75986cf2009-05-14 22:28:01 -07002215 @Override
Alan Viverette5435a302015-01-29 10:25:34 -08002216 protected int[] onCreateDrawableState(int extraSpace) {
2217 if (mAboveAnchor) {
2218 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
2219 View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET);
2220 return drawableState;
svetoslavganov75986cf2009-05-14 22:28:01 -07002221 } else {
Alan Viverette5435a302015-01-29 10:25:34 -08002222 return super.onCreateDrawableState(extraSpace);
svetoslavganov75986cf2009-05-14 22:28:01 -07002223 }
2224 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002225 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002226}