blob: 54b3932dfeb23f252d9787f3ae4f4c76a7697d2e [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;
Adam Powella7287f42010-08-17 21:17:04 -070026import android.content.res.Resources;
svetoslavganov75986cf2009-05-14 22:28:01 -070027import android.content.res.TypedArray;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.graphics.PixelFormat;
29import android.graphics.Rect;
30import android.graphics.drawable.Drawable;
31import android.graphics.drawable.StateListDrawable;
Jeff Brown46e75292010-11-10 16:53:45 -080032import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.os.IBinder;
Alan Viverette5435a302015-01-29 10:25:34 -080034import android.transition.Transition;
35import android.transition.Transition.EpicenterCallback;
Alan Viverette8fd949e2015-03-11 12:21:30 -070036import android.transition.Transition.TransitionListener;
37import android.transition.Transition.TransitionListenerAdapter;
Alan Viverette5435a302015-01-29 10:25:34 -080038import android.transition.TransitionInflater;
39import android.transition.TransitionManager;
40import android.transition.TransitionSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.util.AttributeSet;
Adam Powellc3fa6302010-05-18 11:36:27 -070042import android.view.Gravity;
43import android.view.KeyEvent;
44import android.view.MotionEvent;
45import android.view.View;
Alan Viverette634a8082016-02-03 14:22:41 -050046import android.view.View.OnAttachStateChangeListener;
Adam Powella7287f42010-08-17 21:17:04 -070047import android.view.View.OnTouchListener;
Adam Powellc3fa6302010-05-18 11:36:27 -070048import android.view.ViewGroup;
Alan Viverette8fd949e2015-03-11 12:21:30 -070049import android.view.ViewParent;
Adam Powellc3fa6302010-05-18 11:36:27 -070050import android.view.ViewTreeObserver;
Alan Viverette8fd949e2015-03-11 12:21:30 -070051import android.view.ViewTreeObserver.OnGlobalLayoutListener;
Adam Powellc3fa6302010-05-18 11:36:27 -070052import android.view.ViewTreeObserver.OnScrollChangedListener;
Adam Powella7287f42010-08-17 21:17:04 -070053import android.view.WindowManager;
Alan Viverette259c2842015-03-22 17:39:39 -070054import android.view.WindowManager.LayoutParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055
Adam Powella7287f42010-08-17 21:17:04 -070056import java.lang.ref.WeakReference;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057
58/**
59 * <p>A popup window that can be used to display an arbitrary view. The popup
Scott Kennedy7ed189e2013-01-11 22:31:43 -080060 * window is a floating container that appears on top of the current
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061 * activity.</p>
Alan Viverette5435a302015-01-29 10:25:34 -080062 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063 * @see android.widget.AutoCompleteTextView
64 * @see android.widget.Spinner
65 */
66public class PopupWindow {
67 /**
Romain Guye29f0642009-06-23 21:27:02 -070068 * Mode for {@link #setInputMethodMode(int)}: the requirements for the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069 * input method should be based on the focusability of the popup. That is
70 * if it is focusable than it needs to work with the input method, else
71 * it doesn't.
72 */
73 public static final int INPUT_METHOD_FROM_FOCUSABLE = 0;
Alan Viverette5435a302015-01-29 10:25:34 -080074
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 /**
Romain Guye29f0642009-06-23 21:27:02 -070076 * Mode for {@link #setInputMethodMode(int)}: this popup always needs to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077 * work with an input method, regardless of whether it is focusable. This
78 * means that it will always be displayed so that the user can also operate
79 * the input method while it is shown.
80 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 public static final int INPUT_METHOD_NEEDED = 1;
Alan Viverette5435a302015-01-29 10:25:34 -080082
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 /**
Romain Guye29f0642009-06-23 21:27:02 -070084 * Mode for {@link #setInputMethodMode(int)}: this popup never needs to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 * work with an input method, regardless of whether it is focusable. This
86 * means that it will always be displayed to use as much space on the
87 * screen as needed, regardless of whether this covers the input method.
88 */
89 public static final int INPUT_METHOD_NOT_NEEDED = 2;
Adam Powell54c94de2013-09-26 15:36:34 -070090
91 private static final int DEFAULT_ANCHORED_GRAVITY = Gravity.TOP | Gravity.START;
92
Alan Viverette5435a302015-01-29 10:25:34 -080093 /**
94 * Default animation style indicating that separate animations should be
95 * used for top/bottom anchoring states.
96 */
97 private static final int ANIMATION_STYLE_DEFAULT = -1;
98
99 private final int[] mDrawingLocation = new int[2];
100 private final int[] mScreenLocation = new int[2];
101 private final Rect mTempRect = new Rect();
Alan Viverette5435a302015-01-29 10:25:34 -0800102
Romain Guy448ecf52009-05-14 16:03:42 -0700103 private Context mContext;
104 private WindowManager mWindowManager;
Alan Viverette5435a302015-01-29 10:25:34 -0800105
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 private boolean mIsShowing;
Alan Viverette8fd949e2015-03-11 12:21:30 -0700107 private boolean mIsTransitioningToDismiss;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 private boolean mIsDropdown;
109
Alan Viverette5435a302015-01-29 10:25:34 -0800110 /** View that handles event dispatch and content transitions. */
111 private PopupDecorView mDecorView;
112
Alan Viverette697804e2015-08-06 12:36:47 -0400113 /** View that holds the background and may animate during a transition. */
114 private View mBackgroundView;
115
116 /** The contents of the popup. May be identical to the background view. */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 private View mContentView;
Alan Viverette5435a302015-01-29 10:25:34 -0800118
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119 private boolean mFocusable;
120 private int mInputMethodMode = INPUT_METHOD_FROM_FOCUSABLE;
Dianne Hackborn7eab0942011-01-01 13:21:50 -0800121 private int mSoftInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 private boolean mTouchable = true;
123 private boolean mOutsideTouchable = false;
124 private boolean mClippingEnabled = true;
Jeff Brown46e75292010-11-10 16:53:45 -0800125 private int mSplitTouchEnabled = -1;
Adam Powellba0a2c32010-09-28 17:41:23 -0700126 private boolean mLayoutInScreen;
Adam Powell56c2d332010-11-05 20:03:03 -0700127 private boolean mClipToScreen;
Adam Powell348e69c2011-02-16 16:49:50 -0800128 private boolean mAllowScrollingAnchorParent = true;
Adam Powell0bd1d0a2011-07-22 19:35:06 -0700129 private boolean mLayoutInsetDecor = false;
Adam Powelle0b6cd12011-09-28 22:06:11 -0700130 private boolean mNotTouchModal;
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700131 private boolean mAttachedInDecor = true;
132 private boolean mAttachedInDecorSet = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133
134 private OnTouchListener mTouchInterceptor;
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700135
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 private int mWidthMode;
Alan Viverette259c2842015-03-22 17:39:39 -0700137 private int mWidth = LayoutParams.WRAP_CONTENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 private int mLastWidth;
139 private int mHeightMode;
Alan Viverette259c2842015-03-22 17:39:39 -0700140 private int mHeight = LayoutParams.WRAP_CONTENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 private int mLastHeight;
142
143 private int mPopupWidth;
144 private int mPopupHeight;
Adam Powell56c2d332010-11-05 20:03:03 -0700145
Alan Viveretteccb11e12014-07-08 16:04:02 -0700146 private float mElevation;
147
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 private Drawable mBackground;
149 private Drawable mAboveAnchorBackgroundDrawable;
150 private Drawable mBelowAnchorBackgroundDrawable;
151
Alan Viverette5435a302015-01-29 10:25:34 -0800152 private Transition mEnterTransition;
153 private Transition mExitTransition;
Alan Viverette91098572016-01-19 14:07:31 -0500154 private Rect mEpicenterBounds;
Alan Viverette560f1702014-05-05 14:40:07 -0700155
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156 private boolean mAboveAnchor;
Adam Powell574b37e2010-10-07 11:15:19 -0700157 private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
Alan Viverette5435a302015-01-29 10:25:34 -0800158
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800159 private OnDismissListener mOnDismissListener;
160 private boolean mIgnoreCheekPress = false;
161
Alan Viverette5435a302015-01-29 10:25:34 -0800162 private int mAnimationStyle = ANIMATION_STYLE_DEFAULT;
163
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 private static final int[] ABOVE_ANCHOR_STATE_SET = new int[] {
165 com.android.internal.R.attr.state_above_anchor
166 };
167
Alan Viverette634a8082016-02-03 14:22:41 -0500168 private final OnAttachStateChangeListener mOnAnchorRootDetachedListener =
169 new OnAttachStateChangeListener() {
170 @Override
171 public void onViewAttachedToWindow(View v) {}
172
173 @Override
174 public void onViewDetachedFromWindow(View v) {
175 mIsAnchorRootAttached = false;
176 }
177 };
178
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 private WeakReference<View> mAnchor;
Alan Viverette634a8082016-02-03 14:22:41 -0500180 private WeakReference<View> mAnchorRoot;
181 private boolean mIsAnchorRootAttached;
Alan Viverette560f1702014-05-05 14:40:07 -0700182
Alan Viverette5435a302015-01-29 10:25:34 -0800183 private final OnScrollChangedListener mOnScrollChangedListener = new OnScrollChangedListener() {
184 @Override
185 public void onScrollChanged() {
186 final View anchor = mAnchor != null ? mAnchor.get() : null;
187 if (anchor != null && mDecorView != null) {
188 final WindowManager.LayoutParams p = (WindowManager.LayoutParams)
189 mDecorView.getLayoutParams();
190
191 updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
192 mAnchoredGravity));
193 update(p.x, p.y, -1, -1, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 }
Alan Viverette5435a302015-01-29 10:25:34 -0800195 }
196 };
Alan Viverette560f1702014-05-05 14:40:07 -0700197
Alan Viverette5435a302015-01-29 10:25:34 -0800198 private int mAnchorXoff;
199 private int mAnchorYoff;
200 private int mAnchoredGravity;
Alan Viverette560f1702014-05-05 14:40:07 -0700201 private boolean mOverlapAnchor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202
Fabrice Di Megliob003e282012-10-17 17:20:19 -0700203 private boolean mPopupViewInitialLayoutDirectionInherited;
204
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 /**
206 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
207 *
208 * <p>The popup does provide a background.</p>
209 */
210 public PopupWindow(Context context) {
211 this(context, null);
212 }
213
214 /**
215 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
216 *
217 * <p>The popup does provide a background.</p>
218 */
219 public PopupWindow(Context context, AttributeSet attrs) {
220 this(context, attrs, com.android.internal.R.attr.popupWindowStyle);
221 }
222
223 /**
224 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
225 *
226 * <p>The popup does provide a background.</p>
227 */
Alan Viverette617feb92013-09-09 18:09:13 -0700228 public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
229 this(context, attrs, defStyleAttr, 0);
Adam Powellc3fa6302010-05-18 11:36:27 -0700230 }
Alan Viverette5435a302015-01-29 10:25:34 -0800231
Adam Powellc3fa6302010-05-18 11:36:27 -0700232 /**
233 * <p>Create a new, empty, non focusable popup window of dimension (0,0).</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800234 *
Adam Powellc3fa6302010-05-18 11:36:27 -0700235 * <p>The popup does not provide a background.</p>
236 */
237 public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 mContext = context;
Alan Viverette75d83792015-01-07 15:51:54 -0800239 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240
Alan Viverette617feb92013-09-09 18:09:13 -0700241 final TypedArray a = context.obtainStyledAttributes(
Alan Viverettece8c3582014-11-07 13:19:38 -0800242 attrs, R.styleable.PopupWindow, defStyleAttr, defStyleRes);
243 final Drawable bg = a.getDrawable(R.styleable.PopupWindow_popupBackground);
Alan Viveretteccb11e12014-07-08 16:04:02 -0700244 mElevation = a.getDimension(R.styleable.PopupWindow_popupElevation, 0);
Alan Viverette560f1702014-05-05 14:40:07 -0700245 mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false);
Adam Powellc3808b52010-10-04 10:06:59 -0700246
Alan Viverette5435a302015-01-29 10:25:34 -0800247 // Preserve default behavior from Gingerbread. If the animation is
248 // undefined or explicitly specifies the Gingerbread animation style,
249 // use a sentinel value.
250 if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupAnimationStyle)) {
251 final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, 0);
252 if (animStyle == R.style.Animation_PopupWindow) {
253 mAnimationStyle = ANIMATION_STYLE_DEFAULT;
254 } else {
255 mAnimationStyle = animStyle;
256 }
257 } else {
258 mAnimationStyle = ANIMATION_STYLE_DEFAULT;
259 }
260
261 final Transition enterTransition = getTransition(a.getResourceId(
262 R.styleable.PopupWindow_popupEnterTransition, 0));
263 final Transition exitTransition;
264 if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupExitTransition)) {
265 exitTransition = getTransition(a.getResourceId(
266 R.styleable.PopupWindow_popupExitTransition, 0));
267 } else {
268 exitTransition = enterTransition == null ? null : enterTransition.clone();
269 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271 a.recycle();
Alan Viverettece8c3582014-11-07 13:19:38 -0800272
Alan Viverette5435a302015-01-29 10:25:34 -0800273 setEnterTransition(enterTransition);
274 setExitTransition(exitTransition);
Alan Viverettece8c3582014-11-07 13:19:38 -0800275 setBackgroundDrawable(bg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276 }
277
278 /**
279 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
280 *
281 * <p>The popup does not provide any background. This should be handled
282 * by the content view.</p>
283 */
284 public PopupWindow() {
285 this(null, 0, 0);
286 }
287
288 /**
289 * <p>Create a new non focusable popup window which can display the
290 * <tt>contentView</tt>. The dimension of the window are (0,0).</p>
291 *
292 * <p>The popup does not provide any background. This should be handled
293 * by the content view.</p>
294 *
295 * @param contentView the popup's content
296 */
297 public PopupWindow(View contentView) {
298 this(contentView, 0, 0);
299 }
300
301 /**
302 * <p>Create a new empty, non focusable popup window. The dimension of the
303 * window must be passed to this constructor.</p>
304 *
305 * <p>The popup does not provide any background. This should be handled
306 * by the content view.</p>
307 *
308 * @param width the popup's width
309 * @param height the popup's height
310 */
311 public PopupWindow(int width, int height) {
312 this(null, width, height);
313 }
314
315 /**
316 * <p>Create a new non focusable popup window which can display the
317 * <tt>contentView</tt>. The dimension of the window must be passed to
318 * this constructor.</p>
319 *
320 * <p>The popup does not provide any background. This should be handled
321 * by the content view.</p>
322 *
323 * @param contentView the popup's content
324 * @param width the popup's width
325 * @param height the popup's height
326 */
327 public PopupWindow(View contentView, int width, int height) {
328 this(contentView, width, height, false);
329 }
330
331 /**
332 * <p>Create a new popup window which can display the <tt>contentView</tt>.
333 * The dimension of the window must be passed to this constructor.</p>
334 *
335 * <p>The popup does not provide any background. This should be handled
336 * by the content view.</p>
337 *
338 * @param contentView the popup's content
339 * @param width the popup's width
340 * @param height the popup's height
341 * @param focusable true if the popup can be focused, false otherwise
342 */
Romain Guy448ecf52009-05-14 16:03:42 -0700343 public PopupWindow(View contentView, int width, int height, boolean focusable) {
344 if (contentView != null) {
345 mContext = contentView.getContext();
346 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
347 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700348
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 setContentView(contentView);
350 setWidth(width);
351 setHeight(height);
352 setFocusable(focusable);
353 }
354
Alan Viverette5435a302015-01-29 10:25:34 -0800355 public void setEnterTransition(Transition enterTransition) {
356 mEnterTransition = enterTransition;
Alan Viverette5435a302015-01-29 10:25:34 -0800357 }
358
359 public void setExitTransition(Transition exitTransition) {
360 mExitTransition = exitTransition;
Alan Viverette5435a302015-01-29 10:25:34 -0800361 }
362
Alan Viverette91098572016-01-19 14:07:31 -0500363 /**
364 * Sets the bounds used as the epicenter of the enter and exit transitions.
365 * <p>
366 * Transitions use a point or Rect, referred to as the epicenter, to orient
367 * the direction of travel. For popup windows, the anchor view bounds are
368 * used as the default epicenter.
369 * <p>
370 * See {@link Transition#setEpicenterCallback(EpicenterCallback)} for more
371 * information about how transition epicenters.
372 *
373 * @param bounds the epicenter bounds relative to the anchor view, or
374 * {@code null} to use the default epicenter
375 * @see #getTransitionEpicenter()
376 * @hide
377 */
378 public void setEpicenterBounds(Rect bounds) {
379 mEpicenterBounds = bounds;
380 }
381
Alan Viverette5435a302015-01-29 10:25:34 -0800382 private Transition getTransition(int resId) {
383 if (resId != 0 && resId != R.transition.no_transition) {
384 final TransitionInflater inflater = TransitionInflater.from(mContext);
385 final Transition transition = inflater.inflateTransition(resId);
386 if (transition != null) {
387 final boolean isEmpty = transition instanceof TransitionSet
388 && ((TransitionSet) transition).getTransitionCount() == 0;
389 if (!isEmpty) {
390 return transition;
391 }
392 }
393 }
394 return null;
395 }
396
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800397 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700398 * Return the drawable used as the popup window's background.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 *
Alan Viveretteccb11e12014-07-08 16:04:02 -0700400 * @return the background drawable or {@code null} if not set
401 * @see #setBackgroundDrawable(Drawable)
402 * @attr ref android.R.styleable#PopupWindow_popupBackground
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403 */
404 public Drawable getBackground() {
405 return mBackground;
406 }
407
408 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700409 * Specifies the background drawable for this popup window. The background
410 * can be set to {@code null}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 *
412 * @param background the popup's background
Alan Viveretteccb11e12014-07-08 16:04:02 -0700413 * @see #getBackground()
414 * @attr ref android.R.styleable#PopupWindow_popupBackground
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 */
416 public void setBackgroundDrawable(Drawable background) {
417 mBackground = background;
Alan Viverettece8c3582014-11-07 13:19:38 -0800418
419 // If this is a StateListDrawable, try to find and store the drawable to be
420 // used when the drop-down is placed above its anchor view, and the one to be
421 // used when the drop-down is placed below its anchor view. We extract
422 // the drawables ourselves to work around a problem with using refreshDrawableState
423 // that it will take into account the padding of all drawables specified in a
424 // StateListDrawable, thus adding superfluous padding to drop-down views.
425 //
426 // We assume a StateListDrawable will have a drawable for ABOVE_ANCHOR_STATE_SET and
427 // at least one other drawable, intended for the 'below-anchor state'.
428 if (mBackground instanceof StateListDrawable) {
429 StateListDrawable stateList = (StateListDrawable) mBackground;
430
431 // Find the above-anchor view - this one's easy, it should be labeled as such.
432 int aboveAnchorStateIndex = stateList.getStateDrawableIndex(ABOVE_ANCHOR_STATE_SET);
433
434 // Now, for the below-anchor view, look for any other drawable specified in the
435 // StateListDrawable which is not for the above-anchor state and use that.
436 int count = stateList.getStateCount();
437 int belowAnchorStateIndex = -1;
438 for (int i = 0; i < count; i++) {
439 if (i != aboveAnchorStateIndex) {
440 belowAnchorStateIndex = i;
441 break;
442 }
443 }
444
445 // Store the drawables we found, if we found them. Otherwise, set them both
446 // to null so that we'll just use refreshDrawableState.
447 if (aboveAnchorStateIndex != -1 && belowAnchorStateIndex != -1) {
448 mAboveAnchorBackgroundDrawable = stateList.getStateDrawable(aboveAnchorStateIndex);
449 mBelowAnchorBackgroundDrawable = stateList.getStateDrawable(belowAnchorStateIndex);
450 } else {
451 mBelowAnchorBackgroundDrawable = null;
452 mAboveAnchorBackgroundDrawable = null;
453 }
454 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 }
456
457 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700458 * @return the elevation for this popup window in pixels
459 * @see #setElevation(float)
460 * @attr ref android.R.styleable#PopupWindow_popupElevation
461 */
462 public float getElevation() {
463 return mElevation;
464 }
465
466 /**
467 * Specifies the elevation for this popup window.
468 *
469 * @param elevation the popup's elevation in pixels
470 * @see #getElevation()
471 * @attr ref android.R.styleable#PopupWindow_popupElevation
472 */
473 public void setElevation(float elevation) {
474 mElevation = elevation;
475 }
476
477 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 * <p>Return the animation style to use the popup appears and disappears</p>
479 *
480 * @return the animation style to use the popup appears and disappears
481 */
482 public int getAnimationStyle() {
483 return mAnimationStyle;
484 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700485
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 /**
Shuhrat Dehkanov54ec76d2014-08-28 17:12:23 +0900487 * Set the flag on popup to ignore cheek press events; by default this flag
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488 * is set to false
Shuhrat Dehkanov54ec76d2014-08-28 17:12:23 +0900489 * which means the popup will not ignore cheek press dispatch events.
Alan Viverette5435a302015-01-29 10:25:34 -0800490 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491 * <p>If the popup is showing, calling this method will take effect only
492 * the next time the popup is shown or through a manual call to one of
493 * the {@link #update()} methods.</p>
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700494 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 * @see #update()
496 */
497 public void setIgnoreCheekPress() {
498 mIgnoreCheekPress = true;
499 }
Alan Viverette5435a302015-01-29 10:25:34 -0800500
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501
502 /**
503 * <p>Change the animation style resource for this popup.</p>
504 *
505 * <p>If the popup is showing, calling this method will take effect only
506 * the next time the popup is shown or through a manual call to one of
507 * the {@link #update()} methods.</p>
508 *
509 * @param animationStyle animation style to use when the popup appears
510 * and disappears. Set to -1 for the default animation, 0 for no
511 * animation, or a resource identifier for an explicit animation.
Alan Viverette5435a302015-01-29 10:25:34 -0800512 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513 * @see #update()
514 */
515 public void setAnimationStyle(int animationStyle) {
516 mAnimationStyle = animationStyle;
517 }
Alan Viverette5435a302015-01-29 10:25:34 -0800518
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519 /**
520 * <p>Return the view used as the content of the popup window.</p>
521 *
522 * @return a {@link android.view.View} representing the popup's content
523 *
524 * @see #setContentView(android.view.View)
525 */
526 public View getContentView() {
527 return mContentView;
528 }
529
530 /**
531 * <p>Change the popup's content. The content is represented by an instance
532 * of {@link android.view.View}.</p>
533 *
Gilles Debunne81f08082011-02-17 14:07:19 -0800534 * <p>This method has no effect if called when the popup is showing.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535 *
536 * @param contentView the new content for the popup
537 *
538 * @see #getContentView()
539 * @see #isShowing()
540 */
541 public void setContentView(View contentView) {
542 if (isShowing()) {
543 return;
544 }
545
546 mContentView = contentView;
Romain Guy448ecf52009-05-14 16:03:42 -0700547
Romain Guy0c0b7682011-05-16 11:54:09 -0700548 if (mContext == null && mContentView != null) {
Romain Guy448ecf52009-05-14 16:03:42 -0700549 mContext = mContentView.getContext();
550 }
551
Romain Guy0c0b7682011-05-16 11:54:09 -0700552 if (mWindowManager == null && mContentView != null) {
Romain Guy448ecf52009-05-14 16:03:42 -0700553 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
554 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700555
556 // Setting the default for attachedInDecor based on SDK version here
557 // instead of in the constructor since we might not have the context
558 // object in the constructor. We only want to set default here if the
559 // app hasn't already set the attachedInDecor.
560 if (mContext != null && !mAttachedInDecorSet) {
561 // Attach popup window in decor frame of parent window by default for
562 // {@link Build.VERSION_CODES.LOLLIPOP_MR1} or greater. Keep current
563 // behavior of not attaching to decor frame for older SDKs.
564 setAttachedInDecor(mContext.getApplicationInfo().targetSdkVersion
565 >= Build.VERSION_CODES.LOLLIPOP_MR1);
566 }
567
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568 }
569
570 /**
571 * Set a callback for all touch events being dispatched to the popup
572 * window.
573 */
574 public void setTouchInterceptor(OnTouchListener l) {
575 mTouchInterceptor = l;
576 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700577
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 /**
579 * <p>Indicate whether the popup window can grab the focus.</p>
580 *
581 * @return true if the popup is focusable, false otherwise
582 *
583 * @see #setFocusable(boolean)
584 */
585 public boolean isFocusable() {
586 return mFocusable;
587 }
588
589 /**
590 * <p>Changes the focusability of the popup window. When focusable, the
591 * window will grab the focus from the current focused widget if the popup
592 * contains a focusable {@link android.view.View}. By default a popup
593 * window is not focusable.</p>
594 *
595 * <p>If the popup is showing, calling this method will take effect only
596 * the next time the popup is shown or through a manual call to one of
597 * the {@link #update()} methods.</p>
598 *
599 * @param focusable true if the popup should grab focus, false otherwise.
600 *
601 * @see #isFocusable()
Alan Viverette5435a302015-01-29 10:25:34 -0800602 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603 * @see #update()
604 */
605 public void setFocusable(boolean focusable) {
606 mFocusable = focusable;
607 }
608
609 /**
610 * Return the current value in {@link #setInputMethodMode(int)}.
Alan Viverette5435a302015-01-29 10:25:34 -0800611 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 * @see #setInputMethodMode(int)
613 */
614 public int getInputMethodMode() {
615 return mInputMethodMode;
Alan Viverette5435a302015-01-29 10:25:34 -0800616
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 }
Alan Viverette5435a302015-01-29 10:25:34 -0800618
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800619 /**
620 * Control how the popup operates with an input method: one of
621 * {@link #INPUT_METHOD_FROM_FOCUSABLE}, {@link #INPUT_METHOD_NEEDED},
622 * or {@link #INPUT_METHOD_NOT_NEEDED}.
Alan Viverette5435a302015-01-29 10:25:34 -0800623 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624 * <p>If the popup is showing, calling this method will take effect only
625 * the next time the popup is shown or through a manual call to one of
626 * the {@link #update()} methods.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800627 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 * @see #getInputMethodMode()
629 * @see #update()
630 */
631 public void setInputMethodMode(int mode) {
632 mInputMethodMode = mode;
633 }
Romain Guy374aaaed32009-07-14 15:11:59 -0700634
635 /**
636 * Sets the operating mode for the soft input area.
637 *
638 * @param mode The desired mode, see
639 * {@link android.view.WindowManager.LayoutParams#softInputMode}
640 * for the full list
641 *
642 * @see android.view.WindowManager.LayoutParams#softInputMode
643 * @see #getSoftInputMode()
644 */
645 public void setSoftInputMode(int mode) {
646 mSoftInputMode = mode;
647 }
648
649 /**
650 * Returns the current value in {@link #setSoftInputMode(int)}.
651 *
652 * @see #setSoftInputMode(int)
653 * @see android.view.WindowManager.LayoutParams#softInputMode
654 */
655 public int getSoftInputMode() {
656 return mSoftInputMode;
657 }
Alan Viverette5435a302015-01-29 10:25:34 -0800658
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659 /**
660 * <p>Indicates whether the popup window receives touch events.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800661 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800662 * @return true if the popup is touchable, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800663 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800664 * @see #setTouchable(boolean)
665 */
666 public boolean isTouchable() {
667 return mTouchable;
668 }
669
670 /**
671 * <p>Changes the touchability of the popup window. When touchable, the
672 * window will receive touch events, otherwise touch events will go to the
673 * window below it. By default the window is touchable.</p>
674 *
675 * <p>If the popup is showing, calling this method will take effect only
676 * the next time the popup is shown or through a manual call to one of
677 * the {@link #update()} methods.</p>
678 *
679 * @param touchable true if the popup should receive touch events, false otherwise
680 *
681 * @see #isTouchable()
Alan Viverette5435a302015-01-29 10:25:34 -0800682 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800683 * @see #update()
684 */
685 public void setTouchable(boolean touchable) {
686 mTouchable = touchable;
687 }
688
689 /**
690 * <p>Indicates whether the popup window will be informed of touch events
691 * outside of its window.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800692 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693 * @return true if the popup is outside touchable, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800694 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 * @see #setOutsideTouchable(boolean)
696 */
697 public boolean isOutsideTouchable() {
698 return mOutsideTouchable;
699 }
700
701 /**
702 * <p>Controls whether the pop-up will be informed of touch events outside
703 * of its window. This only makes sense for pop-ups that are touchable
704 * but not focusable, which means touches outside of the window will
705 * be delivered to the window behind. The default is false.</p>
706 *
707 * <p>If the popup is showing, calling this method will take effect only
708 * the next time the popup is shown or through a manual call to one of
709 * the {@link #update()} methods.</p>
710 *
711 * @param touchable true if the popup should receive outside
712 * touch events, false otherwise
713 *
714 * @see #isOutsideTouchable()
Alan Viverette5435a302015-01-29 10:25:34 -0800715 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800716 * @see #update()
717 */
718 public void setOutsideTouchable(boolean touchable) {
719 mOutsideTouchable = touchable;
720 }
721
722 /**
723 * <p>Indicates whether clipping of the popup window is enabled.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800724 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725 * @return true if the clipping is enabled, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800726 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800727 * @see #setClippingEnabled(boolean)
728 */
729 public boolean isClippingEnabled() {
730 return mClippingEnabled;
731 }
732
733 /**
734 * <p>Allows the popup window to extend beyond the bounds of the screen. By default the
735 * window is clipped to the screen boundaries. Setting this to false will allow windows to be
736 * accurately positioned.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800737 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800738 * <p>If the popup is showing, calling this method will take effect only
739 * the next time the popup is shown or through a manual call to one of
740 * the {@link #update()} methods.</p>
741 *
742 * @param enabled false if the window should be allowed to extend outside of the screen
Alan Viverette5435a302015-01-29 10:25:34 -0800743 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744 * @see #isClippingEnabled()
745 * @see #update()
746 */
747 public void setClippingEnabled(boolean enabled) {
748 mClippingEnabled = enabled;
749 }
750
751 /**
Adam Powell56c2d332010-11-05 20:03:03 -0700752 * Clip this popup window to the screen, but not to the containing window.
753 *
754 * @param enabled True to clip to the screen.
755 * @hide
756 */
757 public void setClipToScreenEnabled(boolean enabled) {
758 mClipToScreen = enabled;
759 setClippingEnabled(!enabled);
760 }
Adam Powell348e69c2011-02-16 16:49:50 -0800761
762 /**
763 * Allow PopupWindow to scroll the anchor's parent to provide more room
764 * for the popup. Enabled by default.
765 *
766 * @param enabled True to scroll the anchor's parent when more room is desired by the popup.
767 */
768 void setAllowScrollingAnchorParent(boolean enabled) {
769 mAllowScrollingAnchorParent = enabled;
770 }
Alan Viverette5435a302015-01-29 10:25:34 -0800771
Adam Powell56c2d332010-11-05 20:03:03 -0700772 /**
Jeff Brown01ce2e92010-09-26 22:20:12 -0700773 * <p>Indicates whether the popup window supports splitting touches.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800774 *
Jeff Brown01ce2e92010-09-26 22:20:12 -0700775 * @return true if the touch splitting is enabled, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800776 *
Jeff Brown01ce2e92010-09-26 22:20:12 -0700777 * @see #setSplitTouchEnabled(boolean)
Jeff Brown01ce2e92010-09-26 22:20:12 -0700778 */
779 public boolean isSplitTouchEnabled() {
Jeff Brown46e75292010-11-10 16:53:45 -0800780 if (mSplitTouchEnabled < 0 && mContext != null) {
781 return mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB;
782 }
783 return mSplitTouchEnabled == 1;
Jeff Brown01ce2e92010-09-26 22:20:12 -0700784 }
785
786 /**
787 * <p>Allows the popup window to split touches across other windows that also
Jeff Brown46e75292010-11-10 16:53:45 -0800788 * support split touch. When this flag is false, the first pointer
Jeff Brown01ce2e92010-09-26 22:20:12 -0700789 * that goes down determines the window to which all subsequent touches
Jeff Brown46e75292010-11-10 16:53:45 -0800790 * go until all pointers go up. When this flag is true, each pointer
Jeff Brown01ce2e92010-09-26 22:20:12 -0700791 * (not necessarily the first) that goes down determines the window
792 * to which all subsequent touches of that pointer will go until that
793 * pointer goes up thereby enabling touches with multiple pointers
794 * to be split across multiple windows.</p>
795 *
796 * @param enabled true if the split touches should be enabled, false otherwise
797 * @see #isSplitTouchEnabled()
Jeff Brown01ce2e92010-09-26 22:20:12 -0700798 */
799 public void setSplitTouchEnabled(boolean enabled) {
Jeff Brown46e75292010-11-10 16:53:45 -0800800 mSplitTouchEnabled = enabled ? 1 : 0;
Jeff Brown01ce2e92010-09-26 22:20:12 -0700801 }
802
803 /**
Adam Powellba0a2c32010-09-28 17:41:23 -0700804 * <p>Indicates whether the popup window will be forced into using absolute screen coordinates
805 * for positioning.</p>
806 *
807 * @return true if the window will always be positioned in screen coordinates.
808 * @hide
809 */
810 public boolean isLayoutInScreenEnabled() {
811 return mLayoutInScreen;
812 }
813
814 /**
815 * <p>Allows the popup window to force the flag
816 * {@link WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN}, overriding default behavior.
817 * This will cause the popup to be positioned in absolute screen coordinates.</p>
818 *
819 * @param enabled true if the popup should always be positioned in screen coordinates
820 * @hide
821 */
822 public void setLayoutInScreenEnabled(boolean enabled) {
823 mLayoutInScreen = enabled;
824 }
825
826 /**
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700827 * <p>Indicates whether the popup window will be attached in the decor frame of its parent
828 * window.
829 *
830 * @return true if the window will be attached to the decor frame of its parent window.
831 *
832 * @see #setAttachedInDecor(boolean)
833 * @see WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR
834 */
835 public boolean isAttachedInDecor() {
836 return mAttachedInDecor;
837 }
838
839 /**
840 * <p>This will attach the popup window to the decor frame of the parent window to avoid
841 * overlaping with screen decorations like the navigation bar. Overrides the default behavior of
842 * the flag {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR}.
843 *
844 * <p>By default the flag is set on SDK version {@link Build.VERSION_CODES#LOLLIPOP_MR1} or
845 * greater and cleared on lesser SDK versions.
846 *
847 * @param enabled true if the popup should be attached to the decor frame of its parent window.
848 *
849 * @see WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR
850 */
851 public void setAttachedInDecor(boolean enabled) {
852 mAttachedInDecor = enabled;
853 mAttachedInDecorSet = true;
854 }
855
856 /**
Adam Powell0bd1d0a2011-07-22 19:35:06 -0700857 * Allows the popup window to force the flag
858 * {@link WindowManager.LayoutParams#FLAG_LAYOUT_INSET_DECOR}, overriding default behavior.
859 * This will cause the popup to inset its content to account for system windows overlaying
860 * the screen, such as the status bar.
861 *
862 * <p>This will often be combined with {@link #setLayoutInScreenEnabled(boolean)}.
863 *
864 * @param enabled true if the popup's views should inset content to account for system windows,
865 * the way that decor views behave for full-screen windows.
866 * @hide
867 */
868 public void setLayoutInsetDecor(boolean enabled) {
869 mLayoutInsetDecor = enabled;
870 }
871
872 /**
Alan Viverette80ebe0d2015-04-30 15:53:11 -0700873 * Set the layout type for this window.
874 * <p>
875 * See {@link WindowManager.LayoutParams#type} for possible values.
Adam Powell574b37e2010-10-07 11:15:19 -0700876 *
877 * @param layoutType Layout type for this window.
Chris Banes36344a92015-04-14 10:43:16 +0100878 *
879 * @see WindowManager.LayoutParams#type
Adam Powell574b37e2010-10-07 11:15:19 -0700880 */
881 public void setWindowLayoutType(int layoutType) {
882 mWindowLayoutType = layoutType;
883 }
884
885 /**
Chris Banes36344a92015-04-14 10:43:16 +0100886 * Returns the layout type for this window.
887 *
888 * @see #setWindowLayoutType(int)
Adam Powell574b37e2010-10-07 11:15:19 -0700889 */
890 public int getWindowLayoutType() {
891 return mWindowLayoutType;
892 }
893
894 /**
Adam Powelle0b6cd12011-09-28 22:06:11 -0700895 * Set whether this window is touch modal or if outside touches will be sent to
896 * other windows behind it.
897 * @hide
898 */
899 public void setTouchModal(boolean touchModal) {
900 mNotTouchModal = !touchModal;
901 }
902
903 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904 * <p>Change the width and height measure specs that are given to the
905 * window manager by the popup. By default these are 0, meaning that
906 * the current width or height is requested as an explicit size from
907 * the window manager. You can supply
Alan Viverette5435a302015-01-29 10:25:34 -0800908 * {@link ViewGroup.LayoutParams#WRAP_CONTENT} or
Romain Guy980a9382010-01-08 15:06:28 -0800909 * {@link ViewGroup.LayoutParams#MATCH_PARENT} to have that measure
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910 * spec supplied instead, replacing the absolute width and height that
911 * has been set in the popup.</p>
912 *
913 * <p>If the popup is showing, calling this method will take effect only
914 * the next time the popup is shown.</p>
915 *
916 * @param widthSpec an explicit width measure spec mode, either
917 * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
Romain Guy980a9382010-01-08 15:06:28 -0800918 * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 * width.
920 * @param heightSpec an explicit height measure spec mode, either
921 * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
Romain Guy980a9382010-01-08 15:06:28 -0800922 * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800923 * height.
Alan Viverette259c2842015-03-22 17:39:39 -0700924 *
925 * @deprecated Use {@link #setWidth(int)} and {@link #setHeight(int)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800926 */
Alan Viverette259c2842015-03-22 17:39:39 -0700927 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800928 public void setWindowLayoutMode(int widthSpec, int heightSpec) {
929 mWidthMode = widthSpec;
930 mHeightMode = heightSpec;
931 }
Alan Viverette5435a302015-01-29 10:25:34 -0800932
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800933 /**
Alan Viverette259c2842015-03-22 17:39:39 -0700934 * Returns the popup's height MeasureSpec.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800935 *
936 * @return the height MeasureSpec of the popup
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800937 * @see #setHeight(int)
938 */
939 public int getHeight() {
940 return mHeight;
941 }
942
943 /**
Alan Viverette259c2842015-03-22 17:39:39 -0700944 * Sets the popup's height MeasureSpec.
945 * <p>
946 * If the popup is showing, calling this method will take effect the next
947 * time the popup is shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800948 *
949 * @param height the height MeasureSpec of the popup
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800950 * @see #getHeight()
Alan Viverette5435a302015-01-29 10:25:34 -0800951 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800952 */
953 public void setHeight(int height) {
954 mHeight = height;
955 }
956
957 /**
Alan Viverette259c2842015-03-22 17:39:39 -0700958 * Returns the popup's width MeasureSpec.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800959 *
960 * @return the width MeasureSpec of the popup
Alan Viverette5435a302015-01-29 10:25:34 -0800961 * @see #setWidth(int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800962 */
963 public int getWidth() {
964 return mWidth;
965 }
966
967 /**
Alan Viverette259c2842015-03-22 17:39:39 -0700968 * Sets the popup's width MeasureSpec.
969 * <p>
970 * If the popup is showing, calling this method will take effect the next
971 * time the popup is shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800972 *
973 * @param width the width MeasureSpec of the popup
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800974 * @see #getWidth()
975 * @see #isShowing()
976 */
977 public void setWidth(int width) {
978 mWidth = width;
979 }
980
981 /**
Alan Viverette75d83792015-01-07 15:51:54 -0800982 * Sets whether the popup window should overlap its anchor view when
983 * displayed as a drop-down.
984 * <p>
985 * If the popup is showing, calling this method will take effect only
986 * the next time the popup is shown.
987 *
988 * @param overlapAnchor Whether the popup should overlap its anchor.
989 *
990 * @see #getOverlapAnchor()
991 * @see #isShowing()
992 */
993 public void setOverlapAnchor(boolean overlapAnchor) {
994 mOverlapAnchor = overlapAnchor;
995 }
996
997 /**
998 * Returns whether the popup window should overlap its anchor view when
999 * displayed as a drop-down.
1000 *
1001 * @return Whether the popup should overlap its anchor.
1002 *
1003 * @see #setOverlapAnchor(boolean)
1004 */
1005 public boolean getOverlapAnchor() {
1006 return mOverlapAnchor;
1007 }
1008
1009 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001010 * <p>Indicate whether this popup window is showing on screen.</p>
1011 *
1012 * @return true if the popup is showing, false otherwise
1013 */
1014 public boolean isShowing() {
1015 return mIsShowing;
1016 }
1017
1018 /**
1019 * <p>
1020 * Display the content view in a popup window at the specified location. If the popup window
1021 * cannot fit on screen, it will be clipped. See {@link android.view.WindowManager.LayoutParams}
1022 * for more information on how gravity and the x and y parameters are related. Specifying
1023 * a gravity of {@link android.view.Gravity#NO_GRAVITY} is similar to specifying
1024 * <code>Gravity.LEFT | Gravity.TOP</code>.
1025 * </p>
Alan Viverette5435a302015-01-29 10:25:34 -08001026 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001027 * @param parent a parent view to get the {@link android.view.View#getWindowToken()} token from
1028 * @param gravity the gravity which controls the placement of the popup window
1029 * @param x the popup's x location offset
1030 * @param y the popup's y location offset
1031 */
1032 public void showAtLocation(View parent, int gravity, int x, int y) {
Adam Powell8ee6d7c2011-09-18 14:59:28 -07001033 showAtLocation(parent.getWindowToken(), gravity, x, y);
1034 }
1035
1036 /**
1037 * Display the content view in a popup window at the specified location.
1038 *
1039 * @param token Window token to use for creating the new window
1040 * @param gravity the gravity which controls the placement of the popup window
1041 * @param x the popup's x location offset
1042 * @param y the popup's y location offset
1043 *
1044 * @hide Internal use only. Applications should use
1045 * {@link #showAtLocation(View, int, int, int)} instead.
1046 */
1047 public void showAtLocation(IBinder token, int gravity, int x, int y) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001048 if (isShowing() || mContentView == null) {
1049 return;
1050 }
1051
Alan Viverettee025ed22015-02-02 11:27:21 -08001052 TransitionManager.endTransitions(mDecorView);
1053
Alan Viverette634a8082016-02-03 14:22:41 -05001054 unregisterForViewTreeChanges();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055
1056 mIsShowing = true;
1057 mIsDropdown = false;
1058
Alan Viverettee025ed22015-02-02 11:27:21 -08001059 final WindowManager.LayoutParams p = createPopupLayoutParams(token);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001060 preparePopup(p);
Alan Viverettee025ed22015-02-02 11:27:21 -08001061
1062 // Only override the default if some gravity was specified.
1063 if (gravity != Gravity.NO_GRAVITY) {
1064 p.gravity = gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001065 }
Alan Viverettee025ed22015-02-02 11:27:21 -08001066
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001067 p.x = x;
1068 p.y = y;
Alan Viverettee025ed22015-02-02 11:27:21 -08001069
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070 invokePopup(p);
1071 }
1072
1073 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001074 * Display the content view in a popup window anchored to the bottom-left
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001075 * corner of the anchor view. If there is not enough room on screen to show
1076 * the popup in its entirety, this method tries to find a parent scroll
Alan Viverette75d83792015-01-07 15:51:54 -08001077 * view to scroll. If no parent scroll view can be scrolled, the
1078 * bottom-left corner of the popup is pinned at the top left corner of the
1079 * anchor view.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001080 *
1081 * @param anchor the view on which to pin the popup window
1082 *
1083 * @see #dismiss()
1084 */
1085 public void showAsDropDown(View anchor) {
1086 showAsDropDown(anchor, 0, 0);
1087 }
1088
1089 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001090 * Display the content view in a popup window anchored to the bottom-left
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001091 * corner of the anchor view offset by the specified x and y coordinates.
Alan Viverette75d83792015-01-07 15:51:54 -08001092 * If there is not enough room on screen to show the popup in its entirety,
1093 * this method tries to find a parent scroll view to scroll. If no parent
1094 * scroll view can be scrolled, the bottom-left corner of the popup is
1095 * pinned at the top left corner of the anchor view.
1096 * <p>
1097 * If the view later scrolls to move <code>anchor</code> to a different
1098 * location, the popup will be moved correspondingly.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001099 *
1100 * @param anchor the view on which to pin the popup window
Adam Powell54c94de2013-09-26 15:36:34 -07001101 * @param xoff A horizontal offset from the anchor in pixels
1102 * @param yoff A vertical offset from the anchor in pixels
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001103 *
1104 * @see #dismiss()
1105 */
1106 public void showAsDropDown(View anchor, int xoff, int yoff) {
Adam Powell54c94de2013-09-26 15:36:34 -07001107 showAsDropDown(anchor, xoff, yoff, DEFAULT_ANCHORED_GRAVITY);
1108 }
1109
1110 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001111 * Displays the content view in a popup window anchored to the corner of
1112 * another view. The window is positioned according to the specified
1113 * gravity and offset by the specified x and y coordinates.
1114 * <p>
1115 * If there is not enough room on screen to show the popup in its entirety,
1116 * this method tries to find a parent scroll view to scroll. If no parent
1117 * view can be scrolled, the specified vertical gravity will be ignored and
1118 * the popup will anchor itself such that it is visible.
1119 * <p>
1120 * If the view later scrolls to move <code>anchor</code> to a different
1121 * location, the popup will be moved correspondingly.
Adam Powell54c94de2013-09-26 15:36:34 -07001122 *
1123 * @param anchor the view on which to pin the popup window
1124 * @param xoff A horizontal offset from the anchor in pixels
1125 * @param yoff A vertical offset from the anchor in pixels
1126 * @param gravity Alignment of the popup relative to the anchor
1127 *
1128 * @see #dismiss()
1129 */
1130 public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001131 if (isShowing() || mContentView == null) {
1132 return;
1133 }
1134
Alan Viverettee025ed22015-02-02 11:27:21 -08001135 TransitionManager.endTransitions(mDecorView);
1136
Alan Viverette634a8082016-02-03 14:22:41 -05001137 registerForViewTreeChanges(anchor, xoff, yoff, gravity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001138
1139 mIsShowing = true;
1140 mIsDropdown = true;
1141
Alan Viverettee025ed22015-02-02 11:27:21 -08001142 final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001143 preparePopup(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001144
Alan Viverettee025ed22015-02-02 11:27:21 -08001145 final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff, gravity);
1146 updateAboveAnchor(aboveAnchor);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001147
1148 invokePopup(p);
1149 }
1150
Romain Guy3e141682010-03-08 17:44:40 -08001151 private void updateAboveAnchor(boolean aboveAnchor) {
1152 if (aboveAnchor != mAboveAnchor) {
1153 mAboveAnchor = aboveAnchor;
1154
Alan Viverette697804e2015-08-06 12:36:47 -04001155 if (mBackground != null && mBackgroundView != null) {
1156 // If the background drawable provided was a StateListDrawable
1157 // with above-anchor and below-anchor states, use those.
1158 // Otherwise, rely on refreshDrawableState to do the job.
Romain Guy3e141682010-03-08 17:44:40 -08001159 if (mAboveAnchorBackgroundDrawable != null) {
1160 if (mAboveAnchor) {
Alan Viverette697804e2015-08-06 12:36:47 -04001161 mBackgroundView.setBackground(mAboveAnchorBackgroundDrawable);
Romain Guy3e141682010-03-08 17:44:40 -08001162 } else {
Alan Viverette697804e2015-08-06 12:36:47 -04001163 mBackgroundView.setBackground(mBelowAnchorBackgroundDrawable);
Romain Guy3e141682010-03-08 17:44:40 -08001164 }
1165 } else {
Alan Viverette697804e2015-08-06 12:36:47 -04001166 mBackgroundView.refreshDrawableState();
Romain Guy3e141682010-03-08 17:44:40 -08001167 }
1168 }
1169 }
1170 }
1171
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001172 /**
1173 * Indicates whether the popup is showing above (the y coordinate of the popup's bottom
1174 * is less than the y coordinate of the anchor) or below the anchor view (the y coordinate
1175 * of the popup is greater than y coordinate of the anchor's bottom).
1176 *
1177 * The value returned
1178 * by this method is meaningful only after {@link #showAsDropDown(android.view.View)}
1179 * or {@link #showAsDropDown(android.view.View, int, int)} was invoked.
1180 *
1181 * @return True if this popup is showing above the anchor view, false otherwise.
1182 */
1183 public boolean isAboveAnchor() {
1184 return mAboveAnchor;
1185 }
1186
1187 /**
Alan Viverettee025ed22015-02-02 11:27:21 -08001188 * Prepare the popup by embedding it into a new ViewGroup if the background
1189 * drawable is not null. If embedding is required, the layout parameters'
1190 * height is modified to take into account the background's padding.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001191 *
1192 * @param p the layout parameters of the popup's content view
1193 */
1194 private void preparePopup(WindowManager.LayoutParams p) {
Romain Guy448ecf52009-05-14 16:03:42 -07001195 if (mContentView == null || mContext == null || mWindowManager == null) {
1196 throw new IllegalStateException("You must specify a valid content view by "
1197 + "calling setContentView() before attempting to show the popup.");
1198 }
1199
Alan Viverette8fd949e2015-03-11 12:21:30 -07001200 // The old decor view may be transitioning out. Make sure it finishes
1201 // and cleans up before we try to create another one.
1202 if (mDecorView != null) {
1203 mDecorView.cancelTransitions();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001204 }
Alan Viveretteccb11e12014-07-08 16:04:02 -07001205
Alan Viverette8fd949e2015-03-11 12:21:30 -07001206 // When a background is available, we embed the content view within
1207 // another view that owns the background drawable.
Alan Viverette8fd949e2015-03-11 12:21:30 -07001208 if (mBackground != null) {
Alan Viverette697804e2015-08-06 12:36:47 -04001209 mBackgroundView = createBackgroundView(mContentView);
1210 mBackgroundView.setBackground(mBackground);
Alan Viverette8fd949e2015-03-11 12:21:30 -07001211 } else {
Alan Viverette697804e2015-08-06 12:36:47 -04001212 mBackgroundView = mContentView;
Alan Viverette8fd949e2015-03-11 12:21:30 -07001213 }
1214
Alan Viverette697804e2015-08-06 12:36:47 -04001215 mDecorView = createDecorView(mBackgroundView);
Alan Viverette5435a302015-01-29 10:25:34 -08001216
1217 // The background owner should be elevated so that it casts a shadow.
Alan Viverette697804e2015-08-06 12:36:47 -04001218 mBackgroundView.setElevation(mElevation);
Alan Viverette5435a302015-01-29 10:25:34 -08001219
1220 // We may wrap that in another view, so we'll need to manually specify
1221 // the surface insets.
Alan Viverette697804e2015-08-06 12:36:47 -04001222 final int surfaceInset = (int) Math.ceil(mBackgroundView.getZ() * 2);
Alan Viverette5435a302015-01-29 10:25:34 -08001223 p.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
1224 p.hasManualSurfaceInsets = true;
1225
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001226 mPopupViewInitialLayoutDirectionInherited =
Alan Viverette5435a302015-01-29 10:25:34 -08001227 (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001228 mPopupWidth = p.width;
1229 mPopupHeight = p.height;
1230 }
1231
1232 /**
Alan Viverette5435a302015-01-29 10:25:34 -08001233 * Wraps a content view in a PopupViewContainer.
1234 *
1235 * @param contentView the content view to wrap
1236 * @return a PopupViewContainer that wraps the content view
1237 */
1238 private PopupBackgroundView createBackgroundView(View contentView) {
1239 final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
1240 final int height;
1241 if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
1242 height = ViewGroup.LayoutParams.WRAP_CONTENT;
1243 } else {
1244 height = ViewGroup.LayoutParams.MATCH_PARENT;
1245 }
1246
1247 final PopupBackgroundView backgroundView = new PopupBackgroundView(mContext);
1248 final PopupBackgroundView.LayoutParams listParams = new PopupBackgroundView.LayoutParams(
1249 ViewGroup.LayoutParams.MATCH_PARENT, height);
1250 backgroundView.addView(contentView, listParams);
1251
1252 return backgroundView;
1253 }
1254
1255 /**
1256 * Wraps a content view in a FrameLayout.
1257 *
1258 * @param contentView the content view to wrap
1259 * @return a FrameLayout that wraps the content view
1260 */
1261 private PopupDecorView createDecorView(View contentView) {
1262 final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
1263 final int height;
1264 if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
1265 height = ViewGroup.LayoutParams.WRAP_CONTENT;
1266 } else {
1267 height = ViewGroup.LayoutParams.MATCH_PARENT;
1268 }
1269
1270 final PopupDecorView decorView = new PopupDecorView(mContext);
1271 decorView.addView(contentView, ViewGroup.LayoutParams.MATCH_PARENT, height);
1272 decorView.setClipChildren(false);
1273 decorView.setClipToPadding(false);
1274
1275 return decorView;
1276 }
1277
1278 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001279 * <p>Invoke the popup window by adding the content view to the window
1280 * manager.</p>
1281 *
1282 * <p>The content view must be non-null when this method is invoked.</p>
1283 *
1284 * @param p the layout parameters of the popup's content view
1285 */
1286 private void invokePopup(WindowManager.LayoutParams p) {
Romain Guy0c0b7682011-05-16 11:54:09 -07001287 if (mContext != null) {
1288 p.packageName = mContext.getPackageName();
1289 }
Alan Viverette5435a302015-01-29 10:25:34 -08001290
Alan Viverette8fd949e2015-03-11 12:21:30 -07001291 final PopupDecorView decorView = mDecorView;
1292 decorView.setFitsSystemWindows(mLayoutInsetDecor);
Alan Viverette8fd949e2015-03-11 12:21:30 -07001293
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001294 setLayoutDirectionFromAnchor();
Alan Viverette5435a302015-01-29 10:25:34 -08001295
Alan Viverette8fd949e2015-03-11 12:21:30 -07001296 mWindowManager.addView(decorView, p);
Alan Viverette95888c02015-04-16 13:27:50 -07001297
1298 if (mEnterTransition != null) {
1299 decorView.requestEnterTransition(mEnterTransition);
1300 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001301 }
1302
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001303 private void setLayoutDirectionFromAnchor() {
1304 if (mAnchor != null) {
1305 View anchor = mAnchor.get();
1306 if (anchor != null && mPopupViewInitialLayoutDirectionInherited) {
Alan Viverette5435a302015-01-29 10:25:34 -08001307 mDecorView.setLayoutDirection(anchor.getLayoutDirection());
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001308 }
1309 }
1310 }
1311
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001312 /**
1313 * <p>Generate the layout parameters for the popup window.</p>
1314 *
1315 * @param token the window token used to bind the popup's window
1316 *
1317 * @return the layout parameters to pass to the window manager
1318 */
Alan Viverettee025ed22015-02-02 11:27:21 -08001319 private WindowManager.LayoutParams createPopupLayoutParams(IBinder token) {
1320 final WindowManager.LayoutParams p = new WindowManager.LayoutParams();
1321
1322 // These gravity settings put the view at the top left corner of the
1323 // screen. The view is then positioned to the appropriate location by
1324 // setting the x and y offsets to match the anchor's bottom-left
1325 // corner.
Fabrice Di Meglioaac0d4e2012-07-19 19:21:26 -07001326 p.gravity = Gravity.START | Gravity.TOP;
Alan Viverettee025ed22015-02-02 11:27:21 -08001327 p.flags = computeFlags(p.flags);
1328 p.type = mWindowLayoutType;
1329 p.token = token;
1330 p.softInputMode = mSoftInputMode;
1331 p.windowAnimations = computeAnimationResource();
1332
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001333 if (mBackground != null) {
1334 p.format = mBackground.getOpacity();
1335 } else {
1336 p.format = PixelFormat.TRANSLUCENT;
1337 }
Alan Viverettee025ed22015-02-02 11:27:21 -08001338
1339 if (mHeightMode < 0) {
1340 p.height = mLastHeight = mHeightMode;
1341 } else {
1342 p.height = mLastHeight = mHeight;
1343 }
1344
1345 if (mWidthMode < 0) {
1346 p.width = mLastWidth = mWidthMode;
1347 } else {
1348 p.width = mLastWidth = mWidth;
1349 }
1350
Wale Ogunwale8216eb22015-12-18 10:42:42 -08001351 p.privateFlags = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH
1352 | PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
Robert Carra1eb4392015-12-10 12:43:51 -08001353
Alan Viverettee025ed22015-02-02 11:27:21 -08001354 // Used for debugging.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001355 p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
1356
1357 return p;
1358 }
1359
1360 private int computeFlags(int curFlags) {
1361 curFlags &= ~(
1362 WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES |
1363 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
1364 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
1365 WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
1366 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
Adam Powellba0a2c32010-09-28 17:41:23 -07001367 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
1368 WindowManager.LayoutParams.FLAG_SPLIT_TOUCH);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001369 if(mIgnoreCheekPress) {
1370 curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
1371 }
1372 if (!mFocusable) {
1373 curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1374 if (mInputMethodMode == INPUT_METHOD_NEEDED) {
1375 curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1376 }
1377 } else if (mInputMethodMode == INPUT_METHOD_NOT_NEEDED) {
1378 curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1379 }
1380 if (!mTouchable) {
1381 curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
1382 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001383 if (mOutsideTouchable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001384 curFlags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
1385 }
1386 if (!mClippingEnabled) {
1387 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
1388 }
Jeff Brown46e75292010-11-10 16:53:45 -08001389 if (isSplitTouchEnabled()) {
Jeff Brown01ce2e92010-09-26 22:20:12 -07001390 curFlags |= WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
1391 }
Adam Powellba0a2c32010-09-28 17:41:23 -07001392 if (mLayoutInScreen) {
1393 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
1394 }
Adam Powell0bd1d0a2011-07-22 19:35:06 -07001395 if (mLayoutInsetDecor) {
1396 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
1397 }
Adam Powelle0b6cd12011-09-28 22:06:11 -07001398 if (mNotTouchModal) {
1399 curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
1400 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -07001401 if (mAttachedInDecor) {
1402 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR;
1403 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001404 return curFlags;
1405 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -07001406
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001407 private int computeAnimationResource() {
Alan Viverette5435a302015-01-29 10:25:34 -08001408 if (mAnimationStyle == ANIMATION_STYLE_DEFAULT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001409 if (mIsDropdown) {
1410 return mAboveAnchor
1411 ? com.android.internal.R.style.Animation_DropDownUp
1412 : com.android.internal.R.style.Animation_DropDownDown;
1413 }
1414 return 0;
1415 }
1416 return mAnimationStyle;
1417 }
Alan Viverette560f1702014-05-05 14:40:07 -07001418
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001419 /**
Alan Viverette560f1702014-05-05 14:40:07 -07001420 * Positions the popup window on screen. When the popup window is too tall
1421 * to fit under the anchor, a parent scroll view is seeked and scrolled up
1422 * to reclaim space. If scrolling is not possible or not enough, the popup
1423 * window gets moved on top of the anchor.
1424 * <p>
1425 * The height must have been set on the layout parameters prior to calling
1426 * this method.
Alan Viverette5435a302015-01-29 10:25:34 -08001427 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001428 * @param anchor the view on which the popup window must be anchored
1429 * @param p the layout parameters used to display the drop down
Alan Viverette560f1702014-05-05 14:40:07 -07001430 * @param xoff horizontal offset used to adjust for background padding
1431 * @param yoff vertical offset used to adjust for background padding
1432 * @param gravity horizontal gravity specifying popup alignment
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001433 * @return true if the popup is translated upwards to fit on screen
1434 */
Alan Viverette560f1702014-05-05 14:40:07 -07001435 private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p, int xoff,
1436 int yoff, int gravity) {
Adam Powell62e2bde2011-08-15 15:50:05 -07001437 final int anchorHeight = anchor.getHeight();
Alan Viverette560f1702014-05-05 14:40:07 -07001438 final int anchorWidth = anchor.getWidth();
1439 if (mOverlapAnchor) {
1440 yoff -= anchorHeight;
1441 }
1442
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001443 anchor.getLocationInWindow(mDrawingLocation);
1444 p.x = mDrawingLocation[0] + xoff;
Adam Powell62e2bde2011-08-15 15:50:05 -07001445 p.y = mDrawingLocation[1] + anchorHeight + yoff;
Adam Powell54c94de2013-09-26 15:36:34 -07001446
Alan Viverette560f1702014-05-05 14:40:07 -07001447 final int hgrav = Gravity.getAbsoluteGravity(gravity, anchor.getLayoutDirection())
1448 & Gravity.HORIZONTAL_GRAVITY_MASK;
Adam Powell54c94de2013-09-26 15:36:34 -07001449 if (hgrav == Gravity.RIGHT) {
Alan Viverette560f1702014-05-05 14:40:07 -07001450 // Flip the location to align the right sides of the popup and
1451 // anchor instead of left.
1452 p.x -= mPopupWidth - anchorWidth;
Adam Powell54c94de2013-09-26 15:36:34 -07001453 }
Alan Viverette560f1702014-05-05 14:40:07 -07001454
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001455 boolean onTop = false;
1456
Adam Powell54c94de2013-09-26 15:36:34 -07001457 p.gravity = Gravity.LEFT | Gravity.TOP;
1458
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001459 anchor.getLocationOnScreen(mScreenLocation);
1460 final Rect displayFrame = new Rect();
1461 anchor.getWindowVisibleDisplayFrame(displayFrame);
Adam Powell62e2bde2011-08-15 15:50:05 -07001462
Alan Viverette560f1702014-05-05 14:40:07 -07001463 final int screenY = mScreenLocation[1] + anchorHeight + yoff;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001464 final View root = anchor.getRootView();
Alan Viverette560f1702014-05-05 14:40:07 -07001465 if (screenY + mPopupHeight > displayFrame.bottom
1466 || p.x + mPopupWidth - root.getWidth() > 0) {
1467 // If the drop down disappears at the bottom of the screen, we try
1468 // to scroll a parent scrollview or move the drop down back up on
1469 // top of the edit box.
Adam Powellb7c1b202011-02-17 12:03:09 -08001470 if (mAllowScrollingAnchorParent) {
Alan Viverette560f1702014-05-05 14:40:07 -07001471 final int scrollX = anchor.getScrollX();
1472 final int scrollY = anchor.getScrollY();
1473 final Rect r = new Rect(scrollX, scrollY, scrollX + mPopupWidth + xoff,
1474 scrollY + mPopupHeight + anchorHeight + yoff);
Adam Powellb7c1b202011-02-17 12:03:09 -08001475 anchor.requestRectangleOnScreen(r, true);
1476 }
Romain Guy3e141682010-03-08 17:44:40 -08001477
Alan Viverette560f1702014-05-05 14:40:07 -07001478 // Now we re-evaluate the space available, and decide from that
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001479 // whether the pop-up will go above or below the anchor.
1480 anchor.getLocationInWindow(mDrawingLocation);
1481 p.x = mDrawingLocation[0] + xoff;
Alan Viverette560f1702014-05-05 14:40:07 -07001482 p.y = mDrawingLocation[1] + anchorHeight + yoff;
Adam Powell54c94de2013-09-26 15:36:34 -07001483
Alan Viverette560f1702014-05-05 14:40:07 -07001484 // Preserve the gravity adjustment.
Adam Powell54c94de2013-09-26 15:36:34 -07001485 if (hgrav == Gravity.RIGHT) {
Alan Viverette560f1702014-05-05 14:40:07 -07001486 p.x -= mPopupWidth - anchorWidth;
Adam Powell54c94de2013-09-26 15:36:34 -07001487 }
Alan Viverette560f1702014-05-05 14:40:07 -07001488
1489 // Determine whether there is more space above or below the anchor.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001490 anchor.getLocationOnScreen(mScreenLocation);
Alan Viverette560f1702014-05-05 14:40:07 -07001491 onTop = (displayFrame.bottom - mScreenLocation[1] - anchorHeight - yoff) <
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001492 (mScreenLocation[1] - yoff - displayFrame.top);
Oren Blasberged391262015-09-01 12:12:51 -07001493 if (!mOverlapAnchor) {
1494 if (onTop) {
1495 p.gravity = Gravity.LEFT | Gravity.BOTTOM;
1496 p.y = root.getHeight() - mDrawingLocation[1] + yoff;
1497 } else {
1498 p.y = mDrawingLocation[1] + anchorHeight + yoff;
1499 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001500 }
1501 }
1502
Adam Powell56c2d332010-11-05 20:03:03 -07001503 if (mClipToScreen) {
Chong Zhang7c9732d2015-12-29 14:50:05 -08001504 final int winOffsetX = mScreenLocation[0] - mDrawingLocation[0];
1505 final int winOffsetY = mScreenLocation[1] - mDrawingLocation[1];
1506 p.x += winOffsetX;
1507 p.y += winOffsetY;
Adam Powell56c2d332010-11-05 20:03:03 -07001508 final int displayFrameWidth = displayFrame.right - displayFrame.left;
Alan Viverette560f1702014-05-05 14:40:07 -07001509 final int right = p.x + p.width;
Chong Zhang7c9732d2015-12-29 14:50:05 -08001510 if (right > displayFrame.right) {
1511 p.x -= right - displayFrame.right;
Adam Powell56c2d332010-11-05 20:03:03 -07001512 }
Alan Viverette560f1702014-05-05 14:40:07 -07001513
Adam Powell56c2d332010-11-05 20:03:03 -07001514 if (p.x < displayFrame.left) {
1515 p.x = displayFrame.left;
1516 p.width = Math.min(p.width, displayFrameWidth);
1517 }
1518
Oren Blasberged391262015-09-01 12:12:51 -07001519 if (mOverlapAnchor) {
Oren Blasberged391262015-09-01 12:12:51 -07001520 final int bottom = p.y + p.height;
1521 if (bottom > displayFrame.bottom) {
Chong Zhang7c9732d2015-12-29 14:50:05 -08001522 p.y -= bottom - displayFrame.bottom;
Adam Powell5f83a602011-01-19 17:58:04 -08001523 }
1524 } else {
Oren Blasberged391262015-09-01 12:12:51 -07001525 if (onTop) {
1526 final int popupTop = mScreenLocation[1] + yoff - mPopupHeight;
1527 if (popupTop < 0) {
1528 p.y += popupTop;
1529 }
1530 } else {
1531 p.y = Math.max(p.y, displayFrame.top);
1532 }
Adam Powell5f83a602011-01-19 17:58:04 -08001533 }
Chong Zhang7c9732d2015-12-29 14:50:05 -08001534 p.x -= winOffsetX;
1535 p.y -= winOffsetY;
Adam Powell56c2d332010-11-05 20:03:03 -07001536 }
1537
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001538 p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
Alan Viverette560f1702014-05-05 14:40:07 -07001539
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001540 return onTop;
1541 }
Alan Viverette5435a302015-01-29 10:25:34 -08001542
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001543 /**
1544 * Returns the maximum height that is available for the popup to be
1545 * completely shown. It is recommended that this height be the maximum for
1546 * the popup's height, otherwise it is possible that the popup will be
1547 * clipped.
Alan Viverette5435a302015-01-29 10:25:34 -08001548 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001549 * @param anchor The view on which the popup window must be anchored.
1550 * @return The maximum available height for the popup to be completely
1551 * shown.
1552 */
Alan Viveretteb854d072015-09-28 16:12:18 -04001553 public int getMaxAvailableHeight(@NonNull View anchor) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001554 return getMaxAvailableHeight(anchor, 0);
1555 }
1556
1557 /**
1558 * Returns the maximum height that is available for the popup to be
1559 * completely shown. It is recommended that this height be the maximum for
1560 * the popup's height, otherwise it is possible that the popup will be
1561 * clipped.
1562 *
1563 * @param anchor The view on which the popup window must be anchored.
1564 * @param yOffset y offset from the view's bottom edge
1565 * @return The maximum available height for the popup to be completely
1566 * shown.
1567 */
Alan Viveretteb854d072015-09-28 16:12:18 -04001568 public int getMaxAvailableHeight(@NonNull View anchor, int yOffset) {
Mike LeBeau98acd542009-05-07 19:04:39 -07001569 return getMaxAvailableHeight(anchor, yOffset, false);
1570 }
Alan Viverette5435a302015-01-29 10:25:34 -08001571
Mike LeBeau98acd542009-05-07 19:04:39 -07001572 /**
1573 * Returns the maximum height that is available for the popup to be
1574 * completely shown, optionally ignoring any bottom decorations such as
1575 * the input method. It is recommended that this height be the maximum for
1576 * the popup's height, otherwise it is possible that the popup will be
1577 * clipped.
Alan Viverette5435a302015-01-29 10:25:34 -08001578 *
Mike LeBeau98acd542009-05-07 19:04:39 -07001579 * @param anchor The view on which the popup window must be anchored.
1580 * @param yOffset y offset from the view's bottom edge
1581 * @param ignoreBottomDecorations if true, the height returned will be
1582 * all the way to the bottom of the display, ignoring any
1583 * bottom decorations
1584 * @return The maximum available height for the popup to be completely
1585 * shown.
Mike LeBeau98acd542009-05-07 19:04:39 -07001586 */
Alan Viveretteb854d072015-09-28 16:12:18 -04001587 public int getMaxAvailableHeight(
1588 @NonNull View anchor, int yOffset, boolean ignoreBottomDecorations) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001589 final Rect displayFrame = new Rect();
1590 anchor.getWindowVisibleDisplayFrame(displayFrame);
1591
1592 final int[] anchorPos = mDrawingLocation;
1593 anchor.getLocationOnScreen(anchorPos);
Alan Viverette5435a302015-01-29 10:25:34 -08001594
Alan Viveretteb854d072015-09-28 16:12:18 -04001595 final int bottomEdge;
Mike LeBeau98acd542009-05-07 19:04:39 -07001596 if (ignoreBottomDecorations) {
Alan Viveretteb854d072015-09-28 16:12:18 -04001597 final Resources res = anchor.getContext().getResources();
Adam Powell3f4a7642011-05-20 15:56:25 -07001598 bottomEdge = res.getDisplayMetrics().heightPixels;
Alan Viveretteb854d072015-09-28 16:12:18 -04001599 } else {
1600 bottomEdge = displayFrame.bottom;
Mike LeBeau98acd542009-05-07 19:04:39 -07001601 }
Oren Blasberged391262015-09-01 12:12:51 -07001602
1603 final int distanceToBottom;
1604 if (mOverlapAnchor) {
1605 distanceToBottom = bottomEdge - anchorPos[1] - yOffset;
1606 } else {
1607 distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
1608 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001609 final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
1610
1611 // anchorPos[1] is distance from anchor to top of screen
1612 int returnedHeight = Math.max(distanceToBottom, distanceToTop);
1613 if (mBackground != null) {
1614 mBackground.getPadding(mTempRect);
Alan Viverette5435a302015-01-29 10:25:34 -08001615 returnedHeight -= mTempRect.top + mTempRect.bottom;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001616 }
Alan Viverette5435a302015-01-29 10:25:34 -08001617
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001618 return returnedHeight;
1619 }
Alan Viverette5435a302015-01-29 10:25:34 -08001620
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001621 /**
Alan Viverette7878edf2015-02-03 15:49:18 -08001622 * Disposes of the popup window. This method can be invoked only after
1623 * {@link #showAsDropDown(android.view.View)} has been executed. Failing
1624 * that, calling this method will have no effect.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001625 *
Alan Viverette5435a302015-01-29 10:25:34 -08001626 * @see #showAsDropDown(android.view.View)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001627 */
1628 public void dismiss() {
Alan Viverette8fd949e2015-03-11 12:21:30 -07001629 if (!isShowing() || mIsTransitioningToDismiss) {
Alan Viverettee025ed22015-02-02 11:27:21 -08001630 return;
1631 }
Svetoslav Ganov06f938e2011-11-10 14:31:37 -08001632
Alan Viverette8fd949e2015-03-11 12:21:30 -07001633 final PopupDecorView decorView = mDecorView;
1634 final View contentView = mContentView;
1635
1636 final ViewGroup contentHolder;
1637 final ViewParent contentParent = contentView.getParent();
1638 if (contentParent instanceof ViewGroup) {
1639 contentHolder = ((ViewGroup) contentParent);
1640 } else {
1641 contentHolder = null;
1642 }
1643
1644 // Ensure any ongoing or pending transitions are canceled.
1645 decorView.cancelTransitions();
1646
Alan Viverettee025ed22015-02-02 11:27:21 -08001647 mIsShowing = false;
Alan Viverette8fd949e2015-03-11 12:21:30 -07001648 mIsTransitioningToDismiss = true;
Craig Mautnerb82d0742012-05-23 08:48:39 -07001649
Alan Viverette634a8082016-02-03 14:22:41 -05001650 // This method may be called as part of window detachment, in which
1651 // case the anchor view (and its root) will still return true from
1652 // isAttachedToWindow() during execution of this method; however, we
1653 // can expect the OnAttachStateChangeListener to have been called prior
1654 // to executing this method, so we can rely on that instead.
Alan Viverette95888c02015-04-16 13:27:50 -07001655 final Transition exitTransition = mExitTransition;
Alan Viverette21d36182016-02-24 15:34:11 -05001656 if (mIsAnchorRootAttached && exitTransition != null && decorView.isLaidOut()) {
Alan Viverette95888c02015-04-16 13:27:50 -07001657 // The decor view is non-interactive during exit transitions.
1658 final LayoutParams p = (LayoutParams) decorView.getLayoutParams();
1659 p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
1660 p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
1661 mWindowManager.updateViewLayout(decorView, p);
1662
Alan Viverette634a8082016-02-03 14:22:41 -05001663 // Once we start dismissing the decor view, all state (including
1664 // the anchor root) needs to be moved to the decor view since we
1665 // may open another popup while it's busy exiting.
1666 final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
Alan Viverette91098572016-01-19 14:07:31 -05001667 final Rect epicenter = getTransitionEpicenter();
Alan Viverette95888c02015-04-16 13:27:50 -07001668 exitTransition.setEpicenterCallback(new EpicenterCallback() {
1669 @Override
1670 public Rect onGetEpicenter(Transition transition) {
1671 return epicenter;
1672 }
1673 });
Alan Viverette634a8082016-02-03 14:22:41 -05001674 decorView.startExitTransition(exitTransition, anchorRoot,
1675 new TransitionListenerAdapter() {
1676 @Override
1677 public void onTransitionEnd(Transition transition) {
Alan Viverette79708942016-02-25 16:57:08 -05001678 dismissImmediate(decorView, contentHolder, contentView);
Alan Viverette634a8082016-02-03 14:22:41 -05001679 }
1680 });
Alan Viverettee025ed22015-02-02 11:27:21 -08001681 } else {
Alan Viverette79708942016-02-25 16:57:08 -05001682 dismissImmediate(decorView, contentHolder, contentView);
Alan Viverette7878edf2015-02-03 15:49:18 -08001683 }
1684
Alan Viverette95888c02015-04-16 13:27:50 -07001685 // Clears the anchor view.
Alan Viverette634a8082016-02-03 14:22:41 -05001686 unregisterForViewTreeChanges();
Alan Viverette79708942016-02-25 16:57:08 -05001687
1688 if (mOnDismissListener != null) {
1689 mOnDismissListener.onDismiss();
1690 }
Alan Viverette5435a302015-01-29 10:25:34 -08001691 }
1692
Alan Viverette91098572016-01-19 14:07:31 -05001693 /**
1694 * Returns the window-relative epicenter bounds to be used by enter and
1695 * exit transitions.
1696 * <p>
1697 * <strong>Note:</strong> This is distinct from the rect passed to
1698 * {@link #setEpicenterBounds(Rect)}, which is anchor-relative.
1699 *
1700 * @return the window-relative epicenter bounds to be used by enter and
1701 * exit transitions
1702 */
1703 private Rect getTransitionEpicenter() {
Alan Viverette95888c02015-04-16 13:27:50 -07001704 final View anchor = mAnchor != null ? mAnchor.get() : null;
1705 final View decor = mDecorView;
1706 if (anchor == null || decor == null) {
1707 return null;
1708 }
1709
1710 final int[] anchorLocation = anchor.getLocationOnScreen();
1711 final int[] popupLocation = mDecorView.getLocationOnScreen();
1712
1713 // Compute the position of the anchor relative to the popup.
1714 final Rect bounds = new Rect(0, 0, anchor.getWidth(), anchor.getHeight());
1715 bounds.offset(anchorLocation[0] - popupLocation[0], anchorLocation[1] - popupLocation[1]);
Alan Viverette91098572016-01-19 14:07:31 -05001716
1717 // Use anchor-relative epicenter, if specified.
1718 if (mEpicenterBounds != null) {
1719 final int offsetX = bounds.left;
1720 final int offsetY = bounds.top;
1721 bounds.set(mEpicenterBounds);
1722 bounds.offset(offsetX, offsetY);
1723 }
1724
Alan Viverette95888c02015-04-16 13:27:50 -07001725 return bounds;
1726 }
1727
Alan Viverette5435a302015-01-29 10:25:34 -08001728 /**
1729 * Removes the popup from the window manager and tears down the supporting
1730 * view hierarchy, if necessary.
1731 */
Alan Viverette79708942016-02-25 16:57:08 -05001732 private void dismissImmediate(View decorView, ViewGroup contentHolder, View contentView) {
Alan Viverette8fd949e2015-03-11 12:21:30 -07001733 // If this method gets called and the decor view doesn't have a parent,
1734 // then it was either never added or was already removed. That should
1735 // never happen, but it's worth checking to avoid potential crashes.
1736 if (decorView.getParent() != null) {
1737 mWindowManager.removeViewImmediate(decorView);
Alan Viverettedf4639a02015-03-02 12:40:34 -08001738 }
1739
Alan Viverette8fd949e2015-03-11 12:21:30 -07001740 if (contentHolder != null) {
1741 contentHolder.removeView(contentView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001742 }
Alan Viverette8fd949e2015-03-11 12:21:30 -07001743
1744 // This needs to stay until after all transitions have ended since we
1745 // need the reference to cancel transitions in preparePopup().
1746 mDecorView = null;
Alan Viverette697804e2015-08-06 12:36:47 -04001747 mBackgroundView = null;
Alan Viverette8fd949e2015-03-11 12:21:30 -07001748 mIsTransitioningToDismiss = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001749 }
1750
1751 /**
1752 * Sets the listener to be called when the window is dismissed.
Alan Viverette5435a302015-01-29 10:25:34 -08001753 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001754 * @param onDismissListener The listener.
1755 */
1756 public void setOnDismissListener(OnDismissListener onDismissListener) {
1757 mOnDismissListener = onDismissListener;
1758 }
Alan Viverette5435a302015-01-29 10:25:34 -08001759
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001760 /**
1761 * Updates the state of the popup window, if it is currently being displayed,
Alan Viverette259c2842015-03-22 17:39:39 -07001762 * from the currently set state.
1763 * <p>
1764 * This includes:
1765 * <ul>
1766 * <li>{@link #setClippingEnabled(boolean)}</li>
1767 * <li>{@link #setFocusable(boolean)}</li>
1768 * <li>{@link #setIgnoreCheekPress()}</li>
1769 * <li>{@link #setInputMethodMode(int)}</li>
1770 * <li>{@link #setTouchable(boolean)}</li>
1771 * <li>{@link #setAnimationStyle(int)}</li>
1772 * </ul>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001773 */
1774 public void update() {
1775 if (!isShowing() || mContentView == null) {
1776 return;
1777 }
Alan Viverette5435a302015-01-29 10:25:34 -08001778
1779 final WindowManager.LayoutParams p =
1780 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
1781
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001782 boolean update = false;
Alan Viverette5435a302015-01-29 10:25:34 -08001783
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001784 final int newAnim = computeAnimationResource();
1785 if (newAnim != p.windowAnimations) {
1786 p.windowAnimations = newAnim;
1787 update = true;
1788 }
1789
1790 final int newFlags = computeFlags(p.flags);
1791 if (newFlags != p.flags) {
1792 p.flags = newFlags;
1793 update = true;
1794 }
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001795
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001796 if (update) {
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001797 setLayoutDirectionFromAnchor();
Alan Viverette5435a302015-01-29 10:25:34 -08001798 mWindowManager.updateViewLayout(mDecorView, p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001799 }
1800 }
Romain Guyd6a463a2009-05-21 23:10:10 -07001801
1802 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001803 * Updates the dimension of the popup window.
1804 * <p>
1805 * Calling this function also updates the window with the current popup
1806 * state as described for {@link #update()}.
Romain Guyd6a463a2009-05-21 23:10:10 -07001807 *
Alan Viverette259c2842015-03-22 17:39:39 -07001808 * @param width the new width, must be >= 0 or -1 to ignore
1809 * @param height the new height, must be >= 0 or -1 to ignore
Romain Guyd6a463a2009-05-21 23:10:10 -07001810 */
1811 public void update(int width, int height) {
Alan Viverette5435a302015-01-29 10:25:34 -08001812 final WindowManager.LayoutParams p =
1813 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
Romain Guyd6a463a2009-05-21 23:10:10 -07001814 update(p.x, p.y, width, height, false);
1815 }
Alan Viverette5435a302015-01-29 10:25:34 -08001816
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001817 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001818 * Updates the position and the dimension of the popup window.
1819 * <p>
1820 * Width and height can be set to -1 to update location only. Calling this
1821 * function also updates the window with the current popup state as
1822 * described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001823 *
1824 * @param x the new x location
1825 * @param y the new y location
Alan Viverette259c2842015-03-22 17:39:39 -07001826 * @param width the new width, must be >= 0 or -1 to ignore
1827 * @param height the new height, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001828 */
1829 public void update(int x, int y, int width, int height) {
1830 update(x, y, width, height, false);
1831 }
1832
1833 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001834 * Updates the position and the dimension of the popup window.
1835 * <p>
1836 * Width and height can be set to -1 to update location only. Calling this
1837 * function also updates the window with the current popup state as
1838 * described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001839 *
1840 * @param x the new x location
1841 * @param y the new y location
Alan Viverette259c2842015-03-22 17:39:39 -07001842 * @param width the new width, must be >= 0 or -1 to ignore
1843 * @param height the new height, must be >= 0 or -1 to ignore
1844 * @param force {@code true} to reposition the window even if the specified
1845 * position already seems to correspond to the LayoutParams,
1846 * {@code false} to only reposition if needed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001847 */
1848 public void update(int x, int y, int width, int height, boolean force) {
Alan Viverette259c2842015-03-22 17:39:39 -07001849 if (width >= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001850 mLastWidth = width;
1851 setWidth(width);
1852 }
1853
Alan Viverette259c2842015-03-22 17:39:39 -07001854 if (height >= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001855 mLastHeight = height;
1856 setHeight(height);
1857 }
1858
1859 if (!isShowing() || mContentView == null) {
1860 return;
1861 }
1862
Alan Viverette5435a302015-01-29 10:25:34 -08001863 final WindowManager.LayoutParams p =
1864 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001865
1866 boolean update = force;
1867
1868 final int finalWidth = mWidthMode < 0 ? mWidthMode : mLastWidth;
1869 if (width != -1 && p.width != finalWidth) {
1870 p.width = mLastWidth = finalWidth;
1871 update = true;
1872 }
1873
1874 final int finalHeight = mHeightMode < 0 ? mHeightMode : mLastHeight;
1875 if (height != -1 && p.height != finalHeight) {
1876 p.height = mLastHeight = finalHeight;
1877 update = true;
1878 }
1879
1880 if (p.x != x) {
1881 p.x = x;
1882 update = true;
1883 }
1884
1885 if (p.y != y) {
1886 p.y = y;
1887 update = true;
1888 }
1889
1890 final int newAnim = computeAnimationResource();
1891 if (newAnim != p.windowAnimations) {
1892 p.windowAnimations = newAnim;
1893 update = true;
1894 }
1895
1896 final int newFlags = computeFlags(p.flags);
1897 if (newFlags != p.flags) {
1898 p.flags = newFlags;
1899 update = true;
1900 }
Mike LeBeau98acd542009-05-07 19:04:39 -07001901
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001902 if (update) {
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001903 setLayoutDirectionFromAnchor();
Alan Viverette5435a302015-01-29 10:25:34 -08001904 mWindowManager.updateViewLayout(mDecorView, p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001905 }
1906 }
1907
1908 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001909 * Updates the position and the dimension of the popup window.
1910 * <p>
1911 * Calling this function also updates the window with the current popup
1912 * state as described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001913 *
1914 * @param anchor the popup's anchor view
Alan Viverette259c2842015-03-22 17:39:39 -07001915 * @param width the new width, must be >= 0 or -1 to ignore
1916 * @param height the new height, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001917 */
1918 public void update(View anchor, int width, int height) {
Alan Viverette75d83792015-01-07 15:51:54 -08001919 update(anchor, false, 0, 0, true, width, height);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001920 }
1921
1922 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001923 * Updates the position and the dimension of the popup window.
1924 * <p>
1925 * Width and height can be set to -1 to update location only. Calling this
1926 * function also updates the window with the current popup state as
1927 * described for {@link #update()}.
1928 * <p>
1929 * If the view later scrolls to move {@code anchor} to a different
1930 * location, the popup will be moved correspondingly.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001931 *
1932 * @param anchor the popup's anchor view
1933 * @param xoff x offset from the view's left edge
1934 * @param yoff y offset from the view's bottom edge
Alan Viverette259c2842015-03-22 17:39:39 -07001935 * @param width the new width, must be >= 0 or -1 to ignore
1936 * @param height the new height, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001937 */
1938 public void update(View anchor, int xoff, int yoff, int width, int height) {
Alan Viverette75d83792015-01-07 15:51:54 -08001939 update(anchor, true, xoff, yoff, true, width, height);
The Android Open Source Project10592532009-03-18 17:39:46 -07001940 }
1941
1942 private void update(View anchor, boolean updateLocation, int xoff, int yoff,
Alan Viverette75d83792015-01-07 15:51:54 -08001943 boolean updateDimension, int width, int height) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001944
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001945 if (!isShowing() || mContentView == null) {
1946 return;
1947 }
1948
Alan Viverette75d83792015-01-07 15:51:54 -08001949 final WeakReference<View> oldAnchor = mAnchor;
1950 final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff);
Gilles Debunne81f08082011-02-17 14:07:19 -08001951 if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) {
Alan Viverette634a8082016-02-03 14:22:41 -05001952 registerForViewTreeChanges(anchor, xoff, yoff, mAnchoredGravity);
Gilles Debunne81f08082011-02-17 14:07:19 -08001953 } else if (needsUpdate) {
1954 // No need to register again if this is a DropDown, showAsDropDown already did.
1955 mAnchorXoff = xoff;
1956 mAnchorYoff = yoff;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001957 }
1958
The Android Open Source Project10592532009-03-18 17:39:46 -07001959 if (updateDimension) {
1960 if (width == -1) {
1961 width = mPopupWidth;
1962 } else {
1963 mPopupWidth = width;
1964 }
1965 if (height == -1) {
1966 height = mPopupHeight;
1967 } else {
1968 mPopupHeight = height;
1969 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001970 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001971
Alan Viverette75d83792015-01-07 15:51:54 -08001972 final WindowManager.LayoutParams p =
Alan Viverette5435a302015-01-29 10:25:34 -08001973 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
Alan Viverette75d83792015-01-07 15:51:54 -08001974 final int x = p.x;
1975 final int y = p.y;
Romain Guy3e141682010-03-08 17:44:40 -08001976 if (updateLocation) {
Alan Viverette75d83792015-01-07 15:51:54 -08001977 updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff, mAnchoredGravity));
Romain Guy3e141682010-03-08 17:44:40 -08001978 } else {
Adam Powell54c94de2013-09-26 15:36:34 -07001979 updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
1980 mAnchoredGravity));
Romain Guy3e141682010-03-08 17:44:40 -08001981 }
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001982
Romain Guy3e141682010-03-08 17:44:40 -08001983 update(p.x, p.y, width, height, x != p.x || y != p.y);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001984 }
1985
1986 /**
1987 * Listener that is called when this popup window is dismissed.
1988 */
1989 public interface OnDismissListener {
1990 /**
1991 * Called when this popup window is dismissed.
1992 */
1993 public void onDismiss();
1994 }
1995
Alan Viverette634a8082016-02-03 14:22:41 -05001996 private void unregisterForViewTreeChanges() {
1997 final View anchor = mAnchor != null ? mAnchor.get() : null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001998 if (anchor != null) {
Alan Viverettee025ed22015-02-02 11:27:21 -08001999 final ViewTreeObserver vto = anchor.getViewTreeObserver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002000 vto.removeOnScrollChangedListener(mOnScrollChangedListener);
2001 }
Alan Viverettee025ed22015-02-02 11:27:21 -08002002
Alan Viverette634a8082016-02-03 14:22:41 -05002003 final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
2004 if (anchorRoot != null) {
2005 anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
2006 }
2007
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002008 mAnchor = null;
Alan Viverette634a8082016-02-03 14:22:41 -05002009 mAnchorRoot = null;
2010 mIsAnchorRootAttached = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002011 }
2012
Alan Viverette634a8082016-02-03 14:22:41 -05002013 private void registerForViewTreeChanges(View anchor, int xoff, int yoff, int gravity) {
2014 unregisterForViewTreeChanges();
Alan Viverettee025ed22015-02-02 11:27:21 -08002015
2016 final ViewTreeObserver vto = anchor.getViewTreeObserver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002017 if (vto != null) {
2018 vto.addOnScrollChangedListener(mOnScrollChangedListener);
2019 }
2020
Alan Viverette634a8082016-02-03 14:22:41 -05002021 final View anchorRoot = anchor.getRootView();
2022 anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
2023
2024 mAnchor = new WeakReference<>(anchor);
2025 mAnchorRoot = new WeakReference<>(anchorRoot);
2026 mIsAnchorRootAttached = anchorRoot.isAttachedToWindow();
2027
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002028 mAnchorXoff = xoff;
2029 mAnchorYoff = yoff;
Adam Powell54c94de2013-09-26 15:36:34 -07002030 mAnchoredGravity = gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002031 }
2032
Alan Viverette5435a302015-01-29 10:25:34 -08002033 private class PopupDecorView extends FrameLayout {
Alan Viverette8fd949e2015-03-11 12:21:30 -07002034 private TransitionListenerAdapter mPendingExitListener;
2035
Alan Viverette5435a302015-01-29 10:25:34 -08002036 public PopupDecorView(Context context) {
2037 super(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002038 }
2039
2040 @Override
2041 public boolean dispatchKeyEvent(KeyEvent event) {
2042 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
Per Andersson4ae02b32011-01-17 11:16:23 +01002043 if (getKeyDispatcherState() == null) {
2044 return super.dispatchKeyEvent(event);
2045 }
2046
Alan Viverette5435a302015-01-29 10:25:34 -08002047 if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
2048 final KeyEvent.DispatcherState state = getKeyDispatcherState();
Jeff Brownb3ea9222011-01-10 16:26:36 -08002049 if (state != null) {
2050 state.startTracking(event, this);
2051 }
Dianne Hackborn8d374262009-09-14 21:21:52 -07002052 return true;
Jeff Brownb3ea9222011-01-10 16:26:36 -08002053 } else if (event.getAction() == KeyEvent.ACTION_UP) {
Alan Viverette5435a302015-01-29 10:25:34 -08002054 final KeyEvent.DispatcherState state = getKeyDispatcherState();
Jeff Brownb3ea9222011-01-10 16:26:36 -08002055 if (state != null && state.isTracking(event) && !event.isCanceled()) {
2056 dismiss();
2057 return true;
2058 }
Dianne Hackborn8d374262009-09-14 21:21:52 -07002059 }
2060 return super.dispatchKeyEvent(event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002061 } else {
2062 return super.dispatchKeyEvent(event);
2063 }
2064 }
2065
2066 @Override
2067 public boolean dispatchTouchEvent(MotionEvent ev) {
2068 if (mTouchInterceptor != null && mTouchInterceptor.onTouch(this, ev)) {
2069 return true;
2070 }
2071 return super.dispatchTouchEvent(ev);
2072 }
2073
2074 @Override
2075 public boolean onTouchEvent(MotionEvent event) {
2076 final int x = (int) event.getX();
2077 final int y = (int) event.getY();
Alan Viverette5435a302015-01-29 10:25:34 -08002078
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002079 if ((event.getAction() == MotionEvent.ACTION_DOWN)
2080 && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
2081 dismiss();
2082 return true;
2083 } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
2084 dismiss();
2085 return true;
2086 } else {
2087 return super.onTouchEvent(event);
2088 }
2089 }
Alan Viverette8fd949e2015-03-11 12:21:30 -07002090
2091 /**
2092 * Requests that an enter transition run after the next layout pass.
2093 */
2094 public void requestEnterTransition(Transition transition) {
2095 final ViewTreeObserver observer = getViewTreeObserver();
2096 if (observer != null && transition != null) {
2097 final Transition enterTransition = transition.clone();
2098
2099 // Postpone the enter transition after the first layout pass.
2100 observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
2101 @Override
2102 public void onGlobalLayout() {
2103 final ViewTreeObserver observer = getViewTreeObserver();
2104 if (observer != null) {
2105 observer.removeOnGlobalLayoutListener(this);
2106 }
2107
Alan Viverette91098572016-01-19 14:07:31 -05002108 final Rect epicenter = getTransitionEpicenter();
Alan Viverette95888c02015-04-16 13:27:50 -07002109 enterTransition.setEpicenterCallback(new EpicenterCallback() {
2110 @Override
2111 public Rect onGetEpicenter(Transition transition) {
2112 return epicenter;
2113 }
2114 });
Alan Viverette8fd949e2015-03-11 12:21:30 -07002115 startEnterTransition(enterTransition);
2116 }
2117 });
2118 }
2119 }
2120
2121 /**
2122 * Starts the pending enter transition, if one is set.
2123 */
2124 private void startEnterTransition(Transition enterTransition) {
2125 final int count = getChildCount();
2126 for (int i = 0; i < count; i++) {
2127 final View child = getChildAt(i);
2128 enterTransition.addTarget(child);
2129 child.setVisibility(View.INVISIBLE);
2130 }
2131
2132 TransitionManager.beginDelayedTransition(this, enterTransition);
2133
2134 for (int i = 0; i < count; i++) {
2135 final View child = getChildAt(i);
2136 child.setVisibility(View.VISIBLE);
2137 }
2138 }
2139
2140 /**
2141 * Starts an exit transition immediately.
2142 * <p>
2143 * <strong>Note:</strong> The transition listener is guaranteed to have
2144 * its {@code onTransitionEnd} method called even if the transition
2145 * never starts; however, it may be called with a {@code null} argument.
2146 */
Alan Viverette634a8082016-02-03 14:22:41 -05002147 public void startExitTransition(Transition transition, final View anchorRoot,
2148 final TransitionListener listener) {
Alan Viverette8fd949e2015-03-11 12:21:30 -07002149 if (transition == null) {
2150 return;
2151 }
2152
Alan Viverette634a8082016-02-03 14:22:41 -05002153 // The anchor view's window may go away while we're executing our
2154 // transition, in which case we need to end the transition
2155 // immediately and execute the listener to remove the popup.
2156 anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
2157
Alan Viverette8fd949e2015-03-11 12:21:30 -07002158 // The exit listener MUST be called for cleanup, even if the
2159 // transition never starts or ends. Stash it for later.
2160 mPendingExitListener = new TransitionListenerAdapter() {
2161 @Override
2162 public void onTransitionEnd(Transition transition) {
Alan Viverette634a8082016-02-03 14:22:41 -05002163 anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
Alan Viverette8fd949e2015-03-11 12:21:30 -07002164 listener.onTransitionEnd(transition);
2165
2166 // The listener was called. Our job here is done.
2167 mPendingExitListener = null;
2168 }
2169 };
2170
2171 final Transition exitTransition = transition.clone();
2172 exitTransition.addListener(mPendingExitListener);
2173
2174 final int count = getChildCount();
2175 for (int i = 0; i < count; i++) {
2176 final View child = getChildAt(i);
2177 exitTransition.addTarget(child);
2178 }
2179
2180 TransitionManager.beginDelayedTransition(this, exitTransition);
2181
2182 for (int i = 0; i < count; i++) {
2183 final View child = getChildAt(i);
2184 child.setVisibility(View.INVISIBLE);
2185 }
2186 }
2187
2188 /**
2189 * Cancels all pending or current transitions.
2190 */
2191 public void cancelTransitions() {
2192 TransitionManager.endTransitions(this);
2193
2194 if (mPendingExitListener != null) {
2195 mPendingExitListener.onTransitionEnd(null);
2196 }
2197 }
Alan Viverette634a8082016-02-03 14:22:41 -05002198
2199 private final OnAttachStateChangeListener mOnAnchorRootDetachedListener =
2200 new OnAttachStateChangeListener() {
2201 @Override
2202 public void onViewAttachedToWindow(View v) {}
2203
2204 @Override
2205 public void onViewDetachedFromWindow(View v) {
2206 v.removeOnAttachStateChangeListener(this);
2207
2208 TransitionManager.endTransitions(PopupDecorView.this);
2209 }
2210 };
Alan Viverette5435a302015-01-29 10:25:34 -08002211 }
svetoslavganov75986cf2009-05-14 22:28:01 -07002212
Alan Viverette5435a302015-01-29 10:25:34 -08002213 private class PopupBackgroundView extends FrameLayout {
2214 public PopupBackgroundView(Context context) {
2215 super(context);
2216 }
2217
svetoslavganov75986cf2009-05-14 22:28:01 -07002218 @Override
Alan Viverette5435a302015-01-29 10:25:34 -08002219 protected int[] onCreateDrawableState(int extraSpace) {
2220 if (mAboveAnchor) {
2221 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
2222 View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET);
2223 return drawableState;
svetoslavganov75986cf2009-05-14 22:28:01 -07002224 } else {
Alan Viverette5435a302015-01-29 10:25:34 -08002225 return super.onCreateDrawableState(extraSpace);
svetoslavganov75986cf2009-05-14 22:28:01 -07002226 }
2227 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002228 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002229}