blob: c5b5c845e01d33469dd86212cc18d8656858c2db [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.widget;
18
Adam Powella7287f42010-08-17 21:17:04 -070019import com.android.internal.R;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020
svetoslavganov75986cf2009-05-14 22:28:01 -070021import android.content.Context;
Adam Powella7287f42010-08-17 21:17:04 -070022import android.content.res.Resources;
svetoslavganov75986cf2009-05-14 22:28:01 -070023import android.content.res.TypedArray;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.graphics.PixelFormat;
25import android.graphics.Rect;
26import android.graphics.drawable.Drawable;
27import android.graphics.drawable.StateListDrawable;
Jeff Brown46e75292010-11-10 16:53:45 -080028import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.os.IBinder;
Alan Viverette5435a302015-01-29 10:25:34 -080030import android.transition.Transition;
31import android.transition.Transition.EpicenterCallback;
Alan Viverette8fd949e2015-03-11 12:21:30 -070032import android.transition.Transition.TransitionListener;
33import android.transition.Transition.TransitionListenerAdapter;
Alan Viverette5435a302015-01-29 10:25:34 -080034import android.transition.TransitionInflater;
35import android.transition.TransitionManager;
36import android.transition.TransitionSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.util.AttributeSet;
Adam Powellc3fa6302010-05-18 11:36:27 -070038import android.view.Gravity;
39import android.view.KeyEvent;
40import android.view.MotionEvent;
41import android.view.View;
Adam Powella7287f42010-08-17 21:17:04 -070042import android.view.View.OnTouchListener;
Adam Powellc3fa6302010-05-18 11:36:27 -070043import android.view.ViewGroup;
Alan Viverette8fd949e2015-03-11 12:21:30 -070044import android.view.ViewParent;
Adam Powellc3fa6302010-05-18 11:36:27 -070045import android.view.ViewTreeObserver;
Alan Viverette8fd949e2015-03-11 12:21:30 -070046import android.view.ViewTreeObserver.OnGlobalLayoutListener;
Adam Powellc3fa6302010-05-18 11:36:27 -070047import android.view.ViewTreeObserver.OnScrollChangedListener;
Adam Powella7287f42010-08-17 21:17:04 -070048import android.view.WindowManager;
Alan Viverette259c2842015-03-22 17:39:39 -070049import android.view.WindowManager.LayoutParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050
Adam Powella7287f42010-08-17 21:17:04 -070051import java.lang.ref.WeakReference;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052
53/**
54 * <p>A popup window that can be used to display an arbitrary view. The popup
Scott Kennedy7ed189e2013-01-11 22:31:43 -080055 * window is a floating container that appears on top of the current
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056 * activity.</p>
Alan Viverette5435a302015-01-29 10:25:34 -080057 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058 * @see android.widget.AutoCompleteTextView
59 * @see android.widget.Spinner
60 */
61public class PopupWindow {
62 /**
Romain Guye29f0642009-06-23 21:27:02 -070063 * Mode for {@link #setInputMethodMode(int)}: the requirements for the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064 * input method should be based on the focusability of the popup. That is
65 * if it is focusable than it needs to work with the input method, else
66 * it doesn't.
67 */
68 public static final int INPUT_METHOD_FROM_FOCUSABLE = 0;
Alan Viverette5435a302015-01-29 10:25:34 -080069
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 /**
Romain Guye29f0642009-06-23 21:27:02 -070071 * Mode for {@link #setInputMethodMode(int)}: this popup always needs to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 * work with an input method, regardless of whether it is focusable. This
73 * means that it will always be displayed so that the user can also operate
74 * the input method while it is shown.
75 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 public static final int INPUT_METHOD_NEEDED = 1;
Alan Viverette5435a302015-01-29 10:25:34 -080077
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 /**
Romain Guye29f0642009-06-23 21:27:02 -070079 * Mode for {@link #setInputMethodMode(int)}: this popup never needs to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 * work with an input method, regardless of whether it is focusable. This
81 * means that it will always be displayed to use as much space on the
82 * screen as needed, regardless of whether this covers the input method.
83 */
84 public static final int INPUT_METHOD_NOT_NEEDED = 2;
Adam Powell54c94de2013-09-26 15:36:34 -070085
86 private static final int DEFAULT_ANCHORED_GRAVITY = Gravity.TOP | Gravity.START;
87
Alan Viverette5435a302015-01-29 10:25:34 -080088 /**
89 * Default animation style indicating that separate animations should be
90 * used for top/bottom anchoring states.
91 */
92 private static final int ANIMATION_STYLE_DEFAULT = -1;
93
94 private final int[] mDrawingLocation = new int[2];
95 private final int[] mScreenLocation = new int[2];
96 private final Rect mTempRect = new Rect();
97 private final Rect mAnchorBounds = new Rect();
98
Romain Guy448ecf52009-05-14 16:03:42 -070099 private Context mContext;
100 private WindowManager mWindowManager;
Alan Viverette5435a302015-01-29 10:25:34 -0800101
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 private boolean mIsShowing;
Alan Viverette8fd949e2015-03-11 12:21:30 -0700103 private boolean mIsTransitioningToDismiss;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 private boolean mIsDropdown;
105
Alan Viverette5435a302015-01-29 10:25:34 -0800106 /** View that handles event dispatch and content transitions. */
107 private PopupDecorView mDecorView;
108
Alan Viverette5435a302015-01-29 10:25:34 -0800109 /** The contents of the popup. */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 private View mContentView;
Alan Viverette5435a302015-01-29 10:25:34 -0800111
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 private boolean mFocusable;
113 private int mInputMethodMode = INPUT_METHOD_FROM_FOCUSABLE;
Dianne Hackborn7eab0942011-01-01 13:21:50 -0800114 private int mSoftInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 private boolean mTouchable = true;
116 private boolean mOutsideTouchable = false;
117 private boolean mClippingEnabled = true;
Jeff Brown46e75292010-11-10 16:53:45 -0800118 private int mSplitTouchEnabled = -1;
Adam Powellba0a2c32010-09-28 17:41:23 -0700119 private boolean mLayoutInScreen;
Adam Powell56c2d332010-11-05 20:03:03 -0700120 private boolean mClipToScreen;
Adam Powell348e69c2011-02-16 16:49:50 -0800121 private boolean mAllowScrollingAnchorParent = true;
Adam Powell0bd1d0a2011-07-22 19:35:06 -0700122 private boolean mLayoutInsetDecor = false;
Adam Powelle0b6cd12011-09-28 22:06:11 -0700123 private boolean mNotTouchModal;
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700124 private boolean mAttachedInDecor = true;
125 private boolean mAttachedInDecorSet = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126
127 private OnTouchListener mTouchInterceptor;
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700128
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 private int mWidthMode;
Alan Viverette259c2842015-03-22 17:39:39 -0700130 private int mWidth = LayoutParams.WRAP_CONTENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 private int mLastWidth;
132 private int mHeightMode;
Alan Viverette259c2842015-03-22 17:39:39 -0700133 private int mHeight = LayoutParams.WRAP_CONTENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 private int mLastHeight;
135
136 private int mPopupWidth;
137 private int mPopupHeight;
Adam Powell56c2d332010-11-05 20:03:03 -0700138
Alan Viveretteccb11e12014-07-08 16:04:02 -0700139 private float mElevation;
140
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 private Drawable mBackground;
142 private Drawable mAboveAnchorBackgroundDrawable;
143 private Drawable mBelowAnchorBackgroundDrawable;
144
Alan Viverette5435a302015-01-29 10:25:34 -0800145 private Transition mEnterTransition;
146 private Transition mExitTransition;
Alan Viverette560f1702014-05-05 14:40:07 -0700147
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 private boolean mAboveAnchor;
Adam Powell574b37e2010-10-07 11:15:19 -0700149 private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
Alan Viverette5435a302015-01-29 10:25:34 -0800150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 private OnDismissListener mOnDismissListener;
152 private boolean mIgnoreCheekPress = false;
153
Alan Viverette5435a302015-01-29 10:25:34 -0800154 private int mAnimationStyle = ANIMATION_STYLE_DEFAULT;
155
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156 private static final int[] ABOVE_ANCHOR_STATE_SET = new int[] {
157 com.android.internal.R.attr.state_above_anchor
158 };
159
160 private WeakReference<View> mAnchor;
Alan Viverette560f1702014-05-05 14:40:07 -0700161
Alan Viverette5435a302015-01-29 10:25:34 -0800162 private final EpicenterCallback mEpicenterCallback = new EpicenterCallback() {
163 @Override
164 public Rect onGetEpicenter(Transition transition) {
Alan Viverettedf4639a02015-03-02 12:40:34 -0800165 final View anchor = mAnchor != null ? mAnchor.get() : null;
Alan Viverette7878edf2015-02-03 15:49:18 -0800166 final View decor = mDecorView;
167 if (anchor == null || decor == null) {
168 return null;
169 }
170
171 final Rect anchorBounds = mAnchorBounds;
Alan Viverettedf4639a02015-03-02 12:40:34 -0800172 final int[] anchorLocation = anchor.getLocationOnScreen();
Alan Viverette7878edf2015-02-03 15:49:18 -0800173 final int[] popupLocation = mDecorView.getLocationOnScreen();
174
175 // Compute the position of the anchor relative to the popup.
176 anchorBounds.set(0, 0, anchor.getWidth(), anchor.getHeight());
177 anchorBounds.offset(anchorLocation[0] - popupLocation[0],
178 anchorLocation[1] - popupLocation[1]);
179
180 return anchorBounds;
Alan Viverette5435a302015-01-29 10:25:34 -0800181 }
182 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183
Alan Viverette5435a302015-01-29 10:25:34 -0800184 private final OnScrollChangedListener mOnScrollChangedListener = new OnScrollChangedListener() {
185 @Override
186 public void onScrollChanged() {
187 final View anchor = mAnchor != null ? mAnchor.get() : null;
188 if (anchor != null && mDecorView != null) {
189 final WindowManager.LayoutParams p = (WindowManager.LayoutParams)
190 mDecorView.getLayoutParams();
191
192 updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
193 mAnchoredGravity));
194 update(p.x, p.y, -1, -1, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 }
Alan Viverette5435a302015-01-29 10:25:34 -0800196 }
197 };
Alan Viverette560f1702014-05-05 14:40:07 -0700198
Alan Viverette5435a302015-01-29 10:25:34 -0800199 private int mAnchorXoff;
200 private int mAnchorYoff;
201 private int mAnchoredGravity;
Alan Viverette560f1702014-05-05 14:40:07 -0700202 private boolean mOverlapAnchor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203
Fabrice Di Megliob003e282012-10-17 17:20:19 -0700204 private boolean mPopupViewInitialLayoutDirectionInherited;
205
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 /**
207 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
208 *
209 * <p>The popup does provide a background.</p>
210 */
211 public PopupWindow(Context context) {
212 this(context, null);
213 }
214
215 /**
216 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
217 *
218 * <p>The popup does provide a background.</p>
219 */
220 public PopupWindow(Context context, AttributeSet attrs) {
221 this(context, attrs, com.android.internal.R.attr.popupWindowStyle);
222 }
223
224 /**
225 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
226 *
227 * <p>The popup does provide a background.</p>
228 */
Alan Viverette617feb92013-09-09 18:09:13 -0700229 public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
230 this(context, attrs, defStyleAttr, 0);
Adam Powellc3fa6302010-05-18 11:36:27 -0700231 }
Alan Viverette5435a302015-01-29 10:25:34 -0800232
Adam Powellc3fa6302010-05-18 11:36:27 -0700233 /**
234 * <p>Create a new, empty, non focusable popup window of dimension (0,0).</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800235 *
Adam Powellc3fa6302010-05-18 11:36:27 -0700236 * <p>The popup does not provide a background.</p>
237 */
238 public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239 mContext = context;
Alan Viverette75d83792015-01-07 15:51:54 -0800240 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241
Alan Viverette617feb92013-09-09 18:09:13 -0700242 final TypedArray a = context.obtainStyledAttributes(
Alan Viverettece8c3582014-11-07 13:19:38 -0800243 attrs, R.styleable.PopupWindow, defStyleAttr, defStyleRes);
244 final Drawable bg = a.getDrawable(R.styleable.PopupWindow_popupBackground);
Alan Viveretteccb11e12014-07-08 16:04:02 -0700245 mElevation = a.getDimension(R.styleable.PopupWindow_popupElevation, 0);
Alan Viverette560f1702014-05-05 14:40:07 -0700246 mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false);
Adam Powellc3808b52010-10-04 10:06:59 -0700247
Alan Viverette5435a302015-01-29 10:25:34 -0800248 // Preserve default behavior from Gingerbread. If the animation is
249 // undefined or explicitly specifies the Gingerbread animation style,
250 // use a sentinel value.
251 if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupAnimationStyle)) {
252 final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, 0);
253 if (animStyle == R.style.Animation_PopupWindow) {
254 mAnimationStyle = ANIMATION_STYLE_DEFAULT;
255 } else {
256 mAnimationStyle = animStyle;
257 }
258 } else {
259 mAnimationStyle = ANIMATION_STYLE_DEFAULT;
260 }
261
262 final Transition enterTransition = getTransition(a.getResourceId(
263 R.styleable.PopupWindow_popupEnterTransition, 0));
264 final Transition exitTransition;
265 if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupExitTransition)) {
266 exitTransition = getTransition(a.getResourceId(
267 R.styleable.PopupWindow_popupExitTransition, 0));
268 } else {
269 exitTransition = enterTransition == null ? null : enterTransition.clone();
270 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 a.recycle();
Alan Viverettece8c3582014-11-07 13:19:38 -0800273
Alan Viverette5435a302015-01-29 10:25:34 -0800274 setEnterTransition(enterTransition);
275 setExitTransition(exitTransition);
Alan Viverettece8c3582014-11-07 13:19:38 -0800276 setBackgroundDrawable(bg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 }
278
279 /**
280 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
281 *
282 * <p>The popup does not provide any background. This should be handled
283 * by the content view.</p>
284 */
285 public PopupWindow() {
286 this(null, 0, 0);
287 }
288
289 /**
290 * <p>Create a new non focusable popup window which can display the
291 * <tt>contentView</tt>. The dimension of the window are (0,0).</p>
292 *
293 * <p>The popup does not provide any background. This should be handled
294 * by the content view.</p>
295 *
296 * @param contentView the popup's content
297 */
298 public PopupWindow(View contentView) {
299 this(contentView, 0, 0);
300 }
301
302 /**
303 * <p>Create a new empty, non focusable popup window. The dimension of the
304 * window must be passed to this constructor.</p>
305 *
306 * <p>The popup does not provide any background. This should be handled
307 * by the content view.</p>
308 *
309 * @param width the popup's width
310 * @param height the popup's height
311 */
312 public PopupWindow(int width, int height) {
313 this(null, width, height);
314 }
315
316 /**
317 * <p>Create a new non focusable popup window which can display the
318 * <tt>contentView</tt>. The dimension of the window must be passed to
319 * this constructor.</p>
320 *
321 * <p>The popup does not provide any background. This should be handled
322 * by the content view.</p>
323 *
324 * @param contentView the popup's content
325 * @param width the popup's width
326 * @param height the popup's height
327 */
328 public PopupWindow(View contentView, int width, int height) {
329 this(contentView, width, height, false);
330 }
331
332 /**
333 * <p>Create a new popup window which can display the <tt>contentView</tt>.
334 * The dimension of the window must be passed to this constructor.</p>
335 *
336 * <p>The popup does not provide any background. This should be handled
337 * by the content view.</p>
338 *
339 * @param contentView the popup's content
340 * @param width the popup's width
341 * @param height the popup's height
342 * @param focusable true if the popup can be focused, false otherwise
343 */
Romain Guy448ecf52009-05-14 16:03:42 -0700344 public PopupWindow(View contentView, int width, int height, boolean focusable) {
345 if (contentView != null) {
346 mContext = contentView.getContext();
347 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
348 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700349
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 setContentView(contentView);
351 setWidth(width);
352 setHeight(height);
353 setFocusable(focusable);
354 }
355
Alan Viverette5435a302015-01-29 10:25:34 -0800356 public void setEnterTransition(Transition enterTransition) {
357 mEnterTransition = enterTransition;
358
359 if (mEnterTransition != null) {
360 mEnterTransition.setEpicenterCallback(mEpicenterCallback);
361 }
362 }
363
364 public void setExitTransition(Transition exitTransition) {
365 mExitTransition = exitTransition;
366
367 if (mExitTransition != null) {
368 mExitTransition.setEpicenterCallback(mEpicenterCallback);
369 }
370 }
371
372 private Transition getTransition(int resId) {
373 if (resId != 0 && resId != R.transition.no_transition) {
374 final TransitionInflater inflater = TransitionInflater.from(mContext);
375 final Transition transition = inflater.inflateTransition(resId);
376 if (transition != null) {
377 final boolean isEmpty = transition instanceof TransitionSet
378 && ((TransitionSet) transition).getTransitionCount() == 0;
379 if (!isEmpty) {
380 return transition;
381 }
382 }
383 }
384 return null;
385 }
386
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700388 * Return the drawable used as the popup window's background.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 *
Alan Viveretteccb11e12014-07-08 16:04:02 -0700390 * @return the background drawable or {@code null} if not set
391 * @see #setBackgroundDrawable(Drawable)
392 * @attr ref android.R.styleable#PopupWindow_popupBackground
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393 */
394 public Drawable getBackground() {
395 return mBackground;
396 }
397
398 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700399 * Specifies the background drawable for this popup window. The background
400 * can be set to {@code null}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 *
402 * @param background the popup's background
Alan Viveretteccb11e12014-07-08 16:04:02 -0700403 * @see #getBackground()
404 * @attr ref android.R.styleable#PopupWindow_popupBackground
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 */
406 public void setBackgroundDrawable(Drawable background) {
407 mBackground = background;
Alan Viverettece8c3582014-11-07 13:19:38 -0800408
409 // If this is a StateListDrawable, try to find and store the drawable to be
410 // used when the drop-down is placed above its anchor view, and the one to be
411 // used when the drop-down is placed below its anchor view. We extract
412 // the drawables ourselves to work around a problem with using refreshDrawableState
413 // that it will take into account the padding of all drawables specified in a
414 // StateListDrawable, thus adding superfluous padding to drop-down views.
415 //
416 // We assume a StateListDrawable will have a drawable for ABOVE_ANCHOR_STATE_SET and
417 // at least one other drawable, intended for the 'below-anchor state'.
418 if (mBackground instanceof StateListDrawable) {
419 StateListDrawable stateList = (StateListDrawable) mBackground;
420
421 // Find the above-anchor view - this one's easy, it should be labeled as such.
422 int aboveAnchorStateIndex = stateList.getStateDrawableIndex(ABOVE_ANCHOR_STATE_SET);
423
424 // Now, for the below-anchor view, look for any other drawable specified in the
425 // StateListDrawable which is not for the above-anchor state and use that.
426 int count = stateList.getStateCount();
427 int belowAnchorStateIndex = -1;
428 for (int i = 0; i < count; i++) {
429 if (i != aboveAnchorStateIndex) {
430 belowAnchorStateIndex = i;
431 break;
432 }
433 }
434
435 // Store the drawables we found, if we found them. Otherwise, set them both
436 // to null so that we'll just use refreshDrawableState.
437 if (aboveAnchorStateIndex != -1 && belowAnchorStateIndex != -1) {
438 mAboveAnchorBackgroundDrawable = stateList.getStateDrawable(aboveAnchorStateIndex);
439 mBelowAnchorBackgroundDrawable = stateList.getStateDrawable(belowAnchorStateIndex);
440 } else {
441 mBelowAnchorBackgroundDrawable = null;
442 mAboveAnchorBackgroundDrawable = null;
443 }
444 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 }
446
447 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700448 * @return the elevation for this popup window in pixels
449 * @see #setElevation(float)
450 * @attr ref android.R.styleable#PopupWindow_popupElevation
451 */
452 public float getElevation() {
453 return mElevation;
454 }
455
456 /**
457 * Specifies the elevation for this popup window.
458 *
459 * @param elevation the popup's elevation in pixels
460 * @see #getElevation()
461 * @attr ref android.R.styleable#PopupWindow_popupElevation
462 */
463 public void setElevation(float elevation) {
464 mElevation = elevation;
465 }
466
467 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 * <p>Return the animation style to use the popup appears and disappears</p>
469 *
470 * @return the animation style to use the popup appears and disappears
471 */
472 public int getAnimationStyle() {
473 return mAnimationStyle;
474 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700475
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 /**
Shuhrat Dehkanov54ec76d2014-08-28 17:12:23 +0900477 * Set the flag on popup to ignore cheek press events; by default this flag
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 * is set to false
Shuhrat Dehkanov54ec76d2014-08-28 17:12:23 +0900479 * which means the popup will not ignore cheek press dispatch events.
Alan Viverette5435a302015-01-29 10:25:34 -0800480 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481 * <p>If the popup is showing, calling this method will take effect only
482 * the next time the popup is shown or through a manual call to one of
483 * the {@link #update()} methods.</p>
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700484 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 * @see #update()
486 */
487 public void setIgnoreCheekPress() {
488 mIgnoreCheekPress = true;
489 }
Alan Viverette5435a302015-01-29 10:25:34 -0800490
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491
492 /**
493 * <p>Change the animation style resource for this popup.</p>
494 *
495 * <p>If the popup is showing, calling this method will take effect only
496 * the next time the popup is shown or through a manual call to one of
497 * the {@link #update()} methods.</p>
498 *
499 * @param animationStyle animation style to use when the popup appears
500 * and disappears. Set to -1 for the default animation, 0 for no
501 * animation, or a resource identifier for an explicit animation.
Alan Viverette5435a302015-01-29 10:25:34 -0800502 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 * @see #update()
504 */
505 public void setAnimationStyle(int animationStyle) {
506 mAnimationStyle = animationStyle;
507 }
Alan Viverette5435a302015-01-29 10:25:34 -0800508
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 /**
510 * <p>Return the view used as the content of the popup window.</p>
511 *
512 * @return a {@link android.view.View} representing the popup's content
513 *
514 * @see #setContentView(android.view.View)
515 */
516 public View getContentView() {
517 return mContentView;
518 }
519
520 /**
521 * <p>Change the popup's content. The content is represented by an instance
522 * of {@link android.view.View}.</p>
523 *
Gilles Debunne81f08082011-02-17 14:07:19 -0800524 * <p>This method has no effect if called when the popup is showing.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 *
526 * @param contentView the new content for the popup
527 *
528 * @see #getContentView()
529 * @see #isShowing()
530 */
531 public void setContentView(View contentView) {
532 if (isShowing()) {
533 return;
534 }
535
536 mContentView = contentView;
Romain Guy448ecf52009-05-14 16:03:42 -0700537
Romain Guy0c0b7682011-05-16 11:54:09 -0700538 if (mContext == null && mContentView != null) {
Romain Guy448ecf52009-05-14 16:03:42 -0700539 mContext = mContentView.getContext();
540 }
541
Romain Guy0c0b7682011-05-16 11:54:09 -0700542 if (mWindowManager == null && mContentView != null) {
Romain Guy448ecf52009-05-14 16:03:42 -0700543 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
544 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700545
546 // Setting the default for attachedInDecor based on SDK version here
547 // instead of in the constructor since we might not have the context
548 // object in the constructor. We only want to set default here if the
549 // app hasn't already set the attachedInDecor.
550 if (mContext != null && !mAttachedInDecorSet) {
551 // Attach popup window in decor frame of parent window by default for
552 // {@link Build.VERSION_CODES.LOLLIPOP_MR1} or greater. Keep current
553 // behavior of not attaching to decor frame for older SDKs.
554 setAttachedInDecor(mContext.getApplicationInfo().targetSdkVersion
555 >= Build.VERSION_CODES.LOLLIPOP_MR1);
556 }
557
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558 }
559
560 /**
561 * Set a callback for all touch events being dispatched to the popup
562 * window.
563 */
564 public void setTouchInterceptor(OnTouchListener l) {
565 mTouchInterceptor = l;
566 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700567
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568 /**
569 * <p>Indicate whether the popup window can grab the focus.</p>
570 *
571 * @return true if the popup is focusable, false otherwise
572 *
573 * @see #setFocusable(boolean)
574 */
575 public boolean isFocusable() {
576 return mFocusable;
577 }
578
579 /**
580 * <p>Changes the focusability of the popup window. When focusable, the
581 * window will grab the focus from the current focused widget if the popup
582 * contains a focusable {@link android.view.View}. By default a popup
583 * window is not focusable.</p>
584 *
585 * <p>If the popup is showing, calling this method will take effect only
586 * the next time the popup is shown or through a manual call to one of
587 * the {@link #update()} methods.</p>
588 *
589 * @param focusable true if the popup should grab focus, false otherwise.
590 *
591 * @see #isFocusable()
Alan Viverette5435a302015-01-29 10:25:34 -0800592 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800593 * @see #update()
594 */
595 public void setFocusable(boolean focusable) {
596 mFocusable = focusable;
597 }
598
599 /**
600 * Return the current value in {@link #setInputMethodMode(int)}.
Alan Viverette5435a302015-01-29 10:25:34 -0800601 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 * @see #setInputMethodMode(int)
603 */
604 public int getInputMethodMode() {
605 return mInputMethodMode;
Alan Viverette5435a302015-01-29 10:25:34 -0800606
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800607 }
Alan Viverette5435a302015-01-29 10:25:34 -0800608
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 /**
610 * Control how the popup operates with an input method: one of
611 * {@link #INPUT_METHOD_FROM_FOCUSABLE}, {@link #INPUT_METHOD_NEEDED},
612 * or {@link #INPUT_METHOD_NOT_NEEDED}.
Alan Viverette5435a302015-01-29 10:25:34 -0800613 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614 * <p>If the popup is showing, calling this method will take effect only
615 * the next time the popup is shown or through a manual call to one of
616 * the {@link #update()} methods.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800617 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 * @see #getInputMethodMode()
619 * @see #update()
620 */
621 public void setInputMethodMode(int mode) {
622 mInputMethodMode = mode;
623 }
Romain Guy374aaaed32009-07-14 15:11:59 -0700624
625 /**
626 * Sets the operating mode for the soft input area.
627 *
628 * @param mode The desired mode, see
629 * {@link android.view.WindowManager.LayoutParams#softInputMode}
630 * for the full list
631 *
632 * @see android.view.WindowManager.LayoutParams#softInputMode
633 * @see #getSoftInputMode()
634 */
635 public void setSoftInputMode(int mode) {
636 mSoftInputMode = mode;
637 }
638
639 /**
640 * Returns the current value in {@link #setSoftInputMode(int)}.
641 *
642 * @see #setSoftInputMode(int)
643 * @see android.view.WindowManager.LayoutParams#softInputMode
644 */
645 public int getSoftInputMode() {
646 return mSoftInputMode;
647 }
Alan Viverette5435a302015-01-29 10:25:34 -0800648
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800649 /**
650 * <p>Indicates whether the popup window receives touch events.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800651 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800652 * @return true if the popup is touchable, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800653 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800654 * @see #setTouchable(boolean)
655 */
656 public boolean isTouchable() {
657 return mTouchable;
658 }
659
660 /**
661 * <p>Changes the touchability of the popup window. When touchable, the
662 * window will receive touch events, otherwise touch events will go to the
663 * window below it. By default the window is touchable.</p>
664 *
665 * <p>If the popup is showing, calling this method will take effect only
666 * the next time the popup is shown or through a manual call to one of
667 * the {@link #update()} methods.</p>
668 *
669 * @param touchable true if the popup should receive touch events, false otherwise
670 *
671 * @see #isTouchable()
Alan Viverette5435a302015-01-29 10:25:34 -0800672 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 * @see #update()
674 */
675 public void setTouchable(boolean touchable) {
676 mTouchable = touchable;
677 }
678
679 /**
680 * <p>Indicates whether the popup window will be informed of touch events
681 * outside of its window.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800682 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800683 * @return true if the popup is outside touchable, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800684 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800685 * @see #setOutsideTouchable(boolean)
686 */
687 public boolean isOutsideTouchable() {
688 return mOutsideTouchable;
689 }
690
691 /**
692 * <p>Controls whether the pop-up will be informed of touch events outside
693 * of its window. This only makes sense for pop-ups that are touchable
694 * but not focusable, which means touches outside of the window will
695 * be delivered to the window behind. The default is false.</p>
696 *
697 * <p>If the popup is showing, calling this method will take effect only
698 * the next time the popup is shown or through a manual call to one of
699 * the {@link #update()} methods.</p>
700 *
701 * @param touchable true if the popup should receive outside
702 * touch events, false otherwise
703 *
704 * @see #isOutsideTouchable()
Alan Viverette5435a302015-01-29 10:25:34 -0800705 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800706 * @see #update()
707 */
708 public void setOutsideTouchable(boolean touchable) {
709 mOutsideTouchable = touchable;
710 }
711
712 /**
713 * <p>Indicates whether clipping of the popup window is enabled.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800714 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800715 * @return true if the clipping is enabled, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800716 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800717 * @see #setClippingEnabled(boolean)
718 */
719 public boolean isClippingEnabled() {
720 return mClippingEnabled;
721 }
722
723 /**
724 * <p>Allows the popup window to extend beyond the bounds of the screen. By default the
725 * window is clipped to the screen boundaries. Setting this to false will allow windows to be
726 * accurately positioned.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800727 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800728 * <p>If the popup is showing, calling this method will take effect only
729 * the next time the popup is shown or through a manual call to one of
730 * the {@link #update()} methods.</p>
731 *
732 * @param enabled false if the window should be allowed to extend outside of the screen
Alan Viverette5435a302015-01-29 10:25:34 -0800733 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 * @see #isClippingEnabled()
735 * @see #update()
736 */
737 public void setClippingEnabled(boolean enabled) {
738 mClippingEnabled = enabled;
739 }
740
741 /**
Adam Powell56c2d332010-11-05 20:03:03 -0700742 * Clip this popup window to the screen, but not to the containing window.
743 *
744 * @param enabled True to clip to the screen.
745 * @hide
746 */
747 public void setClipToScreenEnabled(boolean enabled) {
748 mClipToScreen = enabled;
749 setClippingEnabled(!enabled);
750 }
Adam Powell348e69c2011-02-16 16:49:50 -0800751
752 /**
753 * Allow PopupWindow to scroll the anchor's parent to provide more room
754 * for the popup. Enabled by default.
755 *
756 * @param enabled True to scroll the anchor's parent when more room is desired by the popup.
757 */
758 void setAllowScrollingAnchorParent(boolean enabled) {
759 mAllowScrollingAnchorParent = enabled;
760 }
Alan Viverette5435a302015-01-29 10:25:34 -0800761
Adam Powell56c2d332010-11-05 20:03:03 -0700762 /**
Jeff Brown01ce2e92010-09-26 22:20:12 -0700763 * <p>Indicates whether the popup window supports splitting touches.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800764 *
Jeff Brown01ce2e92010-09-26 22:20:12 -0700765 * @return true if the touch splitting is enabled, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800766 *
Jeff Brown01ce2e92010-09-26 22:20:12 -0700767 * @see #setSplitTouchEnabled(boolean)
Jeff Brown01ce2e92010-09-26 22:20:12 -0700768 */
769 public boolean isSplitTouchEnabled() {
Jeff Brown46e75292010-11-10 16:53:45 -0800770 if (mSplitTouchEnabled < 0 && mContext != null) {
771 return mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB;
772 }
773 return mSplitTouchEnabled == 1;
Jeff Brown01ce2e92010-09-26 22:20:12 -0700774 }
775
776 /**
777 * <p>Allows the popup window to split touches across other windows that also
Jeff Brown46e75292010-11-10 16:53:45 -0800778 * support split touch. When this flag is false, the first pointer
Jeff Brown01ce2e92010-09-26 22:20:12 -0700779 * that goes down determines the window to which all subsequent touches
Jeff Brown46e75292010-11-10 16:53:45 -0800780 * go until all pointers go up. When this flag is true, each pointer
Jeff Brown01ce2e92010-09-26 22:20:12 -0700781 * (not necessarily the first) that goes down determines the window
782 * to which all subsequent touches of that pointer will go until that
783 * pointer goes up thereby enabling touches with multiple pointers
784 * to be split across multiple windows.</p>
785 *
786 * @param enabled true if the split touches should be enabled, false otherwise
787 * @see #isSplitTouchEnabled()
Jeff Brown01ce2e92010-09-26 22:20:12 -0700788 */
789 public void setSplitTouchEnabled(boolean enabled) {
Jeff Brown46e75292010-11-10 16:53:45 -0800790 mSplitTouchEnabled = enabled ? 1 : 0;
Jeff Brown01ce2e92010-09-26 22:20:12 -0700791 }
792
793 /**
Adam Powellba0a2c32010-09-28 17:41:23 -0700794 * <p>Indicates whether the popup window will be forced into using absolute screen coordinates
795 * for positioning.</p>
796 *
797 * @return true if the window will always be positioned in screen coordinates.
798 * @hide
799 */
800 public boolean isLayoutInScreenEnabled() {
801 return mLayoutInScreen;
802 }
803
804 /**
805 * <p>Allows the popup window to force the flag
806 * {@link WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN}, overriding default behavior.
807 * This will cause the popup to be positioned in absolute screen coordinates.</p>
808 *
809 * @param enabled true if the popup should always be positioned in screen coordinates
810 * @hide
811 */
812 public void setLayoutInScreenEnabled(boolean enabled) {
813 mLayoutInScreen = enabled;
814 }
815
816 /**
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700817 * <p>Indicates whether the popup window will be attached in the decor frame of its parent
818 * window.
819 *
820 * @return true if the window will be attached to the decor frame of its parent window.
821 *
822 * @see #setAttachedInDecor(boolean)
823 * @see WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR
824 */
825 public boolean isAttachedInDecor() {
826 return mAttachedInDecor;
827 }
828
829 /**
830 * <p>This will attach the popup window to the decor frame of the parent window to avoid
831 * overlaping with screen decorations like the navigation bar. Overrides the default behavior of
832 * the flag {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR}.
833 *
834 * <p>By default the flag is set on SDK version {@link Build.VERSION_CODES#LOLLIPOP_MR1} or
835 * greater and cleared on lesser SDK versions.
836 *
837 * @param enabled true if the popup should be attached to the decor frame of its parent window.
838 *
839 * @see WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR
840 */
841 public void setAttachedInDecor(boolean enabled) {
842 mAttachedInDecor = enabled;
843 mAttachedInDecorSet = true;
844 }
845
846 /**
Adam Powell0bd1d0a2011-07-22 19:35:06 -0700847 * Allows the popup window to force the flag
848 * {@link WindowManager.LayoutParams#FLAG_LAYOUT_INSET_DECOR}, overriding default behavior.
849 * This will cause the popup to inset its content to account for system windows overlaying
850 * the screen, such as the status bar.
851 *
852 * <p>This will often be combined with {@link #setLayoutInScreenEnabled(boolean)}.
853 *
854 * @param enabled true if the popup's views should inset content to account for system windows,
855 * the way that decor views behave for full-screen windows.
856 * @hide
857 */
858 public void setLayoutInsetDecor(boolean enabled) {
859 mLayoutInsetDecor = enabled;
860 }
861
862 /**
Chris Banes36344a92015-04-14 10:43:16 +0100863 * Set the layout type for this window. This value will be passed through to
864 * {@link WindowManager.LayoutParams#type} therefore the value should match any value
865 * {@link WindowManager.LayoutParams#type} accepts.
Adam Powell574b37e2010-10-07 11:15:19 -0700866 *
867 * @param layoutType Layout type for this window.
Chris Banes36344a92015-04-14 10:43:16 +0100868 *
869 * @see WindowManager.LayoutParams#type
Adam Powell574b37e2010-10-07 11:15:19 -0700870 */
871 public void setWindowLayoutType(int layoutType) {
872 mWindowLayoutType = layoutType;
873 }
874
875 /**
Chris Banes36344a92015-04-14 10:43:16 +0100876 * Returns the layout type for this window.
877 *
878 * @see #setWindowLayoutType(int)
Adam Powell574b37e2010-10-07 11:15:19 -0700879 */
880 public int getWindowLayoutType() {
881 return mWindowLayoutType;
882 }
883
884 /**
Adam Powelle0b6cd12011-09-28 22:06:11 -0700885 * Set whether this window is touch modal or if outside touches will be sent to
886 * other windows behind it.
887 * @hide
888 */
889 public void setTouchModal(boolean touchModal) {
890 mNotTouchModal = !touchModal;
891 }
892
893 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 * <p>Change the width and height measure specs that are given to the
895 * window manager by the popup. By default these are 0, meaning that
896 * the current width or height is requested as an explicit size from
897 * the window manager. You can supply
Alan Viverette5435a302015-01-29 10:25:34 -0800898 * {@link ViewGroup.LayoutParams#WRAP_CONTENT} or
Romain Guy980a9382010-01-08 15:06:28 -0800899 * {@link ViewGroup.LayoutParams#MATCH_PARENT} to have that measure
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800900 * spec supplied instead, replacing the absolute width and height that
901 * has been set in the popup.</p>
902 *
903 * <p>If the popup is showing, calling this method will take effect only
904 * the next time the popup is shown.</p>
905 *
906 * @param widthSpec an explicit width measure spec mode, either
907 * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
Romain Guy980a9382010-01-08 15:06:28 -0800908 * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800909 * width.
910 * @param heightSpec an explicit height measure spec mode, either
911 * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
Romain Guy980a9382010-01-08 15:06:28 -0800912 * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 * height.
Alan Viverette259c2842015-03-22 17:39:39 -0700914 *
915 * @deprecated Use {@link #setWidth(int)} and {@link #setHeight(int)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 */
Alan Viverette259c2842015-03-22 17:39:39 -0700917 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800918 public void setWindowLayoutMode(int widthSpec, int heightSpec) {
919 mWidthMode = widthSpec;
920 mHeightMode = heightSpec;
921 }
Alan Viverette5435a302015-01-29 10:25:34 -0800922
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800923 /**
Alan Viverette259c2842015-03-22 17:39:39 -0700924 * Returns the popup's height MeasureSpec.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800925 *
926 * @return the height MeasureSpec of the popup
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800927 * @see #setHeight(int)
928 */
929 public int getHeight() {
930 return mHeight;
931 }
932
933 /**
Alan Viverette259c2842015-03-22 17:39:39 -0700934 * Sets the popup's height MeasureSpec.
935 * <p>
936 * If the popup is showing, calling this method will take effect the next
937 * time the popup is shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800938 *
939 * @param height the height MeasureSpec of the popup
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 * @see #getHeight()
Alan Viverette5435a302015-01-29 10:25:34 -0800941 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800942 */
943 public void setHeight(int height) {
944 mHeight = height;
945 }
946
947 /**
Alan Viverette259c2842015-03-22 17:39:39 -0700948 * Returns the popup's width MeasureSpec.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800949 *
950 * @return the width MeasureSpec of the popup
Alan Viverette5435a302015-01-29 10:25:34 -0800951 * @see #setWidth(int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800952 */
953 public int getWidth() {
954 return mWidth;
955 }
956
957 /**
Alan Viverette259c2842015-03-22 17:39:39 -0700958 * Sets the popup's width MeasureSpec.
959 * <p>
960 * If the popup is showing, calling this method will take effect the next
961 * time the popup is shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800962 *
963 * @param width the width MeasureSpec of the popup
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964 * @see #getWidth()
965 * @see #isShowing()
966 */
967 public void setWidth(int width) {
968 mWidth = width;
969 }
970
971 /**
Alan Viverette75d83792015-01-07 15:51:54 -0800972 * Sets whether the popup window should overlap its anchor view when
973 * displayed as a drop-down.
974 * <p>
975 * If the popup is showing, calling this method will take effect only
976 * the next time the popup is shown.
977 *
978 * @param overlapAnchor Whether the popup should overlap its anchor.
979 *
980 * @see #getOverlapAnchor()
981 * @see #isShowing()
982 */
983 public void setOverlapAnchor(boolean overlapAnchor) {
984 mOverlapAnchor = overlapAnchor;
985 }
986
987 /**
988 * Returns whether the popup window should overlap its anchor view when
989 * displayed as a drop-down.
990 *
991 * @return Whether the popup should overlap its anchor.
992 *
993 * @see #setOverlapAnchor(boolean)
994 */
995 public boolean getOverlapAnchor() {
996 return mOverlapAnchor;
997 }
998
999 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001000 * <p>Indicate whether this popup window is showing on screen.</p>
1001 *
1002 * @return true if the popup is showing, false otherwise
1003 */
1004 public boolean isShowing() {
1005 return mIsShowing;
1006 }
1007
1008 /**
1009 * <p>
1010 * Display the content view in a popup window at the specified location. If the popup window
1011 * cannot fit on screen, it will be clipped. See {@link android.view.WindowManager.LayoutParams}
1012 * for more information on how gravity and the x and y parameters are related. Specifying
1013 * a gravity of {@link android.view.Gravity#NO_GRAVITY} is similar to specifying
1014 * <code>Gravity.LEFT | Gravity.TOP</code>.
1015 * </p>
Alan Viverette5435a302015-01-29 10:25:34 -08001016 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017 * @param parent a parent view to get the {@link android.view.View#getWindowToken()} token from
1018 * @param gravity the gravity which controls the placement of the popup window
1019 * @param x the popup's x location offset
1020 * @param y the popup's y location offset
1021 */
1022 public void showAtLocation(View parent, int gravity, int x, int y) {
Adam Powell8ee6d7c2011-09-18 14:59:28 -07001023 showAtLocation(parent.getWindowToken(), gravity, x, y);
1024 }
1025
1026 /**
1027 * Display the content view in a popup window at the specified location.
1028 *
1029 * @param token Window token to use for creating the new window
1030 * @param gravity the gravity which controls the placement of the popup window
1031 * @param x the popup's x location offset
1032 * @param y the popup's y location offset
1033 *
1034 * @hide Internal use only. Applications should use
1035 * {@link #showAtLocation(View, int, int, int)} instead.
1036 */
1037 public void showAtLocation(IBinder token, int gravity, int x, int y) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001038 if (isShowing() || mContentView == null) {
1039 return;
1040 }
1041
Alan Viverettee025ed22015-02-02 11:27:21 -08001042 TransitionManager.endTransitions(mDecorView);
1043
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 unregisterForScrollChanged();
1045
1046 mIsShowing = true;
1047 mIsDropdown = false;
1048
Alan Viverettee025ed22015-02-02 11:27:21 -08001049 final WindowManager.LayoutParams p = createPopupLayoutParams(token);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050 preparePopup(p);
Alan Viverettee025ed22015-02-02 11:27:21 -08001051
1052 // Only override the default if some gravity was specified.
1053 if (gravity != Gravity.NO_GRAVITY) {
1054 p.gravity = gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055 }
Alan Viverettee025ed22015-02-02 11:27:21 -08001056
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057 p.x = x;
1058 p.y = y;
Alan Viverettee025ed22015-02-02 11:27:21 -08001059
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001060 invokePopup(p);
1061 }
1062
1063 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001064 * Display the content view in a popup window anchored to the bottom-left
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001065 * corner of the anchor view. If there is not enough room on screen to show
1066 * the popup in its entirety, this method tries to find a parent scroll
Alan Viverette75d83792015-01-07 15:51:54 -08001067 * view to scroll. If no parent scroll view can be scrolled, the
1068 * bottom-left corner of the popup is pinned at the top left corner of the
1069 * anchor view.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070 *
1071 * @param anchor the view on which to pin the popup window
1072 *
1073 * @see #dismiss()
1074 */
1075 public void showAsDropDown(View anchor) {
1076 showAsDropDown(anchor, 0, 0);
1077 }
1078
1079 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001080 * Display the content view in a popup window anchored to the bottom-left
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001081 * corner of the anchor view offset by the specified x and y coordinates.
Alan Viverette75d83792015-01-07 15:51:54 -08001082 * If there is not enough room on screen to show the popup in its entirety,
1083 * this method tries to find a parent scroll view to scroll. If no parent
1084 * scroll view can be scrolled, the bottom-left corner of the popup is
1085 * pinned at the top left corner of the anchor view.
1086 * <p>
1087 * If the view later scrolls to move <code>anchor</code> to a different
1088 * location, the popup will be moved correspondingly.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001089 *
1090 * @param anchor the view on which to pin the popup window
Adam Powell54c94de2013-09-26 15:36:34 -07001091 * @param xoff A horizontal offset from the anchor in pixels
1092 * @param yoff A vertical offset from the anchor in pixels
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001093 *
1094 * @see #dismiss()
1095 */
1096 public void showAsDropDown(View anchor, int xoff, int yoff) {
Adam Powell54c94de2013-09-26 15:36:34 -07001097 showAsDropDown(anchor, xoff, yoff, DEFAULT_ANCHORED_GRAVITY);
1098 }
1099
1100 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001101 * Displays the content view in a popup window anchored to the corner of
1102 * another view. The window is positioned according to the specified
1103 * gravity and offset by the specified x and y coordinates.
1104 * <p>
1105 * If there is not enough room on screen to show the popup in its entirety,
1106 * this method tries to find a parent scroll view to scroll. If no parent
1107 * view can be scrolled, the specified vertical gravity will be ignored and
1108 * the popup will anchor itself such that it is visible.
1109 * <p>
1110 * If the view later scrolls to move <code>anchor</code> to a different
1111 * location, the popup will be moved correspondingly.
Adam Powell54c94de2013-09-26 15:36:34 -07001112 *
1113 * @param anchor the view on which to pin the popup window
1114 * @param xoff A horizontal offset from the anchor in pixels
1115 * @param yoff A vertical offset from the anchor in pixels
1116 * @param gravity Alignment of the popup relative to the anchor
1117 *
1118 * @see #dismiss()
1119 */
1120 public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001121 if (isShowing() || mContentView == null) {
1122 return;
1123 }
1124
Alan Viverettee025ed22015-02-02 11:27:21 -08001125 TransitionManager.endTransitions(mDecorView);
1126
Adam Powell54c94de2013-09-26 15:36:34 -07001127 registerForScrollChanged(anchor, xoff, yoff, gravity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001128
1129 mIsShowing = true;
1130 mIsDropdown = true;
1131
Alan Viverettee025ed22015-02-02 11:27:21 -08001132 final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001133 preparePopup(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001134
Alan Viverettee025ed22015-02-02 11:27:21 -08001135 final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff, gravity);
1136 updateAboveAnchor(aboveAnchor);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001137
1138 invokePopup(p);
1139 }
1140
Romain Guy3e141682010-03-08 17:44:40 -08001141 private void updateAboveAnchor(boolean aboveAnchor) {
1142 if (aboveAnchor != mAboveAnchor) {
1143 mAboveAnchor = aboveAnchor;
1144
1145 if (mBackground != null) {
1146 // If the background drawable provided was a StateListDrawable with above-anchor
1147 // and below-anchor states, use those. Otherwise rely on refreshDrawableState to
1148 // do the job.
1149 if (mAboveAnchorBackgroundDrawable != null) {
1150 if (mAboveAnchor) {
Alan Viverette5435a302015-01-29 10:25:34 -08001151 mDecorView.setBackground(mAboveAnchorBackgroundDrawable);
Romain Guy3e141682010-03-08 17:44:40 -08001152 } else {
Alan Viverette5435a302015-01-29 10:25:34 -08001153 mDecorView.setBackground(mBelowAnchorBackgroundDrawable);
Romain Guy3e141682010-03-08 17:44:40 -08001154 }
1155 } else {
Alan Viverette5435a302015-01-29 10:25:34 -08001156 mDecorView.refreshDrawableState();
Romain Guy3e141682010-03-08 17:44:40 -08001157 }
1158 }
1159 }
1160 }
1161
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001162 /**
1163 * Indicates whether the popup is showing above (the y coordinate of the popup's bottom
1164 * is less than the y coordinate of the anchor) or below the anchor view (the y coordinate
1165 * of the popup is greater than y coordinate of the anchor's bottom).
1166 *
1167 * The value returned
1168 * by this method is meaningful only after {@link #showAsDropDown(android.view.View)}
1169 * or {@link #showAsDropDown(android.view.View, int, int)} was invoked.
1170 *
1171 * @return True if this popup is showing above the anchor view, false otherwise.
1172 */
1173 public boolean isAboveAnchor() {
1174 return mAboveAnchor;
1175 }
1176
1177 /**
Alan Viverettee025ed22015-02-02 11:27:21 -08001178 * Prepare the popup by embedding it into a new ViewGroup if the background
1179 * drawable is not null. If embedding is required, the layout parameters'
1180 * height is modified to take into account the background's padding.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001181 *
1182 * @param p the layout parameters of the popup's content view
1183 */
1184 private void preparePopup(WindowManager.LayoutParams p) {
Romain Guy448ecf52009-05-14 16:03:42 -07001185 if (mContentView == null || mContext == null || mWindowManager == null) {
1186 throw new IllegalStateException("You must specify a valid content view by "
1187 + "calling setContentView() before attempting to show the popup.");
1188 }
1189
Alan Viverette8fd949e2015-03-11 12:21:30 -07001190 // The old decor view may be transitioning out. Make sure it finishes
1191 // and cleans up before we try to create another one.
1192 if (mDecorView != null) {
1193 mDecorView.cancelTransitions();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001194 }
Alan Viveretteccb11e12014-07-08 16:04:02 -07001195
Alan Viverette8fd949e2015-03-11 12:21:30 -07001196 // When a background is available, we embed the content view within
1197 // another view that owns the background drawable.
1198 final View backgroundView;
1199 if (mBackground != null) {
1200 backgroundView = createBackgroundView(mContentView);
1201 backgroundView.setBackground(mBackground);
1202 } else {
1203 backgroundView = mContentView;
1204 }
1205
1206 mDecorView = createDecorView(backgroundView);
Alan Viverette5435a302015-01-29 10:25:34 -08001207
1208 // The background owner should be elevated so that it casts a shadow.
Alan Viverette8fd949e2015-03-11 12:21:30 -07001209 backgroundView.setElevation(mElevation);
Alan Viverette5435a302015-01-29 10:25:34 -08001210
1211 // We may wrap that in another view, so we'll need to manually specify
1212 // the surface insets.
Alan Viverette8fd949e2015-03-11 12:21:30 -07001213 final int surfaceInset = (int) Math.ceil(backgroundView.getZ() * 2);
Alan Viverette5435a302015-01-29 10:25:34 -08001214 p.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
1215 p.hasManualSurfaceInsets = true;
1216
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001217 mPopupViewInitialLayoutDirectionInherited =
Alan Viverette5435a302015-01-29 10:25:34 -08001218 (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001219 mPopupWidth = p.width;
1220 mPopupHeight = p.height;
1221 }
1222
1223 /**
Alan Viverette5435a302015-01-29 10:25:34 -08001224 * Wraps a content view in a PopupViewContainer.
1225 *
1226 * @param contentView the content view to wrap
1227 * @return a PopupViewContainer that wraps the content view
1228 */
1229 private PopupBackgroundView createBackgroundView(View contentView) {
1230 final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
1231 final int height;
1232 if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
1233 height = ViewGroup.LayoutParams.WRAP_CONTENT;
1234 } else {
1235 height = ViewGroup.LayoutParams.MATCH_PARENT;
1236 }
1237
1238 final PopupBackgroundView backgroundView = new PopupBackgroundView(mContext);
1239 final PopupBackgroundView.LayoutParams listParams = new PopupBackgroundView.LayoutParams(
1240 ViewGroup.LayoutParams.MATCH_PARENT, height);
1241 backgroundView.addView(contentView, listParams);
1242
1243 return backgroundView;
1244 }
1245
1246 /**
1247 * Wraps a content view in a FrameLayout.
1248 *
1249 * @param contentView the content view to wrap
1250 * @return a FrameLayout that wraps the content view
1251 */
1252 private PopupDecorView createDecorView(View contentView) {
1253 final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
1254 final int height;
1255 if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
1256 height = ViewGroup.LayoutParams.WRAP_CONTENT;
1257 } else {
1258 height = ViewGroup.LayoutParams.MATCH_PARENT;
1259 }
1260
1261 final PopupDecorView decorView = new PopupDecorView(mContext);
1262 decorView.addView(contentView, ViewGroup.LayoutParams.MATCH_PARENT, height);
1263 decorView.setClipChildren(false);
1264 decorView.setClipToPadding(false);
1265
1266 return decorView;
1267 }
1268
1269 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001270 * <p>Invoke the popup window by adding the content view to the window
1271 * manager.</p>
1272 *
1273 * <p>The content view must be non-null when this method is invoked.</p>
1274 *
1275 * @param p the layout parameters of the popup's content view
1276 */
1277 private void invokePopup(WindowManager.LayoutParams p) {
Romain Guy0c0b7682011-05-16 11:54:09 -07001278 if (mContext != null) {
1279 p.packageName = mContext.getPackageName();
1280 }
Alan Viverette5435a302015-01-29 10:25:34 -08001281
Alan Viverette8fd949e2015-03-11 12:21:30 -07001282 final PopupDecorView decorView = mDecorView;
1283 decorView.setFitsSystemWindows(mLayoutInsetDecor);
1284 decorView.requestEnterTransition(mEnterTransition);
1285
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001286 setLayoutDirectionFromAnchor();
Alan Viverette5435a302015-01-29 10:25:34 -08001287
Alan Viverette8fd949e2015-03-11 12:21:30 -07001288 mWindowManager.addView(decorView, p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001289 }
1290
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001291 private void setLayoutDirectionFromAnchor() {
1292 if (mAnchor != null) {
1293 View anchor = mAnchor.get();
1294 if (anchor != null && mPopupViewInitialLayoutDirectionInherited) {
Alan Viverette5435a302015-01-29 10:25:34 -08001295 mDecorView.setLayoutDirection(anchor.getLayoutDirection());
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001296 }
1297 }
1298 }
1299
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001300 /**
1301 * <p>Generate the layout parameters for the popup window.</p>
1302 *
1303 * @param token the window token used to bind the popup's window
1304 *
1305 * @return the layout parameters to pass to the window manager
1306 */
Alan Viverettee025ed22015-02-02 11:27:21 -08001307 private WindowManager.LayoutParams createPopupLayoutParams(IBinder token) {
1308 final WindowManager.LayoutParams p = new WindowManager.LayoutParams();
1309
1310 // These gravity settings put the view at the top left corner of the
1311 // screen. The view is then positioned to the appropriate location by
1312 // setting the x and y offsets to match the anchor's bottom-left
1313 // corner.
Fabrice Di Meglioaac0d4e2012-07-19 19:21:26 -07001314 p.gravity = Gravity.START | Gravity.TOP;
Alan Viverettee025ed22015-02-02 11:27:21 -08001315 p.flags = computeFlags(p.flags);
1316 p.type = mWindowLayoutType;
1317 p.token = token;
1318 p.softInputMode = mSoftInputMode;
1319 p.windowAnimations = computeAnimationResource();
1320
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001321 if (mBackground != null) {
1322 p.format = mBackground.getOpacity();
1323 } else {
1324 p.format = PixelFormat.TRANSLUCENT;
1325 }
Alan Viverettee025ed22015-02-02 11:27:21 -08001326
1327 if (mHeightMode < 0) {
1328 p.height = mLastHeight = mHeightMode;
1329 } else {
1330 p.height = mLastHeight = mHeight;
1331 }
1332
1333 if (mWidthMode < 0) {
1334 p.width = mLastWidth = mWidthMode;
1335 } else {
1336 p.width = mLastWidth = mWidth;
1337 }
1338
1339 // Used for debugging.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001340 p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
1341
1342 return p;
1343 }
1344
1345 private int computeFlags(int curFlags) {
1346 curFlags &= ~(
1347 WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES |
1348 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
1349 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
1350 WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
1351 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
Adam Powellba0a2c32010-09-28 17:41:23 -07001352 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
1353 WindowManager.LayoutParams.FLAG_SPLIT_TOUCH);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001354 if(mIgnoreCheekPress) {
1355 curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
1356 }
1357 if (!mFocusable) {
1358 curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1359 if (mInputMethodMode == INPUT_METHOD_NEEDED) {
1360 curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1361 }
1362 } else if (mInputMethodMode == INPUT_METHOD_NOT_NEEDED) {
1363 curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1364 }
1365 if (!mTouchable) {
1366 curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
1367 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001368 if (mOutsideTouchable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001369 curFlags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
1370 }
1371 if (!mClippingEnabled) {
1372 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
1373 }
Jeff Brown46e75292010-11-10 16:53:45 -08001374 if (isSplitTouchEnabled()) {
Jeff Brown01ce2e92010-09-26 22:20:12 -07001375 curFlags |= WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
1376 }
Adam Powellba0a2c32010-09-28 17:41:23 -07001377 if (mLayoutInScreen) {
1378 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
1379 }
Adam Powell0bd1d0a2011-07-22 19:35:06 -07001380 if (mLayoutInsetDecor) {
1381 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
1382 }
Adam Powelle0b6cd12011-09-28 22:06:11 -07001383 if (mNotTouchModal) {
1384 curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
1385 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -07001386 if (mAttachedInDecor) {
1387 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR;
1388 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001389 return curFlags;
1390 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -07001391
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001392 private int computeAnimationResource() {
Alan Viverette5435a302015-01-29 10:25:34 -08001393 if (mAnimationStyle == ANIMATION_STYLE_DEFAULT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001394 if (mIsDropdown) {
1395 return mAboveAnchor
1396 ? com.android.internal.R.style.Animation_DropDownUp
1397 : com.android.internal.R.style.Animation_DropDownDown;
1398 }
1399 return 0;
1400 }
1401 return mAnimationStyle;
1402 }
Alan Viverette560f1702014-05-05 14:40:07 -07001403
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001404 /**
Alan Viverette560f1702014-05-05 14:40:07 -07001405 * Positions the popup window on screen. When the popup window is too tall
1406 * to fit under the anchor, a parent scroll view is seeked and scrolled up
1407 * to reclaim space. If scrolling is not possible or not enough, the popup
1408 * window gets moved on top of the anchor.
1409 * <p>
1410 * The height must have been set on the layout parameters prior to calling
1411 * this method.
Alan Viverette5435a302015-01-29 10:25:34 -08001412 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001413 * @param anchor the view on which the popup window must be anchored
1414 * @param p the layout parameters used to display the drop down
Alan Viverette560f1702014-05-05 14:40:07 -07001415 * @param xoff horizontal offset used to adjust for background padding
1416 * @param yoff vertical offset used to adjust for background padding
1417 * @param gravity horizontal gravity specifying popup alignment
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001418 * @return true if the popup is translated upwards to fit on screen
1419 */
Alan Viverette560f1702014-05-05 14:40:07 -07001420 private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p, int xoff,
1421 int yoff, int gravity) {
Adam Powell62e2bde2011-08-15 15:50:05 -07001422 final int anchorHeight = anchor.getHeight();
Alan Viverette560f1702014-05-05 14:40:07 -07001423 final int anchorWidth = anchor.getWidth();
1424 if (mOverlapAnchor) {
1425 yoff -= anchorHeight;
1426 }
1427
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001428 anchor.getLocationInWindow(mDrawingLocation);
1429 p.x = mDrawingLocation[0] + xoff;
Adam Powell62e2bde2011-08-15 15:50:05 -07001430 p.y = mDrawingLocation[1] + anchorHeight + yoff;
Adam Powell54c94de2013-09-26 15:36:34 -07001431
Alan Viverette560f1702014-05-05 14:40:07 -07001432 final int hgrav = Gravity.getAbsoluteGravity(gravity, anchor.getLayoutDirection())
1433 & Gravity.HORIZONTAL_GRAVITY_MASK;
Adam Powell54c94de2013-09-26 15:36:34 -07001434 if (hgrav == Gravity.RIGHT) {
Alan Viverette560f1702014-05-05 14:40:07 -07001435 // Flip the location to align the right sides of the popup and
1436 // anchor instead of left.
1437 p.x -= mPopupWidth - anchorWidth;
Adam Powell54c94de2013-09-26 15:36:34 -07001438 }
Alan Viverette560f1702014-05-05 14:40:07 -07001439
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001440 boolean onTop = false;
1441
Adam Powell54c94de2013-09-26 15:36:34 -07001442 p.gravity = Gravity.LEFT | Gravity.TOP;
1443
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001444 anchor.getLocationOnScreen(mScreenLocation);
1445 final Rect displayFrame = new Rect();
1446 anchor.getWindowVisibleDisplayFrame(displayFrame);
Adam Powell62e2bde2011-08-15 15:50:05 -07001447
Alan Viverette560f1702014-05-05 14:40:07 -07001448 final int screenY = mScreenLocation[1] + anchorHeight + yoff;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001449 final View root = anchor.getRootView();
Alan Viverette560f1702014-05-05 14:40:07 -07001450 if (screenY + mPopupHeight > displayFrame.bottom
1451 || p.x + mPopupWidth - root.getWidth() > 0) {
1452 // If the drop down disappears at the bottom of the screen, we try
1453 // to scroll a parent scrollview or move the drop down back up on
1454 // top of the edit box.
Adam Powellb7c1b202011-02-17 12:03:09 -08001455 if (mAllowScrollingAnchorParent) {
Alan Viverette560f1702014-05-05 14:40:07 -07001456 final int scrollX = anchor.getScrollX();
1457 final int scrollY = anchor.getScrollY();
1458 final Rect r = new Rect(scrollX, scrollY, scrollX + mPopupWidth + xoff,
1459 scrollY + mPopupHeight + anchorHeight + yoff);
Adam Powellb7c1b202011-02-17 12:03:09 -08001460 anchor.requestRectangleOnScreen(r, true);
1461 }
Romain Guy3e141682010-03-08 17:44:40 -08001462
Alan Viverette560f1702014-05-05 14:40:07 -07001463 // Now we re-evaluate the space available, and decide from that
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001464 // whether the pop-up will go above or below the anchor.
1465 anchor.getLocationInWindow(mDrawingLocation);
1466 p.x = mDrawingLocation[0] + xoff;
Alan Viverette560f1702014-05-05 14:40:07 -07001467 p.y = mDrawingLocation[1] + anchorHeight + yoff;
Adam Powell54c94de2013-09-26 15:36:34 -07001468
Alan Viverette560f1702014-05-05 14:40:07 -07001469 // Preserve the gravity adjustment.
Adam Powell54c94de2013-09-26 15:36:34 -07001470 if (hgrav == Gravity.RIGHT) {
Alan Viverette560f1702014-05-05 14:40:07 -07001471 p.x -= mPopupWidth - anchorWidth;
Adam Powell54c94de2013-09-26 15:36:34 -07001472 }
Alan Viverette560f1702014-05-05 14:40:07 -07001473
1474 // Determine whether there is more space above or below the anchor.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001475 anchor.getLocationOnScreen(mScreenLocation);
Alan Viverette560f1702014-05-05 14:40:07 -07001476 onTop = (displayFrame.bottom - mScreenLocation[1] - anchorHeight - yoff) <
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001477 (mScreenLocation[1] - yoff - displayFrame.top);
1478 if (onTop) {
Adam Powell54c94de2013-09-26 15:36:34 -07001479 p.gravity = Gravity.LEFT | Gravity.BOTTOM;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001480 p.y = root.getHeight() - mDrawingLocation[1] + yoff;
1481 } else {
Alan Viverette560f1702014-05-05 14:40:07 -07001482 p.y = mDrawingLocation[1] + anchorHeight + yoff;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001483 }
1484 }
1485
Adam Powell56c2d332010-11-05 20:03:03 -07001486 if (mClipToScreen) {
1487 final int displayFrameWidth = displayFrame.right - displayFrame.left;
Alan Viverette560f1702014-05-05 14:40:07 -07001488 final int right = p.x + p.width;
Adam Powell56c2d332010-11-05 20:03:03 -07001489 if (right > displayFrameWidth) {
1490 p.x -= right - displayFrameWidth;
1491 }
Alan Viverette560f1702014-05-05 14:40:07 -07001492
Adam Powell56c2d332010-11-05 20:03:03 -07001493 if (p.x < displayFrame.left) {
1494 p.x = displayFrame.left;
1495 p.width = Math.min(p.width, displayFrameWidth);
1496 }
1497
Adam Powell5f83a602011-01-19 17:58:04 -08001498 if (onTop) {
Alan Viverette560f1702014-05-05 14:40:07 -07001499 final int popupTop = mScreenLocation[1] + yoff - mPopupHeight;
Adam Powell5f83a602011-01-19 17:58:04 -08001500 if (popupTop < 0) {
1501 p.y += popupTop;
1502 }
1503 } else {
1504 p.y = Math.max(p.y, displayFrame.top);
1505 }
Adam Powell56c2d332010-11-05 20:03:03 -07001506 }
1507
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001508 p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
Alan Viverette560f1702014-05-05 14:40:07 -07001509
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001510 return onTop;
1511 }
Alan Viverette5435a302015-01-29 10:25:34 -08001512
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001513 /**
1514 * Returns the maximum height that is available for the popup to be
1515 * completely shown. It is recommended that this height be the maximum for
1516 * the popup's height, otherwise it is possible that the popup will be
1517 * clipped.
Alan Viverette5435a302015-01-29 10:25:34 -08001518 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001519 * @param anchor The view on which the popup window must be anchored.
1520 * @return The maximum available height for the popup to be completely
1521 * shown.
1522 */
1523 public int getMaxAvailableHeight(View anchor) {
1524 return getMaxAvailableHeight(anchor, 0);
1525 }
1526
1527 /**
1528 * Returns the maximum height that is available for the popup to be
1529 * completely shown. It is recommended that this height be the maximum for
1530 * the popup's height, otherwise it is possible that the popup will be
1531 * clipped.
1532 *
1533 * @param anchor The view on which the popup window must be anchored.
1534 * @param yOffset y offset from the view's bottom edge
1535 * @return The maximum available height for the popup to be completely
1536 * shown.
1537 */
1538 public int getMaxAvailableHeight(View anchor, int yOffset) {
Mike LeBeau98acd542009-05-07 19:04:39 -07001539 return getMaxAvailableHeight(anchor, yOffset, false);
1540 }
Alan Viverette5435a302015-01-29 10:25:34 -08001541
Mike LeBeau98acd542009-05-07 19:04:39 -07001542 /**
1543 * Returns the maximum height that is available for the popup to be
1544 * completely shown, optionally ignoring any bottom decorations such as
1545 * the input method. 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 *
Mike LeBeau98acd542009-05-07 19:04:39 -07001549 * @param anchor The view on which the popup window must be anchored.
1550 * @param yOffset y offset from the view's bottom edge
1551 * @param ignoreBottomDecorations if true, the height returned will be
1552 * all the way to the bottom of the display, ignoring any
1553 * bottom decorations
1554 * @return The maximum available height for the popup to be completely
1555 * shown.
Alan Viverette5435a302015-01-29 10:25:34 -08001556 *
Mike LeBeau98acd542009-05-07 19:04:39 -07001557 * @hide Pending API council approval.
1558 */
1559 public int getMaxAvailableHeight(View anchor, int yOffset, boolean ignoreBottomDecorations) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001560 final Rect displayFrame = new Rect();
1561 anchor.getWindowVisibleDisplayFrame(displayFrame);
1562
1563 final int[] anchorPos = mDrawingLocation;
1564 anchor.getLocationOnScreen(anchorPos);
Alan Viverette5435a302015-01-29 10:25:34 -08001565
Mike LeBeau98acd542009-05-07 19:04:39 -07001566 int bottomEdge = displayFrame.bottom;
1567 if (ignoreBottomDecorations) {
Adam Powella7287f42010-08-17 21:17:04 -07001568 Resources res = anchor.getContext().getResources();
Adam Powell3f4a7642011-05-20 15:56:25 -07001569 bottomEdge = res.getDisplayMetrics().heightPixels;
Mike LeBeau98acd542009-05-07 19:04:39 -07001570 }
1571 final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001572 final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
1573
1574 // anchorPos[1] is distance from anchor to top of screen
1575 int returnedHeight = Math.max(distanceToBottom, distanceToTop);
1576 if (mBackground != null) {
1577 mBackground.getPadding(mTempRect);
Alan Viverette5435a302015-01-29 10:25:34 -08001578 returnedHeight -= mTempRect.top + mTempRect.bottom;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001579 }
Alan Viverette5435a302015-01-29 10:25:34 -08001580
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001581 return returnedHeight;
1582 }
Alan Viverette5435a302015-01-29 10:25:34 -08001583
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001584 /**
Alan Viverette7878edf2015-02-03 15:49:18 -08001585 * Disposes of the popup window. This method can be invoked only after
1586 * {@link #showAsDropDown(android.view.View)} has been executed. Failing
1587 * that, calling this method will have no effect.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001588 *
Alan Viverette5435a302015-01-29 10:25:34 -08001589 * @see #showAsDropDown(android.view.View)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001590 */
1591 public void dismiss() {
Alan Viverette8fd949e2015-03-11 12:21:30 -07001592 if (!isShowing() || mIsTransitioningToDismiss) {
Alan Viverettee025ed22015-02-02 11:27:21 -08001593 return;
1594 }
Svetoslav Ganov06f938e2011-11-10 14:31:37 -08001595
Alan Viverette8fd949e2015-03-11 12:21:30 -07001596 final PopupDecorView decorView = mDecorView;
1597 final View contentView = mContentView;
1598
1599 final ViewGroup contentHolder;
1600 final ViewParent contentParent = contentView.getParent();
1601 if (contentParent instanceof ViewGroup) {
1602 contentHolder = ((ViewGroup) contentParent);
1603 } else {
1604 contentHolder = null;
1605 }
1606
1607 // Ensure any ongoing or pending transitions are canceled.
1608 decorView.cancelTransitions();
1609
Alan Viverettee025ed22015-02-02 11:27:21 -08001610 unregisterForScrollChanged();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001611
Alan Viverettee025ed22015-02-02 11:27:21 -08001612 mIsShowing = false;
Alan Viverette8fd949e2015-03-11 12:21:30 -07001613 mIsTransitioningToDismiss = true;
Craig Mautnerb82d0742012-05-23 08:48:39 -07001614
Alan Viverette8fd949e2015-03-11 12:21:30 -07001615 if (mExitTransition != null && decorView.isLaidOut()) {
1616 decorView.startExitTransition(mExitTransition, new TransitionListenerAdapter() {
Alan Viverettee025ed22015-02-02 11:27:21 -08001617 @Override
1618 public void onTransitionEnd(Transition transition) {
Alan Viverette8fd949e2015-03-11 12:21:30 -07001619 dismissImmediate(decorView, contentHolder, contentView);
Alan Viverettee025ed22015-02-02 11:27:21 -08001620 }
1621 });
Alan Viverettee025ed22015-02-02 11:27:21 -08001622 } else {
Alan Viverette8fd949e2015-03-11 12:21:30 -07001623 dismissImmediate(decorView, contentHolder, contentView);
Alan Viverette7878edf2015-02-03 15:49:18 -08001624 }
1625
1626 if (mOnDismissListener != null) {
1627 mOnDismissListener.onDismiss();
Alan Viverette5435a302015-01-29 10:25:34 -08001628 }
1629 }
1630
1631 /**
1632 * Removes the popup from the window manager and tears down the supporting
1633 * view hierarchy, if necessary.
1634 */
Alan Viverette8fd949e2015-03-11 12:21:30 -07001635 private void dismissImmediate(View decorView, ViewGroup contentHolder, View contentView) {
1636 // If this method gets called and the decor view doesn't have a parent,
1637 // then it was either never added or was already removed. That should
1638 // never happen, but it's worth checking to avoid potential crashes.
1639 if (decorView.getParent() != null) {
1640 mWindowManager.removeViewImmediate(decorView);
Alan Viverettedf4639a02015-03-02 12:40:34 -08001641 }
1642
Alan Viverette8fd949e2015-03-11 12:21:30 -07001643 if (contentHolder != null) {
1644 contentHolder.removeView(contentView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001645 }
Alan Viverette8fd949e2015-03-11 12:21:30 -07001646
1647 // This needs to stay until after all transitions have ended since we
1648 // need the reference to cancel transitions in preparePopup().
1649 mDecorView = null;
1650 mIsTransitioningToDismiss = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001651 }
1652
1653 /**
1654 * Sets the listener to be called when the window is dismissed.
Alan Viverette5435a302015-01-29 10:25:34 -08001655 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001656 * @param onDismissListener The listener.
1657 */
1658 public void setOnDismissListener(OnDismissListener onDismissListener) {
1659 mOnDismissListener = onDismissListener;
1660 }
Alan Viverette5435a302015-01-29 10:25:34 -08001661
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001662 /**
1663 * Updates the state of the popup window, if it is currently being displayed,
Alan Viverette259c2842015-03-22 17:39:39 -07001664 * from the currently set state.
1665 * <p>
1666 * This includes:
1667 * <ul>
1668 * <li>{@link #setClippingEnabled(boolean)}</li>
1669 * <li>{@link #setFocusable(boolean)}</li>
1670 * <li>{@link #setIgnoreCheekPress()}</li>
1671 * <li>{@link #setInputMethodMode(int)}</li>
1672 * <li>{@link #setTouchable(boolean)}</li>
1673 * <li>{@link #setAnimationStyle(int)}</li>
1674 * </ul>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001675 */
1676 public void update() {
1677 if (!isShowing() || mContentView == null) {
1678 return;
1679 }
Alan Viverette5435a302015-01-29 10:25:34 -08001680
1681 final WindowManager.LayoutParams p =
1682 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
1683
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001684 boolean update = false;
Alan Viverette5435a302015-01-29 10:25:34 -08001685
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001686 final int newAnim = computeAnimationResource();
1687 if (newAnim != p.windowAnimations) {
1688 p.windowAnimations = newAnim;
1689 update = true;
1690 }
1691
1692 final int newFlags = computeFlags(p.flags);
1693 if (newFlags != p.flags) {
1694 p.flags = newFlags;
1695 update = true;
1696 }
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001697
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001698 if (update) {
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001699 setLayoutDirectionFromAnchor();
Alan Viverette5435a302015-01-29 10:25:34 -08001700 mWindowManager.updateViewLayout(mDecorView, p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001701 }
1702 }
Romain Guyd6a463a2009-05-21 23:10:10 -07001703
1704 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001705 * Updates the dimension of the popup window.
1706 * <p>
1707 * Calling this function also updates the window with the current popup
1708 * state as described for {@link #update()}.
Romain Guyd6a463a2009-05-21 23:10:10 -07001709 *
Alan Viverette259c2842015-03-22 17:39:39 -07001710 * @param width the new width, must be >= 0 or -1 to ignore
1711 * @param height the new height, must be >= 0 or -1 to ignore
Romain Guyd6a463a2009-05-21 23:10:10 -07001712 */
1713 public void update(int width, int height) {
Alan Viverette5435a302015-01-29 10:25:34 -08001714 final WindowManager.LayoutParams p =
1715 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
Romain Guyd6a463a2009-05-21 23:10:10 -07001716 update(p.x, p.y, width, height, false);
1717 }
Alan Viverette5435a302015-01-29 10:25:34 -08001718
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001719 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001720 * Updates the position and the dimension of the popup window.
1721 * <p>
1722 * Width and height can be set to -1 to update location only. Calling this
1723 * function also updates the window with the current popup state as
1724 * described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001725 *
1726 * @param x the new x location
1727 * @param y the new y location
Alan Viverette259c2842015-03-22 17:39:39 -07001728 * @param width the new width, must be >= 0 or -1 to ignore
1729 * @param height the new height, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001730 */
1731 public void update(int x, int y, int width, int height) {
1732 update(x, y, width, height, false);
1733 }
1734
1735 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001736 * Updates the position and the dimension of the popup window.
1737 * <p>
1738 * Width and height can be set to -1 to update location only. Calling this
1739 * function also updates the window with the current popup state as
1740 * described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001741 *
1742 * @param x the new x location
1743 * @param y the new y location
Alan Viverette259c2842015-03-22 17:39:39 -07001744 * @param width the new width, must be >= 0 or -1 to ignore
1745 * @param height the new height, must be >= 0 or -1 to ignore
1746 * @param force {@code true} to reposition the window even if the specified
1747 * position already seems to correspond to the LayoutParams,
1748 * {@code false} to only reposition if needed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001749 */
1750 public void update(int x, int y, int width, int height, boolean force) {
Alan Viverette259c2842015-03-22 17:39:39 -07001751 if (width >= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001752 mLastWidth = width;
1753 setWidth(width);
1754 }
1755
Alan Viverette259c2842015-03-22 17:39:39 -07001756 if (height >= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001757 mLastHeight = height;
1758 setHeight(height);
1759 }
1760
1761 if (!isShowing() || mContentView == null) {
1762 return;
1763 }
1764
Alan Viverette5435a302015-01-29 10:25:34 -08001765 final WindowManager.LayoutParams p =
1766 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001767
1768 boolean update = force;
1769
1770 final int finalWidth = mWidthMode < 0 ? mWidthMode : mLastWidth;
1771 if (width != -1 && p.width != finalWidth) {
1772 p.width = mLastWidth = finalWidth;
1773 update = true;
1774 }
1775
1776 final int finalHeight = mHeightMode < 0 ? mHeightMode : mLastHeight;
1777 if (height != -1 && p.height != finalHeight) {
1778 p.height = mLastHeight = finalHeight;
1779 update = true;
1780 }
1781
1782 if (p.x != x) {
1783 p.x = x;
1784 update = true;
1785 }
1786
1787 if (p.y != y) {
1788 p.y = y;
1789 update = true;
1790 }
1791
1792 final int newAnim = computeAnimationResource();
1793 if (newAnim != p.windowAnimations) {
1794 p.windowAnimations = newAnim;
1795 update = true;
1796 }
1797
1798 final int newFlags = computeFlags(p.flags);
1799 if (newFlags != p.flags) {
1800 p.flags = newFlags;
1801 update = true;
1802 }
Mike LeBeau98acd542009-05-07 19:04:39 -07001803
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001804 if (update) {
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001805 setLayoutDirectionFromAnchor();
Alan Viverette5435a302015-01-29 10:25:34 -08001806 mWindowManager.updateViewLayout(mDecorView, p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001807 }
1808 }
1809
1810 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001811 * Updates the position and the dimension of the popup window.
1812 * <p>
1813 * Calling this function also updates the window with the current popup
1814 * state as described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001815 *
1816 * @param anchor the popup's anchor view
Alan Viverette259c2842015-03-22 17:39:39 -07001817 * @param width the new width, must be >= 0 or -1 to ignore
1818 * @param height the new height, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001819 */
1820 public void update(View anchor, int width, int height) {
Alan Viverette75d83792015-01-07 15:51:54 -08001821 update(anchor, false, 0, 0, true, width, height);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001822 }
1823
1824 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001825 * Updates the position and the dimension of the popup window.
1826 * <p>
1827 * Width and height can be set to -1 to update location only. Calling this
1828 * function also updates the window with the current popup state as
1829 * described for {@link #update()}.
1830 * <p>
1831 * If the view later scrolls to move {@code anchor} to a different
1832 * location, the popup will be moved correspondingly.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001833 *
1834 * @param anchor the popup's anchor view
1835 * @param xoff x offset from the view's left edge
1836 * @param yoff y offset from the view's bottom edge
Alan Viverette259c2842015-03-22 17:39:39 -07001837 * @param width the new width, must be >= 0 or -1 to ignore
1838 * @param height the new height, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001839 */
1840 public void update(View anchor, int xoff, int yoff, int width, int height) {
Alan Viverette75d83792015-01-07 15:51:54 -08001841 update(anchor, true, xoff, yoff, true, width, height);
The Android Open Source Project10592532009-03-18 17:39:46 -07001842 }
1843
1844 private void update(View anchor, boolean updateLocation, int xoff, int yoff,
Alan Viverette75d83792015-01-07 15:51:54 -08001845 boolean updateDimension, int width, int height) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001846
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001847 if (!isShowing() || mContentView == null) {
1848 return;
1849 }
1850
Alan Viverette75d83792015-01-07 15:51:54 -08001851 final WeakReference<View> oldAnchor = mAnchor;
1852 final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff);
Gilles Debunne81f08082011-02-17 14:07:19 -08001853 if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) {
Alan Viverette75d83792015-01-07 15:51:54 -08001854 registerForScrollChanged(anchor, xoff, yoff, mAnchoredGravity);
Gilles Debunne81f08082011-02-17 14:07:19 -08001855 } else if (needsUpdate) {
1856 // No need to register again if this is a DropDown, showAsDropDown already did.
1857 mAnchorXoff = xoff;
1858 mAnchorYoff = yoff;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001859 }
1860
The Android Open Source Project10592532009-03-18 17:39:46 -07001861 if (updateDimension) {
1862 if (width == -1) {
1863 width = mPopupWidth;
1864 } else {
1865 mPopupWidth = width;
1866 }
1867 if (height == -1) {
1868 height = mPopupHeight;
1869 } else {
1870 mPopupHeight = height;
1871 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001872 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001873
Alan Viverette75d83792015-01-07 15:51:54 -08001874 final WindowManager.LayoutParams p =
Alan Viverette5435a302015-01-29 10:25:34 -08001875 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
Alan Viverette75d83792015-01-07 15:51:54 -08001876 final int x = p.x;
1877 final int y = p.y;
Romain Guy3e141682010-03-08 17:44:40 -08001878 if (updateLocation) {
Alan Viverette75d83792015-01-07 15:51:54 -08001879 updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff, mAnchoredGravity));
Romain Guy3e141682010-03-08 17:44:40 -08001880 } else {
Adam Powell54c94de2013-09-26 15:36:34 -07001881 updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
1882 mAnchoredGravity));
Romain Guy3e141682010-03-08 17:44:40 -08001883 }
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001884
Romain Guy3e141682010-03-08 17:44:40 -08001885 update(p.x, p.y, width, height, x != p.x || y != p.y);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001886 }
1887
1888 /**
1889 * Listener that is called when this popup window is dismissed.
1890 */
1891 public interface OnDismissListener {
1892 /**
1893 * Called when this popup window is dismissed.
1894 */
1895 public void onDismiss();
1896 }
1897
1898 private void unregisterForScrollChanged() {
Alan Viverettee025ed22015-02-02 11:27:21 -08001899 final WeakReference<View> anchorRef = mAnchor;
1900 final View anchor = anchorRef == null ? null : anchorRef.get();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001901 if (anchor != null) {
Alan Viverettee025ed22015-02-02 11:27:21 -08001902 final ViewTreeObserver vto = anchor.getViewTreeObserver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001903 vto.removeOnScrollChangedListener(mOnScrollChangedListener);
1904 }
Alan Viverettee025ed22015-02-02 11:27:21 -08001905
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001906 mAnchor = null;
1907 }
1908
Adam Powell54c94de2013-09-26 15:36:34 -07001909 private void registerForScrollChanged(View anchor, int xoff, int yoff, int gravity) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001910 unregisterForScrollChanged();
1911
Alan Viverette5435a302015-01-29 10:25:34 -08001912 mAnchor = new WeakReference<>(anchor);
Alan Viverettee025ed22015-02-02 11:27:21 -08001913
1914 final ViewTreeObserver vto = anchor.getViewTreeObserver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001915 if (vto != null) {
1916 vto.addOnScrollChangedListener(mOnScrollChangedListener);
1917 }
1918
1919 mAnchorXoff = xoff;
1920 mAnchorYoff = yoff;
Adam Powell54c94de2013-09-26 15:36:34 -07001921 mAnchoredGravity = gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001922 }
1923
Alan Viverette5435a302015-01-29 10:25:34 -08001924 private class PopupDecorView extends FrameLayout {
Alan Viverette8fd949e2015-03-11 12:21:30 -07001925 private TransitionListenerAdapter mPendingExitListener;
1926
Alan Viverette5435a302015-01-29 10:25:34 -08001927 public PopupDecorView(Context context) {
1928 super(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001929 }
1930
1931 @Override
1932 public boolean dispatchKeyEvent(KeyEvent event) {
1933 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
Per Andersson4ae02b32011-01-17 11:16:23 +01001934 if (getKeyDispatcherState() == null) {
1935 return super.dispatchKeyEvent(event);
1936 }
1937
Alan Viverette5435a302015-01-29 10:25:34 -08001938 if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
1939 final KeyEvent.DispatcherState state = getKeyDispatcherState();
Jeff Brownb3ea9222011-01-10 16:26:36 -08001940 if (state != null) {
1941 state.startTracking(event, this);
1942 }
Dianne Hackborn8d374262009-09-14 21:21:52 -07001943 return true;
Jeff Brownb3ea9222011-01-10 16:26:36 -08001944 } else if (event.getAction() == KeyEvent.ACTION_UP) {
Alan Viverette5435a302015-01-29 10:25:34 -08001945 final KeyEvent.DispatcherState state = getKeyDispatcherState();
Jeff Brownb3ea9222011-01-10 16:26:36 -08001946 if (state != null && state.isTracking(event) && !event.isCanceled()) {
1947 dismiss();
1948 return true;
1949 }
Dianne Hackborn8d374262009-09-14 21:21:52 -07001950 }
1951 return super.dispatchKeyEvent(event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001952 } else {
1953 return super.dispatchKeyEvent(event);
1954 }
1955 }
1956
1957 @Override
1958 public boolean dispatchTouchEvent(MotionEvent ev) {
1959 if (mTouchInterceptor != null && mTouchInterceptor.onTouch(this, ev)) {
1960 return true;
1961 }
1962 return super.dispatchTouchEvent(ev);
1963 }
1964
1965 @Override
1966 public boolean onTouchEvent(MotionEvent event) {
1967 final int x = (int) event.getX();
1968 final int y = (int) event.getY();
Alan Viverette5435a302015-01-29 10:25:34 -08001969
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001970 if ((event.getAction() == MotionEvent.ACTION_DOWN)
1971 && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
1972 dismiss();
1973 return true;
1974 } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
1975 dismiss();
1976 return true;
1977 } else {
1978 return super.onTouchEvent(event);
1979 }
1980 }
Alan Viverette8fd949e2015-03-11 12:21:30 -07001981
1982 /**
1983 * Requests that an enter transition run after the next layout pass.
1984 */
1985 public void requestEnterTransition(Transition transition) {
1986 final ViewTreeObserver observer = getViewTreeObserver();
1987 if (observer != null && transition != null) {
1988 final Transition enterTransition = transition.clone();
1989
1990 // Postpone the enter transition after the first layout pass.
1991 observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
1992 @Override
1993 public void onGlobalLayout() {
1994 final ViewTreeObserver observer = getViewTreeObserver();
1995 if (observer != null) {
1996 observer.removeOnGlobalLayoutListener(this);
1997 }
1998
1999 startEnterTransition(enterTransition);
2000 }
2001 });
2002 }
2003 }
2004
2005 /**
2006 * Starts the pending enter transition, if one is set.
2007 */
2008 private void startEnterTransition(Transition enterTransition) {
2009 final int count = getChildCount();
2010 for (int i = 0; i < count; i++) {
2011 final View child = getChildAt(i);
2012 enterTransition.addTarget(child);
2013 child.setVisibility(View.INVISIBLE);
2014 }
2015
2016 TransitionManager.beginDelayedTransition(this, enterTransition);
2017
2018 for (int i = 0; i < count; i++) {
2019 final View child = getChildAt(i);
2020 child.setVisibility(View.VISIBLE);
2021 }
2022 }
2023
2024 /**
2025 * Starts an exit transition immediately.
2026 * <p>
2027 * <strong>Note:</strong> The transition listener is guaranteed to have
2028 * its {@code onTransitionEnd} method called even if the transition
2029 * never starts; however, it may be called with a {@code null} argument.
2030 */
2031 public void startExitTransition(Transition transition, final TransitionListener listener) {
2032 if (transition == null) {
2033 return;
2034 }
2035
2036 // The exit listener MUST be called for cleanup, even if the
2037 // transition never starts or ends. Stash it for later.
2038 mPendingExitListener = new TransitionListenerAdapter() {
2039 @Override
2040 public void onTransitionEnd(Transition transition) {
2041 listener.onTransitionEnd(transition);
2042
2043 // The listener was called. Our job here is done.
2044 mPendingExitListener = null;
2045 }
2046 };
2047
2048 final Transition exitTransition = transition.clone();
2049 exitTransition.addListener(mPendingExitListener);
2050
2051 final int count = getChildCount();
2052 for (int i = 0; i < count; i++) {
2053 final View child = getChildAt(i);
2054 exitTransition.addTarget(child);
2055 }
2056
2057 TransitionManager.beginDelayedTransition(this, exitTransition);
2058
2059 for (int i = 0; i < count; i++) {
2060 final View child = getChildAt(i);
2061 child.setVisibility(View.INVISIBLE);
2062 }
2063 }
2064
2065 /**
2066 * Cancels all pending or current transitions.
2067 */
2068 public void cancelTransitions() {
2069 TransitionManager.endTransitions(this);
2070
2071 if (mPendingExitListener != null) {
2072 mPendingExitListener.onTransitionEnd(null);
2073 }
2074 }
Alan Viverette5435a302015-01-29 10:25:34 -08002075 }
svetoslavganov75986cf2009-05-14 22:28:01 -07002076
Alan Viverette5435a302015-01-29 10:25:34 -08002077 private class PopupBackgroundView extends FrameLayout {
2078 public PopupBackgroundView(Context context) {
2079 super(context);
2080 }
2081
svetoslavganov75986cf2009-05-14 22:28:01 -07002082 @Override
Alan Viverette5435a302015-01-29 10:25:34 -08002083 protected int[] onCreateDrawableState(int extraSpace) {
2084 if (mAboveAnchor) {
2085 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
2086 View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET);
2087 return drawableState;
svetoslavganov75986cf2009-05-14 22:28:01 -07002088 } else {
Alan Viverette5435a302015-01-29 10:25:34 -08002089 return super.onCreateDrawableState(extraSpace);
svetoslavganov75986cf2009-05-14 22:28:01 -07002090 }
2091 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002092 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002093}