blob: c3ac278760f8da63a459db864dbfe3be45fbfe6d [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();
Alan Viverette5435a302015-01-29 10:25:34 -080097
Romain Guy448ecf52009-05-14 16:03:42 -070098 private Context mContext;
99 private WindowManager mWindowManager;
Alan Viverette5435a302015-01-29 10:25:34 -0800100
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 private boolean mIsShowing;
Alan Viverette8fd949e2015-03-11 12:21:30 -0700102 private boolean mIsTransitioningToDismiss;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 private boolean mIsDropdown;
104
Alan Viverette5435a302015-01-29 10:25:34 -0800105 /** View that handles event dispatch and content transitions. */
106 private PopupDecorView mDecorView;
107
Alan Viverette5435a302015-01-29 10:25:34 -0800108 /** The contents of the popup. */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 private View mContentView;
Alan Viverette5435a302015-01-29 10:25:34 -0800110
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 private boolean mFocusable;
112 private int mInputMethodMode = INPUT_METHOD_FROM_FOCUSABLE;
Dianne Hackborn7eab0942011-01-01 13:21:50 -0800113 private int mSoftInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 private boolean mTouchable = true;
115 private boolean mOutsideTouchable = false;
116 private boolean mClippingEnabled = true;
Jeff Brown46e75292010-11-10 16:53:45 -0800117 private int mSplitTouchEnabled = -1;
Adam Powellba0a2c32010-09-28 17:41:23 -0700118 private boolean mLayoutInScreen;
Adam Powell56c2d332010-11-05 20:03:03 -0700119 private boolean mClipToScreen;
Adam Powell348e69c2011-02-16 16:49:50 -0800120 private boolean mAllowScrollingAnchorParent = true;
Adam Powell0bd1d0a2011-07-22 19:35:06 -0700121 private boolean mLayoutInsetDecor = false;
Adam Powelle0b6cd12011-09-28 22:06:11 -0700122 private boolean mNotTouchModal;
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700123 private boolean mAttachedInDecor = true;
124 private boolean mAttachedInDecorSet = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125
126 private OnTouchListener mTouchInterceptor;
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700127
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 private int mWidthMode;
Alan Viverette259c2842015-03-22 17:39:39 -0700129 private int mWidth = LayoutParams.WRAP_CONTENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 private int mLastWidth;
131 private int mHeightMode;
Alan Viverette259c2842015-03-22 17:39:39 -0700132 private int mHeight = LayoutParams.WRAP_CONTENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 private int mLastHeight;
134
135 private int mPopupWidth;
136 private int mPopupHeight;
Adam Powell56c2d332010-11-05 20:03:03 -0700137
Alan Viveretteccb11e12014-07-08 16:04:02 -0700138 private float mElevation;
139
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 private Drawable mBackground;
141 private Drawable mAboveAnchorBackgroundDrawable;
142 private Drawable mBelowAnchorBackgroundDrawable;
143
Alan Viverette5435a302015-01-29 10:25:34 -0800144 private Transition mEnterTransition;
145 private Transition mExitTransition;
Alan Viverette560f1702014-05-05 14:40:07 -0700146
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 private boolean mAboveAnchor;
Adam Powell574b37e2010-10-07 11:15:19 -0700148 private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
Alan Viverette5435a302015-01-29 10:25:34 -0800149
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 private OnDismissListener mOnDismissListener;
151 private boolean mIgnoreCheekPress = false;
152
Alan Viverette5435a302015-01-29 10:25:34 -0800153 private int mAnimationStyle = ANIMATION_STYLE_DEFAULT;
154
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 private static final int[] ABOVE_ANCHOR_STATE_SET = new int[] {
156 com.android.internal.R.attr.state_above_anchor
157 };
158
159 private WeakReference<View> mAnchor;
Alan Viverette560f1702014-05-05 14:40:07 -0700160
Alan Viverette5435a302015-01-29 10:25:34 -0800161 private final OnScrollChangedListener mOnScrollChangedListener = new OnScrollChangedListener() {
162 @Override
163 public void onScrollChanged() {
164 final View anchor = mAnchor != null ? mAnchor.get() : null;
165 if (anchor != null && mDecorView != null) {
166 final WindowManager.LayoutParams p = (WindowManager.LayoutParams)
167 mDecorView.getLayoutParams();
168
169 updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
170 mAnchoredGravity));
171 update(p.x, p.y, -1, -1, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 }
Alan Viverette5435a302015-01-29 10:25:34 -0800173 }
174 };
Alan Viverette560f1702014-05-05 14:40:07 -0700175
Alan Viverette5435a302015-01-29 10:25:34 -0800176 private int mAnchorXoff;
177 private int mAnchorYoff;
178 private int mAnchoredGravity;
Alan Viverette560f1702014-05-05 14:40:07 -0700179 private boolean mOverlapAnchor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180
Fabrice Di Megliob003e282012-10-17 17:20:19 -0700181 private boolean mPopupViewInitialLayoutDirectionInherited;
182
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 /**
184 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
185 *
186 * <p>The popup does provide a background.</p>
187 */
188 public PopupWindow(Context context) {
189 this(context, null);
190 }
191
192 /**
193 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
194 *
195 * <p>The popup does provide a background.</p>
196 */
197 public PopupWindow(Context context, AttributeSet attrs) {
198 this(context, attrs, com.android.internal.R.attr.popupWindowStyle);
199 }
200
201 /**
202 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
203 *
204 * <p>The popup does provide a background.</p>
205 */
Alan Viverette617feb92013-09-09 18:09:13 -0700206 public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
207 this(context, attrs, defStyleAttr, 0);
Adam Powellc3fa6302010-05-18 11:36:27 -0700208 }
Alan Viverette5435a302015-01-29 10:25:34 -0800209
Adam Powellc3fa6302010-05-18 11:36:27 -0700210 /**
211 * <p>Create a new, empty, non focusable popup window of dimension (0,0).</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800212 *
Adam Powellc3fa6302010-05-18 11:36:27 -0700213 * <p>The popup does not provide a background.</p>
214 */
215 public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 mContext = context;
Alan Viverette75d83792015-01-07 15:51:54 -0800217 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218
Alan Viverette617feb92013-09-09 18:09:13 -0700219 final TypedArray a = context.obtainStyledAttributes(
Alan Viverettece8c3582014-11-07 13:19:38 -0800220 attrs, R.styleable.PopupWindow, defStyleAttr, defStyleRes);
221 final Drawable bg = a.getDrawable(R.styleable.PopupWindow_popupBackground);
Alan Viveretteccb11e12014-07-08 16:04:02 -0700222 mElevation = a.getDimension(R.styleable.PopupWindow_popupElevation, 0);
Alan Viverette560f1702014-05-05 14:40:07 -0700223 mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false);
Adam Powellc3808b52010-10-04 10:06:59 -0700224
Alan Viverette5435a302015-01-29 10:25:34 -0800225 // Preserve default behavior from Gingerbread. If the animation is
226 // undefined or explicitly specifies the Gingerbread animation style,
227 // use a sentinel value.
228 if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupAnimationStyle)) {
229 final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, 0);
230 if (animStyle == R.style.Animation_PopupWindow) {
231 mAnimationStyle = ANIMATION_STYLE_DEFAULT;
232 } else {
233 mAnimationStyle = animStyle;
234 }
235 } else {
236 mAnimationStyle = ANIMATION_STYLE_DEFAULT;
237 }
238
239 final Transition enterTransition = getTransition(a.getResourceId(
240 R.styleable.PopupWindow_popupEnterTransition, 0));
241 final Transition exitTransition;
242 if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupExitTransition)) {
243 exitTransition = getTransition(a.getResourceId(
244 R.styleable.PopupWindow_popupExitTransition, 0));
245 } else {
246 exitTransition = enterTransition == null ? null : enterTransition.clone();
247 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 a.recycle();
Alan Viverettece8c3582014-11-07 13:19:38 -0800250
Alan Viverette5435a302015-01-29 10:25:34 -0800251 setEnterTransition(enterTransition);
252 setExitTransition(exitTransition);
Alan Viverettece8c3582014-11-07 13:19:38 -0800253 setBackgroundDrawable(bg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 }
255
256 /**
257 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
258 *
259 * <p>The popup does not provide any background. This should be handled
260 * by the content view.</p>
261 */
262 public PopupWindow() {
263 this(null, 0, 0);
264 }
265
266 /**
267 * <p>Create a new non focusable popup window which can display the
268 * <tt>contentView</tt>. The dimension of the window are (0,0).</p>
269 *
270 * <p>The popup does not provide any background. This should be handled
271 * by the content view.</p>
272 *
273 * @param contentView the popup's content
274 */
275 public PopupWindow(View contentView) {
276 this(contentView, 0, 0);
277 }
278
279 /**
280 * <p>Create a new empty, non focusable popup window. The dimension of the
281 * window must be passed to this constructor.</p>
282 *
283 * <p>The popup does not provide any background. This should be handled
284 * by the content view.</p>
285 *
286 * @param width the popup's width
287 * @param height the popup's height
288 */
289 public PopupWindow(int width, int height) {
290 this(null, width, height);
291 }
292
293 /**
294 * <p>Create a new non focusable popup window which can display the
295 * <tt>contentView</tt>. The dimension of the window must be passed to
296 * this constructor.</p>
297 *
298 * <p>The popup does not provide any background. This should be handled
299 * by the content view.</p>
300 *
301 * @param contentView the popup's content
302 * @param width the popup's width
303 * @param height the popup's height
304 */
305 public PopupWindow(View contentView, int width, int height) {
306 this(contentView, width, height, false);
307 }
308
309 /**
310 * <p>Create a new popup window which can display the <tt>contentView</tt>.
311 * The dimension of the window must be passed to this constructor.</p>
312 *
313 * <p>The popup does not provide any background. This should be handled
314 * by the content view.</p>
315 *
316 * @param contentView the popup's content
317 * @param width the popup's width
318 * @param height the popup's height
319 * @param focusable true if the popup can be focused, false otherwise
320 */
Romain Guy448ecf52009-05-14 16:03:42 -0700321 public PopupWindow(View contentView, int width, int height, boolean focusable) {
322 if (contentView != null) {
323 mContext = contentView.getContext();
324 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
325 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700326
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 setContentView(contentView);
328 setWidth(width);
329 setHeight(height);
330 setFocusable(focusable);
331 }
332
Alan Viverette5435a302015-01-29 10:25:34 -0800333 public void setEnterTransition(Transition enterTransition) {
334 mEnterTransition = enterTransition;
Alan Viverette5435a302015-01-29 10:25:34 -0800335 }
336
337 public void setExitTransition(Transition exitTransition) {
338 mExitTransition = exitTransition;
Alan Viverette5435a302015-01-29 10:25:34 -0800339 }
340
341 private Transition getTransition(int resId) {
342 if (resId != 0 && resId != R.transition.no_transition) {
343 final TransitionInflater inflater = TransitionInflater.from(mContext);
344 final Transition transition = inflater.inflateTransition(resId);
345 if (transition != null) {
346 final boolean isEmpty = transition instanceof TransitionSet
347 && ((TransitionSet) transition).getTransitionCount() == 0;
348 if (!isEmpty) {
349 return transition;
350 }
351 }
352 }
353 return null;
354 }
355
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700357 * Return the drawable used as the popup window's background.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 *
Alan Viveretteccb11e12014-07-08 16:04:02 -0700359 * @return the background drawable or {@code null} if not set
360 * @see #setBackgroundDrawable(Drawable)
361 * @attr ref android.R.styleable#PopupWindow_popupBackground
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 */
363 public Drawable getBackground() {
364 return mBackground;
365 }
366
367 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700368 * Specifies the background drawable for this popup window. The background
369 * can be set to {@code null}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370 *
371 * @param background the popup's background
Alan Viveretteccb11e12014-07-08 16:04:02 -0700372 * @see #getBackground()
373 * @attr ref android.R.styleable#PopupWindow_popupBackground
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 */
375 public void setBackgroundDrawable(Drawable background) {
376 mBackground = background;
Alan Viverettece8c3582014-11-07 13:19:38 -0800377
378 // If this is a StateListDrawable, try to find and store the drawable to be
379 // used when the drop-down is placed above its anchor view, and the one to be
380 // used when the drop-down is placed below its anchor view. We extract
381 // the drawables ourselves to work around a problem with using refreshDrawableState
382 // that it will take into account the padding of all drawables specified in a
383 // StateListDrawable, thus adding superfluous padding to drop-down views.
384 //
385 // We assume a StateListDrawable will have a drawable for ABOVE_ANCHOR_STATE_SET and
386 // at least one other drawable, intended for the 'below-anchor state'.
387 if (mBackground instanceof StateListDrawable) {
388 StateListDrawable stateList = (StateListDrawable) mBackground;
389
390 // Find the above-anchor view - this one's easy, it should be labeled as such.
391 int aboveAnchorStateIndex = stateList.getStateDrawableIndex(ABOVE_ANCHOR_STATE_SET);
392
393 // Now, for the below-anchor view, look for any other drawable specified in the
394 // StateListDrawable which is not for the above-anchor state and use that.
395 int count = stateList.getStateCount();
396 int belowAnchorStateIndex = -1;
397 for (int i = 0; i < count; i++) {
398 if (i != aboveAnchorStateIndex) {
399 belowAnchorStateIndex = i;
400 break;
401 }
402 }
403
404 // Store the drawables we found, if we found them. Otherwise, set them both
405 // to null so that we'll just use refreshDrawableState.
406 if (aboveAnchorStateIndex != -1 && belowAnchorStateIndex != -1) {
407 mAboveAnchorBackgroundDrawable = stateList.getStateDrawable(aboveAnchorStateIndex);
408 mBelowAnchorBackgroundDrawable = stateList.getStateDrawable(belowAnchorStateIndex);
409 } else {
410 mBelowAnchorBackgroundDrawable = null;
411 mAboveAnchorBackgroundDrawable = null;
412 }
413 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 }
415
416 /**
Alan Viveretteccb11e12014-07-08 16:04:02 -0700417 * @return the elevation for this popup window in pixels
418 * @see #setElevation(float)
419 * @attr ref android.R.styleable#PopupWindow_popupElevation
420 */
421 public float getElevation() {
422 return mElevation;
423 }
424
425 /**
426 * Specifies the elevation for this popup window.
427 *
428 * @param elevation the popup's elevation in pixels
429 * @see #getElevation()
430 * @attr ref android.R.styleable#PopupWindow_popupElevation
431 */
432 public void setElevation(float elevation) {
433 mElevation = elevation;
434 }
435
436 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437 * <p>Return the animation style to use the popup appears and disappears</p>
438 *
439 * @return the animation style to use the popup appears and disappears
440 */
441 public int getAnimationStyle() {
442 return mAnimationStyle;
443 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700444
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 /**
Shuhrat Dehkanov54ec76d2014-08-28 17:12:23 +0900446 * Set the flag on popup to ignore cheek press events; by default this flag
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 * is set to false
Shuhrat Dehkanov54ec76d2014-08-28 17:12:23 +0900448 * which means the popup will not ignore cheek press dispatch events.
Alan Viverette5435a302015-01-29 10:25:34 -0800449 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 * <p>If the popup is showing, calling this method will take effect only
451 * the next time the popup is shown or through a manual call to one of
452 * the {@link #update()} methods.</p>
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700453 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 * @see #update()
455 */
456 public void setIgnoreCheekPress() {
457 mIgnoreCheekPress = true;
458 }
Alan Viverette5435a302015-01-29 10:25:34 -0800459
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460
461 /**
462 * <p>Change the animation style resource for this popup.</p>
463 *
464 * <p>If the popup is showing, calling this method will take effect only
465 * the next time the popup is shown or through a manual call to one of
466 * the {@link #update()} methods.</p>
467 *
468 * @param animationStyle animation style to use when the popup appears
469 * and disappears. Set to -1 for the default animation, 0 for no
470 * animation, or a resource identifier for an explicit animation.
Alan Viverette5435a302015-01-29 10:25:34 -0800471 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 * @see #update()
473 */
474 public void setAnimationStyle(int animationStyle) {
475 mAnimationStyle = animationStyle;
476 }
Alan Viverette5435a302015-01-29 10:25:34 -0800477
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 /**
479 * <p>Return the view used as the content of the popup window.</p>
480 *
481 * @return a {@link android.view.View} representing the popup's content
482 *
483 * @see #setContentView(android.view.View)
484 */
485 public View getContentView() {
486 return mContentView;
487 }
488
489 /**
490 * <p>Change the popup's content. The content is represented by an instance
491 * of {@link android.view.View}.</p>
492 *
Gilles Debunne81f08082011-02-17 14:07:19 -0800493 * <p>This method has no effect if called when the popup is showing.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 *
495 * @param contentView the new content for the popup
496 *
497 * @see #getContentView()
498 * @see #isShowing()
499 */
500 public void setContentView(View contentView) {
501 if (isShowing()) {
502 return;
503 }
504
505 mContentView = contentView;
Romain Guy448ecf52009-05-14 16:03:42 -0700506
Romain Guy0c0b7682011-05-16 11:54:09 -0700507 if (mContext == null && mContentView != null) {
Romain Guy448ecf52009-05-14 16:03:42 -0700508 mContext = mContentView.getContext();
509 }
510
Romain Guy0c0b7682011-05-16 11:54:09 -0700511 if (mWindowManager == null && mContentView != null) {
Romain Guy448ecf52009-05-14 16:03:42 -0700512 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
513 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700514
515 // Setting the default for attachedInDecor based on SDK version here
516 // instead of in the constructor since we might not have the context
517 // object in the constructor. We only want to set default here if the
518 // app hasn't already set the attachedInDecor.
519 if (mContext != null && !mAttachedInDecorSet) {
520 // Attach popup window in decor frame of parent window by default for
521 // {@link Build.VERSION_CODES.LOLLIPOP_MR1} or greater. Keep current
522 // behavior of not attaching to decor frame for older SDKs.
523 setAttachedInDecor(mContext.getApplicationInfo().targetSdkVersion
524 >= Build.VERSION_CODES.LOLLIPOP_MR1);
525 }
526
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 }
528
529 /**
530 * Set a callback for all touch events being dispatched to the popup
531 * window.
532 */
533 public void setTouchInterceptor(OnTouchListener l) {
534 mTouchInterceptor = l;
535 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700536
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537 /**
538 * <p>Indicate whether the popup window can grab the focus.</p>
539 *
540 * @return true if the popup is focusable, false otherwise
541 *
542 * @see #setFocusable(boolean)
543 */
544 public boolean isFocusable() {
545 return mFocusable;
546 }
547
548 /**
549 * <p>Changes the focusability of the popup window. When focusable, the
550 * window will grab the focus from the current focused widget if the popup
551 * contains a focusable {@link android.view.View}. By default a popup
552 * window is not focusable.</p>
553 *
554 * <p>If the popup is showing, calling this method will take effect only
555 * the next time the popup is shown or through a manual call to one of
556 * the {@link #update()} methods.</p>
557 *
558 * @param focusable true if the popup should grab focus, false otherwise.
559 *
560 * @see #isFocusable()
Alan Viverette5435a302015-01-29 10:25:34 -0800561 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562 * @see #update()
563 */
564 public void setFocusable(boolean focusable) {
565 mFocusable = focusable;
566 }
567
568 /**
569 * Return the current value in {@link #setInputMethodMode(int)}.
Alan Viverette5435a302015-01-29 10:25:34 -0800570 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 * @see #setInputMethodMode(int)
572 */
573 public int getInputMethodMode() {
574 return mInputMethodMode;
Alan Viverette5435a302015-01-29 10:25:34 -0800575
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800576 }
Alan Viverette5435a302015-01-29 10:25:34 -0800577
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 /**
579 * Control how the popup operates with an input method: one of
580 * {@link #INPUT_METHOD_FROM_FOCUSABLE}, {@link #INPUT_METHOD_NEEDED},
581 * or {@link #INPUT_METHOD_NOT_NEEDED}.
Alan Viverette5435a302015-01-29 10:25:34 -0800582 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800583 * <p>If the popup is showing, calling this method will take effect only
584 * the next time the popup is shown or through a manual call to one of
585 * the {@link #update()} methods.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800586 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 * @see #getInputMethodMode()
588 * @see #update()
589 */
590 public void setInputMethodMode(int mode) {
591 mInputMethodMode = mode;
592 }
Romain Guy374aaaed32009-07-14 15:11:59 -0700593
594 /**
595 * Sets the operating mode for the soft input area.
596 *
597 * @param mode The desired mode, see
598 * {@link android.view.WindowManager.LayoutParams#softInputMode}
599 * for the full list
600 *
601 * @see android.view.WindowManager.LayoutParams#softInputMode
602 * @see #getSoftInputMode()
603 */
604 public void setSoftInputMode(int mode) {
605 mSoftInputMode = mode;
606 }
607
608 /**
609 * Returns the current value in {@link #setSoftInputMode(int)}.
610 *
611 * @see #setSoftInputMode(int)
612 * @see android.view.WindowManager.LayoutParams#softInputMode
613 */
614 public int getSoftInputMode() {
615 return mSoftInputMode;
616 }
Alan Viverette5435a302015-01-29 10:25:34 -0800617
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 /**
619 * <p>Indicates whether the popup window receives touch events.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800620 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621 * @return true if the popup is touchable, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800622 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800623 * @see #setTouchable(boolean)
624 */
625 public boolean isTouchable() {
626 return mTouchable;
627 }
628
629 /**
630 * <p>Changes the touchability of the popup window. When touchable, the
631 * window will receive touch events, otherwise touch events will go to the
632 * window below it. By default the window is touchable.</p>
633 *
634 * <p>If the popup is showing, calling this method will take effect only
635 * the next time the popup is shown or through a manual call to one of
636 * the {@link #update()} methods.</p>
637 *
638 * @param touchable true if the popup should receive touch events, false otherwise
639 *
640 * @see #isTouchable()
Alan Viverette5435a302015-01-29 10:25:34 -0800641 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800642 * @see #update()
643 */
644 public void setTouchable(boolean touchable) {
645 mTouchable = touchable;
646 }
647
648 /**
649 * <p>Indicates whether the popup window will be informed of touch events
650 * outside of its window.</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 outside touchable, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800653 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800654 * @see #setOutsideTouchable(boolean)
655 */
656 public boolean isOutsideTouchable() {
657 return mOutsideTouchable;
658 }
659
660 /**
661 * <p>Controls whether the pop-up will be informed of touch events outside
662 * of its window. This only makes sense for pop-ups that are touchable
663 * but not focusable, which means touches outside of the window will
664 * be delivered to the window behind. The default is false.</p>
665 *
666 * <p>If the popup is showing, calling this method will take effect only
667 * the next time the popup is shown or through a manual call to one of
668 * the {@link #update()} methods.</p>
669 *
670 * @param touchable true if the popup should receive outside
671 * touch events, false otherwise
672 *
673 * @see #isOutsideTouchable()
Alan Viverette5435a302015-01-29 10:25:34 -0800674 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675 * @see #update()
676 */
677 public void setOutsideTouchable(boolean touchable) {
678 mOutsideTouchable = touchable;
679 }
680
681 /**
682 * <p>Indicates whether clipping of the popup window is enabled.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800683 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800684 * @return true if the clipping is enabled, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800685 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686 * @see #setClippingEnabled(boolean)
687 */
688 public boolean isClippingEnabled() {
689 return mClippingEnabled;
690 }
691
692 /**
693 * <p>Allows the popup window to extend beyond the bounds of the screen. By default the
694 * window is clipped to the screen boundaries. Setting this to false will allow windows to be
695 * accurately positioned.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800696 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800697 * <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 enabled false if the window should be allowed to extend outside of the screen
Alan Viverette5435a302015-01-29 10:25:34 -0800702 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800703 * @see #isClippingEnabled()
704 * @see #update()
705 */
706 public void setClippingEnabled(boolean enabled) {
707 mClippingEnabled = enabled;
708 }
709
710 /**
Adam Powell56c2d332010-11-05 20:03:03 -0700711 * Clip this popup window to the screen, but not to the containing window.
712 *
713 * @param enabled True to clip to the screen.
714 * @hide
715 */
716 public void setClipToScreenEnabled(boolean enabled) {
717 mClipToScreen = enabled;
718 setClippingEnabled(!enabled);
719 }
Adam Powell348e69c2011-02-16 16:49:50 -0800720
721 /**
722 * Allow PopupWindow to scroll the anchor's parent to provide more room
723 * for the popup. Enabled by default.
724 *
725 * @param enabled True to scroll the anchor's parent when more room is desired by the popup.
726 */
727 void setAllowScrollingAnchorParent(boolean enabled) {
728 mAllowScrollingAnchorParent = enabled;
729 }
Alan Viverette5435a302015-01-29 10:25:34 -0800730
Adam Powell56c2d332010-11-05 20:03:03 -0700731 /**
Jeff Brown01ce2e92010-09-26 22:20:12 -0700732 * <p>Indicates whether the popup window supports splitting touches.</p>
Alan Viverette5435a302015-01-29 10:25:34 -0800733 *
Jeff Brown01ce2e92010-09-26 22:20:12 -0700734 * @return true if the touch splitting is enabled, false otherwise
Alan Viverette5435a302015-01-29 10:25:34 -0800735 *
Jeff Brown01ce2e92010-09-26 22:20:12 -0700736 * @see #setSplitTouchEnabled(boolean)
Jeff Brown01ce2e92010-09-26 22:20:12 -0700737 */
738 public boolean isSplitTouchEnabled() {
Jeff Brown46e75292010-11-10 16:53:45 -0800739 if (mSplitTouchEnabled < 0 && mContext != null) {
740 return mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB;
741 }
742 return mSplitTouchEnabled == 1;
Jeff Brown01ce2e92010-09-26 22:20:12 -0700743 }
744
745 /**
746 * <p>Allows the popup window to split touches across other windows that also
Jeff Brown46e75292010-11-10 16:53:45 -0800747 * support split touch. When this flag is false, the first pointer
Jeff Brown01ce2e92010-09-26 22:20:12 -0700748 * that goes down determines the window to which all subsequent touches
Jeff Brown46e75292010-11-10 16:53:45 -0800749 * go until all pointers go up. When this flag is true, each pointer
Jeff Brown01ce2e92010-09-26 22:20:12 -0700750 * (not necessarily the first) that goes down determines the window
751 * to which all subsequent touches of that pointer will go until that
752 * pointer goes up thereby enabling touches with multiple pointers
753 * to be split across multiple windows.</p>
754 *
755 * @param enabled true if the split touches should be enabled, false otherwise
756 * @see #isSplitTouchEnabled()
Jeff Brown01ce2e92010-09-26 22:20:12 -0700757 */
758 public void setSplitTouchEnabled(boolean enabled) {
Jeff Brown46e75292010-11-10 16:53:45 -0800759 mSplitTouchEnabled = enabled ? 1 : 0;
Jeff Brown01ce2e92010-09-26 22:20:12 -0700760 }
761
762 /**
Adam Powellba0a2c32010-09-28 17:41:23 -0700763 * <p>Indicates whether the popup window will be forced into using absolute screen coordinates
764 * for positioning.</p>
765 *
766 * @return true if the window will always be positioned in screen coordinates.
767 * @hide
768 */
769 public boolean isLayoutInScreenEnabled() {
770 return mLayoutInScreen;
771 }
772
773 /**
774 * <p>Allows the popup window to force the flag
775 * {@link WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN}, overriding default behavior.
776 * This will cause the popup to be positioned in absolute screen coordinates.</p>
777 *
778 * @param enabled true if the popup should always be positioned in screen coordinates
779 * @hide
780 */
781 public void setLayoutInScreenEnabled(boolean enabled) {
782 mLayoutInScreen = enabled;
783 }
784
785 /**
Wale Ogunwale393b1c12014-10-18 16:22:01 -0700786 * <p>Indicates whether the popup window will be attached in the decor frame of its parent
787 * window.
788 *
789 * @return true if the window will be attached to the decor frame of its parent window.
790 *
791 * @see #setAttachedInDecor(boolean)
792 * @see WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR
793 */
794 public boolean isAttachedInDecor() {
795 return mAttachedInDecor;
796 }
797
798 /**
799 * <p>This will attach the popup window to the decor frame of the parent window to avoid
800 * overlaping with screen decorations like the navigation bar. Overrides the default behavior of
801 * the flag {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR}.
802 *
803 * <p>By default the flag is set on SDK version {@link Build.VERSION_CODES#LOLLIPOP_MR1} or
804 * greater and cleared on lesser SDK versions.
805 *
806 * @param enabled true if the popup should be attached to the decor frame of its parent window.
807 *
808 * @see WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR
809 */
810 public void setAttachedInDecor(boolean enabled) {
811 mAttachedInDecor = enabled;
812 mAttachedInDecorSet = true;
813 }
814
815 /**
Adam Powell0bd1d0a2011-07-22 19:35:06 -0700816 * Allows the popup window to force the flag
817 * {@link WindowManager.LayoutParams#FLAG_LAYOUT_INSET_DECOR}, overriding default behavior.
818 * This will cause the popup to inset its content to account for system windows overlaying
819 * the screen, such as the status bar.
820 *
821 * <p>This will often be combined with {@link #setLayoutInScreenEnabled(boolean)}.
822 *
823 * @param enabled true if the popup's views should inset content to account for system windows,
824 * the way that decor views behave for full-screen windows.
825 * @hide
826 */
827 public void setLayoutInsetDecor(boolean enabled) {
828 mLayoutInsetDecor = enabled;
829 }
830
831 /**
Chris Banes36344a92015-04-14 10:43:16 +0100832 * Set the layout type for this window. This value will be passed through to
833 * {@link WindowManager.LayoutParams#type} therefore the value should match any value
834 * {@link WindowManager.LayoutParams#type} accepts.
Adam Powell574b37e2010-10-07 11:15:19 -0700835 *
836 * @param layoutType Layout type for this window.
Chris Banes36344a92015-04-14 10:43:16 +0100837 *
838 * @see WindowManager.LayoutParams#type
Adam Powell574b37e2010-10-07 11:15:19 -0700839 */
840 public void setWindowLayoutType(int layoutType) {
841 mWindowLayoutType = layoutType;
842 }
843
844 /**
Chris Banes36344a92015-04-14 10:43:16 +0100845 * Returns the layout type for this window.
846 *
847 * @see #setWindowLayoutType(int)
Adam Powell574b37e2010-10-07 11:15:19 -0700848 */
849 public int getWindowLayoutType() {
850 return mWindowLayoutType;
851 }
852
853 /**
Adam Powelle0b6cd12011-09-28 22:06:11 -0700854 * Set whether this window is touch modal or if outside touches will be sent to
855 * other windows behind it.
856 * @hide
857 */
858 public void setTouchModal(boolean touchModal) {
859 mNotTouchModal = !touchModal;
860 }
861
862 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800863 * <p>Change the width and height measure specs that are given to the
864 * window manager by the popup. By default these are 0, meaning that
865 * the current width or height is requested as an explicit size from
866 * the window manager. You can supply
Alan Viverette5435a302015-01-29 10:25:34 -0800867 * {@link ViewGroup.LayoutParams#WRAP_CONTENT} or
Romain Guy980a9382010-01-08 15:06:28 -0800868 * {@link ViewGroup.LayoutParams#MATCH_PARENT} to have that measure
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 * spec supplied instead, replacing the absolute width and height that
870 * has been set in the popup.</p>
871 *
872 * <p>If the popup is showing, calling this method will take effect only
873 * the next time the popup is shown.</p>
874 *
875 * @param widthSpec an explicit width measure spec mode, either
876 * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
Romain Guy980a9382010-01-08 15:06:28 -0800877 * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 * width.
879 * @param heightSpec an explicit height measure spec mode, either
880 * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
Romain Guy980a9382010-01-08 15:06:28 -0800881 * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800882 * height.
Alan Viverette259c2842015-03-22 17:39:39 -0700883 *
884 * @deprecated Use {@link #setWidth(int)} and {@link #setHeight(int)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800885 */
Alan Viverette259c2842015-03-22 17:39:39 -0700886 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800887 public void setWindowLayoutMode(int widthSpec, int heightSpec) {
888 mWidthMode = widthSpec;
889 mHeightMode = heightSpec;
890 }
Alan Viverette5435a302015-01-29 10:25:34 -0800891
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800892 /**
Alan Viverette259c2842015-03-22 17:39:39 -0700893 * Returns the popup's height MeasureSpec.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 *
895 * @return the height MeasureSpec of the popup
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896 * @see #setHeight(int)
897 */
898 public int getHeight() {
899 return mHeight;
900 }
901
902 /**
Alan Viverette259c2842015-03-22 17:39:39 -0700903 * Sets the popup's height MeasureSpec.
904 * <p>
905 * If the popup is showing, calling this method will take effect the next
906 * time the popup is shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800907 *
908 * @param height the height MeasureSpec of the popup
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800909 * @see #getHeight()
Alan Viverette5435a302015-01-29 10:25:34 -0800910 * @see #isShowing()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800911 */
912 public void setHeight(int height) {
913 mHeight = height;
914 }
915
916 /**
Alan Viverette259c2842015-03-22 17:39:39 -0700917 * Returns the popup's width MeasureSpec.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800918 *
919 * @return the width MeasureSpec of the popup
Alan Viverette5435a302015-01-29 10:25:34 -0800920 * @see #setWidth(int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800921 */
922 public int getWidth() {
923 return mWidth;
924 }
925
926 /**
Alan Viverette259c2842015-03-22 17:39:39 -0700927 * Sets the popup's width MeasureSpec.
928 * <p>
929 * If the popup is showing, calling this method will take effect the next
930 * time the popup is shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 *
932 * @param width the width MeasureSpec of the popup
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800933 * @see #getWidth()
934 * @see #isShowing()
935 */
936 public void setWidth(int width) {
937 mWidth = width;
938 }
939
940 /**
Alan Viverette75d83792015-01-07 15:51:54 -0800941 * Sets whether the popup window should overlap its anchor view when
942 * displayed as a drop-down.
943 * <p>
944 * If the popup is showing, calling this method will take effect only
945 * the next time the popup is shown.
946 *
947 * @param overlapAnchor Whether the popup should overlap its anchor.
948 *
949 * @see #getOverlapAnchor()
950 * @see #isShowing()
951 */
952 public void setOverlapAnchor(boolean overlapAnchor) {
953 mOverlapAnchor = overlapAnchor;
954 }
955
956 /**
957 * Returns whether the popup window should overlap its anchor view when
958 * displayed as a drop-down.
959 *
960 * @return Whether the popup should overlap its anchor.
961 *
962 * @see #setOverlapAnchor(boolean)
963 */
964 public boolean getOverlapAnchor() {
965 return mOverlapAnchor;
966 }
967
968 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800969 * <p>Indicate whether this popup window is showing on screen.</p>
970 *
971 * @return true if the popup is showing, false otherwise
972 */
973 public boolean isShowing() {
974 return mIsShowing;
975 }
976
977 /**
978 * <p>
979 * Display the content view in a popup window at the specified location. If the popup window
980 * cannot fit on screen, it will be clipped. See {@link android.view.WindowManager.LayoutParams}
981 * for more information on how gravity and the x and y parameters are related. Specifying
982 * a gravity of {@link android.view.Gravity#NO_GRAVITY} is similar to specifying
983 * <code>Gravity.LEFT | Gravity.TOP</code>.
984 * </p>
Alan Viverette5435a302015-01-29 10:25:34 -0800985 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800986 * @param parent a parent view to get the {@link android.view.View#getWindowToken()} token from
987 * @param gravity the gravity which controls the placement of the popup window
988 * @param x the popup's x location offset
989 * @param y the popup's y location offset
990 */
991 public void showAtLocation(View parent, int gravity, int x, int y) {
Adam Powell8ee6d7c2011-09-18 14:59:28 -0700992 showAtLocation(parent.getWindowToken(), gravity, x, y);
993 }
994
995 /**
996 * Display the content view in a popup window at the specified location.
997 *
998 * @param token Window token to use for creating the new window
999 * @param gravity the gravity which controls the placement of the popup window
1000 * @param x the popup's x location offset
1001 * @param y the popup's y location offset
1002 *
1003 * @hide Internal use only. Applications should use
1004 * {@link #showAtLocation(View, int, int, int)} instead.
1005 */
1006 public void showAtLocation(IBinder token, int gravity, int x, int y) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001007 if (isShowing() || mContentView == null) {
1008 return;
1009 }
1010
Alan Viverettee025ed22015-02-02 11:27:21 -08001011 TransitionManager.endTransitions(mDecorView);
1012
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001013 unregisterForScrollChanged();
1014
1015 mIsShowing = true;
1016 mIsDropdown = false;
1017
Alan Viverettee025ed22015-02-02 11:27:21 -08001018 final WindowManager.LayoutParams p = createPopupLayoutParams(token);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019 preparePopup(p);
Alan Viverettee025ed22015-02-02 11:27:21 -08001020
1021 // Only override the default if some gravity was specified.
1022 if (gravity != Gravity.NO_GRAVITY) {
1023 p.gravity = gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001024 }
Alan Viverettee025ed22015-02-02 11:27:21 -08001025
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001026 p.x = x;
1027 p.y = y;
Alan Viverettee025ed22015-02-02 11:27:21 -08001028
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001029 invokePopup(p);
1030 }
1031
1032 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001033 * Display the content view in a popup window anchored to the bottom-left
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001034 * corner of the anchor view. If there is not enough room on screen to show
1035 * the popup in its entirety, this method tries to find a parent scroll
Alan Viverette75d83792015-01-07 15:51:54 -08001036 * view to scroll. If no parent scroll view can be scrolled, the
1037 * bottom-left corner of the popup is pinned at the top left corner of the
1038 * anchor view.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 *
1040 * @param anchor the view on which to pin the popup window
1041 *
1042 * @see #dismiss()
1043 */
1044 public void showAsDropDown(View anchor) {
1045 showAsDropDown(anchor, 0, 0);
1046 }
1047
1048 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001049 * Display the content view in a popup window anchored to the bottom-left
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050 * corner of the anchor view offset by the specified x and y coordinates.
Alan Viverette75d83792015-01-07 15:51:54 -08001051 * If there is not enough room on screen to show the popup in its entirety,
1052 * this method tries to find a parent scroll view to scroll. If no parent
1053 * scroll view can be scrolled, the bottom-left corner of the popup is
1054 * pinned at the top left corner of the anchor view.
1055 * <p>
1056 * If the view later scrolls to move <code>anchor</code> to a different
1057 * location, the popup will be moved correspondingly.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001058 *
1059 * @param anchor the view on which to pin the popup window
Adam Powell54c94de2013-09-26 15:36:34 -07001060 * @param xoff A horizontal offset from the anchor in pixels
1061 * @param yoff A vertical offset from the anchor in pixels
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001062 *
1063 * @see #dismiss()
1064 */
1065 public void showAsDropDown(View anchor, int xoff, int yoff) {
Adam Powell54c94de2013-09-26 15:36:34 -07001066 showAsDropDown(anchor, xoff, yoff, DEFAULT_ANCHORED_GRAVITY);
1067 }
1068
1069 /**
Alan Viverette75d83792015-01-07 15:51:54 -08001070 * Displays the content view in a popup window anchored to the corner of
1071 * another view. The window is positioned according to the specified
1072 * gravity and offset by the specified x and y coordinates.
1073 * <p>
1074 * If there is not enough room on screen to show the popup in its entirety,
1075 * this method tries to find a parent scroll view to scroll. If no parent
1076 * view can be scrolled, the specified vertical gravity will be ignored and
1077 * the popup will anchor itself such that it is visible.
1078 * <p>
1079 * If the view later scrolls to move <code>anchor</code> to a different
1080 * location, the popup will be moved correspondingly.
Adam Powell54c94de2013-09-26 15:36:34 -07001081 *
1082 * @param anchor the view on which to pin the popup window
1083 * @param xoff A horizontal offset from the anchor in pixels
1084 * @param yoff A vertical offset from the anchor in pixels
1085 * @param gravity Alignment of the popup relative to the anchor
1086 *
1087 * @see #dismiss()
1088 */
1089 public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001090 if (isShowing() || mContentView == null) {
1091 return;
1092 }
1093
Alan Viverettee025ed22015-02-02 11:27:21 -08001094 TransitionManager.endTransitions(mDecorView);
1095
Adam Powell54c94de2013-09-26 15:36:34 -07001096 registerForScrollChanged(anchor, xoff, yoff, gravity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001097
1098 mIsShowing = true;
1099 mIsDropdown = true;
1100
Alan Viverettee025ed22015-02-02 11:27:21 -08001101 final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001102 preparePopup(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001103
Alan Viverettee025ed22015-02-02 11:27:21 -08001104 final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff, gravity);
1105 updateAboveAnchor(aboveAnchor);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001106
1107 invokePopup(p);
1108 }
1109
Romain Guy3e141682010-03-08 17:44:40 -08001110 private void updateAboveAnchor(boolean aboveAnchor) {
1111 if (aboveAnchor != mAboveAnchor) {
1112 mAboveAnchor = aboveAnchor;
1113
1114 if (mBackground != null) {
1115 // If the background drawable provided was a StateListDrawable with above-anchor
1116 // and below-anchor states, use those. Otherwise rely on refreshDrawableState to
1117 // do the job.
1118 if (mAboveAnchorBackgroundDrawable != null) {
1119 if (mAboveAnchor) {
Alan Viverette5435a302015-01-29 10:25:34 -08001120 mDecorView.setBackground(mAboveAnchorBackgroundDrawable);
Romain Guy3e141682010-03-08 17:44:40 -08001121 } else {
Alan Viverette5435a302015-01-29 10:25:34 -08001122 mDecorView.setBackground(mBelowAnchorBackgroundDrawable);
Romain Guy3e141682010-03-08 17:44:40 -08001123 }
1124 } else {
Alan Viverette5435a302015-01-29 10:25:34 -08001125 mDecorView.refreshDrawableState();
Romain Guy3e141682010-03-08 17:44:40 -08001126 }
1127 }
1128 }
1129 }
1130
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001131 /**
1132 * Indicates whether the popup is showing above (the y coordinate of the popup's bottom
1133 * is less than the y coordinate of the anchor) or below the anchor view (the y coordinate
1134 * of the popup is greater than y coordinate of the anchor's bottom).
1135 *
1136 * The value returned
1137 * by this method is meaningful only after {@link #showAsDropDown(android.view.View)}
1138 * or {@link #showAsDropDown(android.view.View, int, int)} was invoked.
1139 *
1140 * @return True if this popup is showing above the anchor view, false otherwise.
1141 */
1142 public boolean isAboveAnchor() {
1143 return mAboveAnchor;
1144 }
1145
1146 /**
Alan Viverettee025ed22015-02-02 11:27:21 -08001147 * Prepare the popup by embedding it into a new ViewGroup if the background
1148 * drawable is not null. If embedding is required, the layout parameters'
1149 * height is modified to take into account the background's padding.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001150 *
1151 * @param p the layout parameters of the popup's content view
1152 */
1153 private void preparePopup(WindowManager.LayoutParams p) {
Romain Guy448ecf52009-05-14 16:03:42 -07001154 if (mContentView == null || mContext == null || mWindowManager == null) {
1155 throw new IllegalStateException("You must specify a valid content view by "
1156 + "calling setContentView() before attempting to show the popup.");
1157 }
1158
Alan Viverette8fd949e2015-03-11 12:21:30 -07001159 // The old decor view may be transitioning out. Make sure it finishes
1160 // and cleans up before we try to create another one.
1161 if (mDecorView != null) {
1162 mDecorView.cancelTransitions();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001163 }
Alan Viveretteccb11e12014-07-08 16:04:02 -07001164
Alan Viverette8fd949e2015-03-11 12:21:30 -07001165 // When a background is available, we embed the content view within
1166 // another view that owns the background drawable.
1167 final View backgroundView;
1168 if (mBackground != null) {
1169 backgroundView = createBackgroundView(mContentView);
1170 backgroundView.setBackground(mBackground);
1171 } else {
1172 backgroundView = mContentView;
1173 }
1174
1175 mDecorView = createDecorView(backgroundView);
Alan Viverette5435a302015-01-29 10:25:34 -08001176
1177 // The background owner should be elevated so that it casts a shadow.
Alan Viverette8fd949e2015-03-11 12:21:30 -07001178 backgroundView.setElevation(mElevation);
Alan Viverette5435a302015-01-29 10:25:34 -08001179
1180 // We may wrap that in another view, so we'll need to manually specify
1181 // the surface insets.
Alan Viverette8fd949e2015-03-11 12:21:30 -07001182 final int surfaceInset = (int) Math.ceil(backgroundView.getZ() * 2);
Alan Viverette5435a302015-01-29 10:25:34 -08001183 p.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
1184 p.hasManualSurfaceInsets = true;
1185
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001186 mPopupViewInitialLayoutDirectionInherited =
Alan Viverette5435a302015-01-29 10:25:34 -08001187 (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001188 mPopupWidth = p.width;
1189 mPopupHeight = p.height;
1190 }
1191
1192 /**
Alan Viverette5435a302015-01-29 10:25:34 -08001193 * Wraps a content view in a PopupViewContainer.
1194 *
1195 * @param contentView the content view to wrap
1196 * @return a PopupViewContainer that wraps the content view
1197 */
1198 private PopupBackgroundView createBackgroundView(View contentView) {
1199 final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
1200 final int height;
1201 if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
1202 height = ViewGroup.LayoutParams.WRAP_CONTENT;
1203 } else {
1204 height = ViewGroup.LayoutParams.MATCH_PARENT;
1205 }
1206
1207 final PopupBackgroundView backgroundView = new PopupBackgroundView(mContext);
1208 final PopupBackgroundView.LayoutParams listParams = new PopupBackgroundView.LayoutParams(
1209 ViewGroup.LayoutParams.MATCH_PARENT, height);
1210 backgroundView.addView(contentView, listParams);
1211
1212 return backgroundView;
1213 }
1214
1215 /**
1216 * Wraps a content view in a FrameLayout.
1217 *
1218 * @param contentView the content view to wrap
1219 * @return a FrameLayout that wraps the content view
1220 */
1221 private PopupDecorView createDecorView(View contentView) {
1222 final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
1223 final int height;
1224 if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
1225 height = ViewGroup.LayoutParams.WRAP_CONTENT;
1226 } else {
1227 height = ViewGroup.LayoutParams.MATCH_PARENT;
1228 }
1229
1230 final PopupDecorView decorView = new PopupDecorView(mContext);
1231 decorView.addView(contentView, ViewGroup.LayoutParams.MATCH_PARENT, height);
1232 decorView.setClipChildren(false);
1233 decorView.setClipToPadding(false);
1234
1235 return decorView;
1236 }
1237
1238 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001239 * <p>Invoke the popup window by adding the content view to the window
1240 * manager.</p>
1241 *
1242 * <p>The content view must be non-null when this method is invoked.</p>
1243 *
1244 * @param p the layout parameters of the popup's content view
1245 */
1246 private void invokePopup(WindowManager.LayoutParams p) {
Romain Guy0c0b7682011-05-16 11:54:09 -07001247 if (mContext != null) {
1248 p.packageName = mContext.getPackageName();
1249 }
Alan Viverette5435a302015-01-29 10:25:34 -08001250
Alan Viverette8fd949e2015-03-11 12:21:30 -07001251 final PopupDecorView decorView = mDecorView;
1252 decorView.setFitsSystemWindows(mLayoutInsetDecor);
Alan Viverette8fd949e2015-03-11 12:21:30 -07001253
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001254 setLayoutDirectionFromAnchor();
Alan Viverette5435a302015-01-29 10:25:34 -08001255
Alan Viverette8fd949e2015-03-11 12:21:30 -07001256 mWindowManager.addView(decorView, p);
Alan Viverette95888c02015-04-16 13:27:50 -07001257
1258 if (mEnterTransition != null) {
1259 decorView.requestEnterTransition(mEnterTransition);
1260 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001261 }
1262
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001263 private void setLayoutDirectionFromAnchor() {
1264 if (mAnchor != null) {
1265 View anchor = mAnchor.get();
1266 if (anchor != null && mPopupViewInitialLayoutDirectionInherited) {
Alan Viverette5435a302015-01-29 10:25:34 -08001267 mDecorView.setLayoutDirection(anchor.getLayoutDirection());
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001268 }
1269 }
1270 }
1271
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001272 /**
1273 * <p>Generate the layout parameters for the popup window.</p>
1274 *
1275 * @param token the window token used to bind the popup's window
1276 *
1277 * @return the layout parameters to pass to the window manager
1278 */
Alan Viverettee025ed22015-02-02 11:27:21 -08001279 private WindowManager.LayoutParams createPopupLayoutParams(IBinder token) {
1280 final WindowManager.LayoutParams p = new WindowManager.LayoutParams();
1281
1282 // These gravity settings put the view at the top left corner of the
1283 // screen. The view is then positioned to the appropriate location by
1284 // setting the x and y offsets to match the anchor's bottom-left
1285 // corner.
Fabrice Di Meglioaac0d4e2012-07-19 19:21:26 -07001286 p.gravity = Gravity.START | Gravity.TOP;
Alan Viverettee025ed22015-02-02 11:27:21 -08001287 p.flags = computeFlags(p.flags);
1288 p.type = mWindowLayoutType;
1289 p.token = token;
1290 p.softInputMode = mSoftInputMode;
1291 p.windowAnimations = computeAnimationResource();
1292
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001293 if (mBackground != null) {
1294 p.format = mBackground.getOpacity();
1295 } else {
1296 p.format = PixelFormat.TRANSLUCENT;
1297 }
Alan Viverettee025ed22015-02-02 11:27:21 -08001298
1299 if (mHeightMode < 0) {
1300 p.height = mLastHeight = mHeightMode;
1301 } else {
1302 p.height = mLastHeight = mHeight;
1303 }
1304
1305 if (mWidthMode < 0) {
1306 p.width = mLastWidth = mWidthMode;
1307 } else {
1308 p.width = mLastWidth = mWidth;
1309 }
1310
1311 // Used for debugging.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001312 p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
1313
1314 return p;
1315 }
1316
1317 private int computeFlags(int curFlags) {
1318 curFlags &= ~(
1319 WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES |
1320 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
1321 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
1322 WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
1323 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
Adam Powellba0a2c32010-09-28 17:41:23 -07001324 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
1325 WindowManager.LayoutParams.FLAG_SPLIT_TOUCH);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001326 if(mIgnoreCheekPress) {
1327 curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
1328 }
1329 if (!mFocusable) {
1330 curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1331 if (mInputMethodMode == INPUT_METHOD_NEEDED) {
1332 curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1333 }
1334 } else if (mInputMethodMode == INPUT_METHOD_NOT_NEEDED) {
1335 curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1336 }
1337 if (!mTouchable) {
1338 curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
1339 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001340 if (mOutsideTouchable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001341 curFlags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
1342 }
1343 if (!mClippingEnabled) {
1344 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
1345 }
Jeff Brown46e75292010-11-10 16:53:45 -08001346 if (isSplitTouchEnabled()) {
Jeff Brown01ce2e92010-09-26 22:20:12 -07001347 curFlags |= WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
1348 }
Adam Powellba0a2c32010-09-28 17:41:23 -07001349 if (mLayoutInScreen) {
1350 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
1351 }
Adam Powell0bd1d0a2011-07-22 19:35:06 -07001352 if (mLayoutInsetDecor) {
1353 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
1354 }
Adam Powelle0b6cd12011-09-28 22:06:11 -07001355 if (mNotTouchModal) {
1356 curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
1357 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -07001358 if (mAttachedInDecor) {
1359 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR;
1360 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001361 return curFlags;
1362 }
Wale Ogunwale393b1c12014-10-18 16:22:01 -07001363
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001364 private int computeAnimationResource() {
Alan Viverette5435a302015-01-29 10:25:34 -08001365 if (mAnimationStyle == ANIMATION_STYLE_DEFAULT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001366 if (mIsDropdown) {
1367 return mAboveAnchor
1368 ? com.android.internal.R.style.Animation_DropDownUp
1369 : com.android.internal.R.style.Animation_DropDownDown;
1370 }
1371 return 0;
1372 }
1373 return mAnimationStyle;
1374 }
Alan Viverette560f1702014-05-05 14:40:07 -07001375
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001376 /**
Alan Viverette560f1702014-05-05 14:40:07 -07001377 * Positions the popup window on screen. When the popup window is too tall
1378 * to fit under the anchor, a parent scroll view is seeked and scrolled up
1379 * to reclaim space. If scrolling is not possible or not enough, the popup
1380 * window gets moved on top of the anchor.
1381 * <p>
1382 * The height must have been set on the layout parameters prior to calling
1383 * this method.
Alan Viverette5435a302015-01-29 10:25:34 -08001384 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001385 * @param anchor the view on which the popup window must be anchored
1386 * @param p the layout parameters used to display the drop down
Alan Viverette560f1702014-05-05 14:40:07 -07001387 * @param xoff horizontal offset used to adjust for background padding
1388 * @param yoff vertical offset used to adjust for background padding
1389 * @param gravity horizontal gravity specifying popup alignment
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001390 * @return true if the popup is translated upwards to fit on screen
1391 */
Alan Viverette560f1702014-05-05 14:40:07 -07001392 private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p, int xoff,
1393 int yoff, int gravity) {
Adam Powell62e2bde2011-08-15 15:50:05 -07001394 final int anchorHeight = anchor.getHeight();
Alan Viverette560f1702014-05-05 14:40:07 -07001395 final int anchorWidth = anchor.getWidth();
1396 if (mOverlapAnchor) {
1397 yoff -= anchorHeight;
1398 }
1399
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001400 anchor.getLocationInWindow(mDrawingLocation);
1401 p.x = mDrawingLocation[0] + xoff;
Adam Powell62e2bde2011-08-15 15:50:05 -07001402 p.y = mDrawingLocation[1] + anchorHeight + yoff;
Adam Powell54c94de2013-09-26 15:36:34 -07001403
Alan Viverette560f1702014-05-05 14:40:07 -07001404 final int hgrav = Gravity.getAbsoluteGravity(gravity, anchor.getLayoutDirection())
1405 & Gravity.HORIZONTAL_GRAVITY_MASK;
Adam Powell54c94de2013-09-26 15:36:34 -07001406 if (hgrav == Gravity.RIGHT) {
Alan Viverette560f1702014-05-05 14:40:07 -07001407 // Flip the location to align the right sides of the popup and
1408 // anchor instead of left.
1409 p.x -= mPopupWidth - anchorWidth;
Adam Powell54c94de2013-09-26 15:36:34 -07001410 }
Alan Viverette560f1702014-05-05 14:40:07 -07001411
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001412 boolean onTop = false;
1413
Adam Powell54c94de2013-09-26 15:36:34 -07001414 p.gravity = Gravity.LEFT | Gravity.TOP;
1415
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001416 anchor.getLocationOnScreen(mScreenLocation);
1417 final Rect displayFrame = new Rect();
1418 anchor.getWindowVisibleDisplayFrame(displayFrame);
Adam Powell62e2bde2011-08-15 15:50:05 -07001419
Alan Viverette560f1702014-05-05 14:40:07 -07001420 final int screenY = mScreenLocation[1] + anchorHeight + yoff;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001421 final View root = anchor.getRootView();
Alan Viverette560f1702014-05-05 14:40:07 -07001422 if (screenY + mPopupHeight > displayFrame.bottom
1423 || p.x + mPopupWidth - root.getWidth() > 0) {
1424 // If the drop down disappears at the bottom of the screen, we try
1425 // to scroll a parent scrollview or move the drop down back up on
1426 // top of the edit box.
Adam Powellb7c1b202011-02-17 12:03:09 -08001427 if (mAllowScrollingAnchorParent) {
Alan Viverette560f1702014-05-05 14:40:07 -07001428 final int scrollX = anchor.getScrollX();
1429 final int scrollY = anchor.getScrollY();
1430 final Rect r = new Rect(scrollX, scrollY, scrollX + mPopupWidth + xoff,
1431 scrollY + mPopupHeight + anchorHeight + yoff);
Adam Powellb7c1b202011-02-17 12:03:09 -08001432 anchor.requestRectangleOnScreen(r, true);
1433 }
Romain Guy3e141682010-03-08 17:44:40 -08001434
Alan Viverette560f1702014-05-05 14:40:07 -07001435 // Now we re-evaluate the space available, and decide from that
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001436 // whether the pop-up will go above or below the anchor.
1437 anchor.getLocationInWindow(mDrawingLocation);
1438 p.x = mDrawingLocation[0] + xoff;
Alan Viverette560f1702014-05-05 14:40:07 -07001439 p.y = mDrawingLocation[1] + anchorHeight + yoff;
Adam Powell54c94de2013-09-26 15:36:34 -07001440
Alan Viverette560f1702014-05-05 14:40:07 -07001441 // Preserve the gravity adjustment.
Adam Powell54c94de2013-09-26 15:36:34 -07001442 if (hgrav == Gravity.RIGHT) {
Alan Viverette560f1702014-05-05 14:40:07 -07001443 p.x -= mPopupWidth - anchorWidth;
Adam Powell54c94de2013-09-26 15:36:34 -07001444 }
Alan Viverette560f1702014-05-05 14:40:07 -07001445
1446 // Determine whether there is more space above or below the anchor.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001447 anchor.getLocationOnScreen(mScreenLocation);
Alan Viverette560f1702014-05-05 14:40:07 -07001448 onTop = (displayFrame.bottom - mScreenLocation[1] - anchorHeight - yoff) <
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001449 (mScreenLocation[1] - yoff - displayFrame.top);
1450 if (onTop) {
Adam Powell54c94de2013-09-26 15:36:34 -07001451 p.gravity = Gravity.LEFT | Gravity.BOTTOM;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001452 p.y = root.getHeight() - mDrawingLocation[1] + yoff;
1453 } else {
Alan Viverette560f1702014-05-05 14:40:07 -07001454 p.y = mDrawingLocation[1] + anchorHeight + yoff;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001455 }
1456 }
1457
Adam Powell56c2d332010-11-05 20:03:03 -07001458 if (mClipToScreen) {
1459 final int displayFrameWidth = displayFrame.right - displayFrame.left;
Alan Viverette560f1702014-05-05 14:40:07 -07001460 final int right = p.x + p.width;
Adam Powell56c2d332010-11-05 20:03:03 -07001461 if (right > displayFrameWidth) {
1462 p.x -= right - displayFrameWidth;
1463 }
Alan Viverette560f1702014-05-05 14:40:07 -07001464
Adam Powell56c2d332010-11-05 20:03:03 -07001465 if (p.x < displayFrame.left) {
1466 p.x = displayFrame.left;
1467 p.width = Math.min(p.width, displayFrameWidth);
1468 }
1469
Adam Powell5f83a602011-01-19 17:58:04 -08001470 if (onTop) {
Alan Viverette560f1702014-05-05 14:40:07 -07001471 final int popupTop = mScreenLocation[1] + yoff - mPopupHeight;
Adam Powell5f83a602011-01-19 17:58:04 -08001472 if (popupTop < 0) {
1473 p.y += popupTop;
1474 }
1475 } else {
1476 p.y = Math.max(p.y, displayFrame.top);
1477 }
Adam Powell56c2d332010-11-05 20:03:03 -07001478 }
1479
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001480 p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
Alan Viverette560f1702014-05-05 14:40:07 -07001481
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001482 return onTop;
1483 }
Alan Viverette5435a302015-01-29 10:25:34 -08001484
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001485 /**
1486 * Returns the maximum height that is available for the popup to be
1487 * completely shown. It is recommended that this height be the maximum for
1488 * the popup's height, otherwise it is possible that the popup will be
1489 * clipped.
Alan Viverette5435a302015-01-29 10:25:34 -08001490 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001491 * @param anchor The view on which the popup window must be anchored.
1492 * @return The maximum available height for the popup to be completely
1493 * shown.
1494 */
1495 public int getMaxAvailableHeight(View anchor) {
1496 return getMaxAvailableHeight(anchor, 0);
1497 }
1498
1499 /**
1500 * Returns the maximum height that is available for the popup to be
1501 * completely shown. It is recommended that this height be the maximum for
1502 * the popup's height, otherwise it is possible that the popup will be
1503 * clipped.
1504 *
1505 * @param anchor The view on which the popup window must be anchored.
1506 * @param yOffset y offset from the view's bottom edge
1507 * @return The maximum available height for the popup to be completely
1508 * shown.
1509 */
1510 public int getMaxAvailableHeight(View anchor, int yOffset) {
Mike LeBeau98acd542009-05-07 19:04:39 -07001511 return getMaxAvailableHeight(anchor, yOffset, false);
1512 }
Alan Viverette5435a302015-01-29 10:25:34 -08001513
Mike LeBeau98acd542009-05-07 19:04:39 -07001514 /**
1515 * Returns the maximum height that is available for the popup to be
1516 * completely shown, optionally ignoring any bottom decorations such as
1517 * the input method. It is recommended that this height be the maximum for
1518 * the popup's height, otherwise it is possible that the popup will be
1519 * clipped.
Alan Viverette5435a302015-01-29 10:25:34 -08001520 *
Mike LeBeau98acd542009-05-07 19:04:39 -07001521 * @param anchor The view on which the popup window must be anchored.
1522 * @param yOffset y offset from the view's bottom edge
1523 * @param ignoreBottomDecorations if true, the height returned will be
1524 * all the way to the bottom of the display, ignoring any
1525 * bottom decorations
1526 * @return The maximum available height for the popup to be completely
1527 * shown.
Alan Viverette5435a302015-01-29 10:25:34 -08001528 *
Mike LeBeau98acd542009-05-07 19:04:39 -07001529 * @hide Pending API council approval.
1530 */
1531 public int getMaxAvailableHeight(View anchor, int yOffset, boolean ignoreBottomDecorations) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001532 final Rect displayFrame = new Rect();
1533 anchor.getWindowVisibleDisplayFrame(displayFrame);
1534
1535 final int[] anchorPos = mDrawingLocation;
1536 anchor.getLocationOnScreen(anchorPos);
Alan Viverette5435a302015-01-29 10:25:34 -08001537
Mike LeBeau98acd542009-05-07 19:04:39 -07001538 int bottomEdge = displayFrame.bottom;
1539 if (ignoreBottomDecorations) {
Adam Powella7287f42010-08-17 21:17:04 -07001540 Resources res = anchor.getContext().getResources();
Adam Powell3f4a7642011-05-20 15:56:25 -07001541 bottomEdge = res.getDisplayMetrics().heightPixels;
Mike LeBeau98acd542009-05-07 19:04:39 -07001542 }
1543 final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001544 final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
1545
1546 // anchorPos[1] is distance from anchor to top of screen
1547 int returnedHeight = Math.max(distanceToBottom, distanceToTop);
1548 if (mBackground != null) {
1549 mBackground.getPadding(mTempRect);
Alan Viverette5435a302015-01-29 10:25:34 -08001550 returnedHeight -= mTempRect.top + mTempRect.bottom;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001551 }
Alan Viverette5435a302015-01-29 10:25:34 -08001552
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001553 return returnedHeight;
1554 }
Alan Viverette5435a302015-01-29 10:25:34 -08001555
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001556 /**
Alan Viverette7878edf2015-02-03 15:49:18 -08001557 * Disposes of the popup window. This method can be invoked only after
1558 * {@link #showAsDropDown(android.view.View)} has been executed. Failing
1559 * that, calling this method will have no effect.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001560 *
Alan Viverette5435a302015-01-29 10:25:34 -08001561 * @see #showAsDropDown(android.view.View)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001562 */
1563 public void dismiss() {
Alan Viverette8fd949e2015-03-11 12:21:30 -07001564 if (!isShowing() || mIsTransitioningToDismiss) {
Alan Viverettee025ed22015-02-02 11:27:21 -08001565 return;
1566 }
Svetoslav Ganov06f938e2011-11-10 14:31:37 -08001567
Alan Viverette8fd949e2015-03-11 12:21:30 -07001568 final PopupDecorView decorView = mDecorView;
1569 final View contentView = mContentView;
1570
1571 final ViewGroup contentHolder;
1572 final ViewParent contentParent = contentView.getParent();
1573 if (contentParent instanceof ViewGroup) {
1574 contentHolder = ((ViewGroup) contentParent);
1575 } else {
1576 contentHolder = null;
1577 }
1578
1579 // Ensure any ongoing or pending transitions are canceled.
1580 decorView.cancelTransitions();
1581
Alan Viverettee025ed22015-02-02 11:27:21 -08001582 mIsShowing = false;
Alan Viverette8fd949e2015-03-11 12:21:30 -07001583 mIsTransitioningToDismiss = true;
Craig Mautnerb82d0742012-05-23 08:48:39 -07001584
Alan Viverette95888c02015-04-16 13:27:50 -07001585 final Transition exitTransition = mExitTransition;
1586 if (exitTransition != null && decorView.isLaidOut()) {
1587 // The decor view is non-interactive during exit transitions.
1588 final LayoutParams p = (LayoutParams) decorView.getLayoutParams();
1589 p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
1590 p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
1591 mWindowManager.updateViewLayout(decorView, p);
1592
1593 final Rect epicenter = getRelativeAnchorBounds();
1594 exitTransition.setEpicenterCallback(new EpicenterCallback() {
1595 @Override
1596 public Rect onGetEpicenter(Transition transition) {
1597 return epicenter;
1598 }
1599 });
1600 decorView.startExitTransition(exitTransition, new TransitionListenerAdapter() {
Alan Viverettee025ed22015-02-02 11:27:21 -08001601 @Override
1602 public void onTransitionEnd(Transition transition) {
Alan Viverette8fd949e2015-03-11 12:21:30 -07001603 dismissImmediate(decorView, contentHolder, contentView);
Alan Viverettee025ed22015-02-02 11:27:21 -08001604 }
1605 });
Alan Viverettee025ed22015-02-02 11:27:21 -08001606 } else {
Alan Viverette8fd949e2015-03-11 12:21:30 -07001607 dismissImmediate(decorView, contentHolder, contentView);
Alan Viverette7878edf2015-02-03 15:49:18 -08001608 }
1609
Alan Viverette95888c02015-04-16 13:27:50 -07001610 // Clears the anchor view.
1611 unregisterForScrollChanged();
1612
Alan Viverette7878edf2015-02-03 15:49:18 -08001613 if (mOnDismissListener != null) {
1614 mOnDismissListener.onDismiss();
Alan Viverette5435a302015-01-29 10:25:34 -08001615 }
1616 }
1617
Alan Viverette95888c02015-04-16 13:27:50 -07001618 private Rect getRelativeAnchorBounds() {
1619 final View anchor = mAnchor != null ? mAnchor.get() : null;
1620 final View decor = mDecorView;
1621 if (anchor == null || decor == null) {
1622 return null;
1623 }
1624
1625 final int[] anchorLocation = anchor.getLocationOnScreen();
1626 final int[] popupLocation = mDecorView.getLocationOnScreen();
1627
1628 // Compute the position of the anchor relative to the popup.
1629 final Rect bounds = new Rect(0, 0, anchor.getWidth(), anchor.getHeight());
1630 bounds.offset(anchorLocation[0] - popupLocation[0], anchorLocation[1] - popupLocation[1]);
1631 return bounds;
1632 }
1633
Alan Viverette5435a302015-01-29 10:25:34 -08001634 /**
1635 * Removes the popup from the window manager and tears down the supporting
1636 * view hierarchy, if necessary.
1637 */
Alan Viverette8fd949e2015-03-11 12:21:30 -07001638 private void dismissImmediate(View decorView, ViewGroup contentHolder, View contentView) {
1639 // If this method gets called and the decor view doesn't have a parent,
1640 // then it was either never added or was already removed. That should
1641 // never happen, but it's worth checking to avoid potential crashes.
1642 if (decorView.getParent() != null) {
1643 mWindowManager.removeViewImmediate(decorView);
Alan Viverettedf4639a02015-03-02 12:40:34 -08001644 }
1645
Alan Viverette8fd949e2015-03-11 12:21:30 -07001646 if (contentHolder != null) {
1647 contentHolder.removeView(contentView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001648 }
Alan Viverette8fd949e2015-03-11 12:21:30 -07001649
1650 // This needs to stay until after all transitions have ended since we
1651 // need the reference to cancel transitions in preparePopup().
1652 mDecorView = null;
1653 mIsTransitioningToDismiss = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001654 }
1655
1656 /**
1657 * Sets the listener to be called when the window is dismissed.
Alan Viverette5435a302015-01-29 10:25:34 -08001658 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001659 * @param onDismissListener The listener.
1660 */
1661 public void setOnDismissListener(OnDismissListener onDismissListener) {
1662 mOnDismissListener = onDismissListener;
1663 }
Alan Viverette5435a302015-01-29 10:25:34 -08001664
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001665 /**
1666 * Updates the state of the popup window, if it is currently being displayed,
Alan Viverette259c2842015-03-22 17:39:39 -07001667 * from the currently set state.
1668 * <p>
1669 * This includes:
1670 * <ul>
1671 * <li>{@link #setClippingEnabled(boolean)}</li>
1672 * <li>{@link #setFocusable(boolean)}</li>
1673 * <li>{@link #setIgnoreCheekPress()}</li>
1674 * <li>{@link #setInputMethodMode(int)}</li>
1675 * <li>{@link #setTouchable(boolean)}</li>
1676 * <li>{@link #setAnimationStyle(int)}</li>
1677 * </ul>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001678 */
1679 public void update() {
1680 if (!isShowing() || mContentView == null) {
1681 return;
1682 }
Alan Viverette5435a302015-01-29 10:25:34 -08001683
1684 final WindowManager.LayoutParams p =
1685 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
1686
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001687 boolean update = false;
Alan Viverette5435a302015-01-29 10:25:34 -08001688
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001689 final int newAnim = computeAnimationResource();
1690 if (newAnim != p.windowAnimations) {
1691 p.windowAnimations = newAnim;
1692 update = true;
1693 }
1694
1695 final int newFlags = computeFlags(p.flags);
1696 if (newFlags != p.flags) {
1697 p.flags = newFlags;
1698 update = true;
1699 }
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001700
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001701 if (update) {
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001702 setLayoutDirectionFromAnchor();
Alan Viverette5435a302015-01-29 10:25:34 -08001703 mWindowManager.updateViewLayout(mDecorView, p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001704 }
1705 }
Romain Guyd6a463a2009-05-21 23:10:10 -07001706
1707 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001708 * Updates the dimension of the popup window.
1709 * <p>
1710 * Calling this function also updates the window with the current popup
1711 * state as described for {@link #update()}.
Romain Guyd6a463a2009-05-21 23:10:10 -07001712 *
Alan Viverette259c2842015-03-22 17:39:39 -07001713 * @param width the new width, must be >= 0 or -1 to ignore
1714 * @param height the new height, must be >= 0 or -1 to ignore
Romain Guyd6a463a2009-05-21 23:10:10 -07001715 */
1716 public void update(int width, int height) {
Alan Viverette5435a302015-01-29 10:25:34 -08001717 final WindowManager.LayoutParams p =
1718 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
Romain Guyd6a463a2009-05-21 23:10:10 -07001719 update(p.x, p.y, width, height, false);
1720 }
Alan Viverette5435a302015-01-29 10:25:34 -08001721
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001722 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001723 * Updates the position and the dimension of the popup window.
1724 * <p>
1725 * Width and height can be set to -1 to update location only. Calling this
1726 * function also updates the window with the current popup state as
1727 * described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001728 *
1729 * @param x the new x location
1730 * @param y the new y location
Alan Viverette259c2842015-03-22 17:39:39 -07001731 * @param width the new width, must be >= 0 or -1 to ignore
1732 * @param height the new height, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001733 */
1734 public void update(int x, int y, int width, int height) {
1735 update(x, y, width, height, false);
1736 }
1737
1738 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001739 * Updates the position and the dimension of the popup window.
1740 * <p>
1741 * Width and height can be set to -1 to update location only. Calling this
1742 * function also updates the window with the current popup state as
1743 * described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001744 *
1745 * @param x the new x location
1746 * @param y the new y location
Alan Viverette259c2842015-03-22 17:39:39 -07001747 * @param width the new width, must be >= 0 or -1 to ignore
1748 * @param height the new height, must be >= 0 or -1 to ignore
1749 * @param force {@code true} to reposition the window even if the specified
1750 * position already seems to correspond to the LayoutParams,
1751 * {@code false} to only reposition if needed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001752 */
1753 public void update(int x, int y, int width, int height, boolean force) {
Alan Viverette259c2842015-03-22 17:39:39 -07001754 if (width >= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001755 mLastWidth = width;
1756 setWidth(width);
1757 }
1758
Alan Viverette259c2842015-03-22 17:39:39 -07001759 if (height >= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001760 mLastHeight = height;
1761 setHeight(height);
1762 }
1763
1764 if (!isShowing() || mContentView == null) {
1765 return;
1766 }
1767
Alan Viverette5435a302015-01-29 10:25:34 -08001768 final WindowManager.LayoutParams p =
1769 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001770
1771 boolean update = force;
1772
1773 final int finalWidth = mWidthMode < 0 ? mWidthMode : mLastWidth;
1774 if (width != -1 && p.width != finalWidth) {
1775 p.width = mLastWidth = finalWidth;
1776 update = true;
1777 }
1778
1779 final int finalHeight = mHeightMode < 0 ? mHeightMode : mLastHeight;
1780 if (height != -1 && p.height != finalHeight) {
1781 p.height = mLastHeight = finalHeight;
1782 update = true;
1783 }
1784
1785 if (p.x != x) {
1786 p.x = x;
1787 update = true;
1788 }
1789
1790 if (p.y != y) {
1791 p.y = y;
1792 update = true;
1793 }
1794
1795 final int newAnim = computeAnimationResource();
1796 if (newAnim != p.windowAnimations) {
1797 p.windowAnimations = newAnim;
1798 update = true;
1799 }
1800
1801 final int newFlags = computeFlags(p.flags);
1802 if (newFlags != p.flags) {
1803 p.flags = newFlags;
1804 update = true;
1805 }
Mike LeBeau98acd542009-05-07 19:04:39 -07001806
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001807 if (update) {
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001808 setLayoutDirectionFromAnchor();
Alan Viverette5435a302015-01-29 10:25:34 -08001809 mWindowManager.updateViewLayout(mDecorView, p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001810 }
1811 }
1812
1813 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001814 * Updates the position and the dimension of the popup window.
1815 * <p>
1816 * Calling this function also updates the window with the current popup
1817 * state as described for {@link #update()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001818 *
1819 * @param anchor the popup's anchor view
Alan Viverette259c2842015-03-22 17:39:39 -07001820 * @param width the new width, must be >= 0 or -1 to ignore
1821 * @param height the new height, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001822 */
1823 public void update(View anchor, int width, int height) {
Alan Viverette75d83792015-01-07 15:51:54 -08001824 update(anchor, false, 0, 0, true, width, height);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001825 }
1826
1827 /**
Alan Viverette259c2842015-03-22 17:39:39 -07001828 * Updates the position and the dimension of the popup window.
1829 * <p>
1830 * Width and height can be set to -1 to update location only. Calling this
1831 * function also updates the window with the current popup state as
1832 * described for {@link #update()}.
1833 * <p>
1834 * If the view later scrolls to move {@code anchor} to a different
1835 * location, the popup will be moved correspondingly.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001836 *
1837 * @param anchor the popup's anchor view
1838 * @param xoff x offset from the view's left edge
1839 * @param yoff y offset from the view's bottom edge
Alan Viverette259c2842015-03-22 17:39:39 -07001840 * @param width the new width, must be >= 0 or -1 to ignore
1841 * @param height the new height, must be >= 0 or -1 to ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001842 */
1843 public void update(View anchor, int xoff, int yoff, int width, int height) {
Alan Viverette75d83792015-01-07 15:51:54 -08001844 update(anchor, true, xoff, yoff, true, width, height);
The Android Open Source Project10592532009-03-18 17:39:46 -07001845 }
1846
1847 private void update(View anchor, boolean updateLocation, int xoff, int yoff,
Alan Viverette75d83792015-01-07 15:51:54 -08001848 boolean updateDimension, int width, int height) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001849
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001850 if (!isShowing() || mContentView == null) {
1851 return;
1852 }
1853
Alan Viverette75d83792015-01-07 15:51:54 -08001854 final WeakReference<View> oldAnchor = mAnchor;
1855 final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff);
Gilles Debunne81f08082011-02-17 14:07:19 -08001856 if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) {
Alan Viverette75d83792015-01-07 15:51:54 -08001857 registerForScrollChanged(anchor, xoff, yoff, mAnchoredGravity);
Gilles Debunne81f08082011-02-17 14:07:19 -08001858 } else if (needsUpdate) {
1859 // No need to register again if this is a DropDown, showAsDropDown already did.
1860 mAnchorXoff = xoff;
1861 mAnchorYoff = yoff;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001862 }
1863
The Android Open Source Project10592532009-03-18 17:39:46 -07001864 if (updateDimension) {
1865 if (width == -1) {
1866 width = mPopupWidth;
1867 } else {
1868 mPopupWidth = width;
1869 }
1870 if (height == -1) {
1871 height = mPopupHeight;
1872 } else {
1873 mPopupHeight = height;
1874 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001875 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001876
Alan Viverette75d83792015-01-07 15:51:54 -08001877 final WindowManager.LayoutParams p =
Alan Viverette5435a302015-01-29 10:25:34 -08001878 (WindowManager.LayoutParams) mDecorView.getLayoutParams();
Alan Viverette75d83792015-01-07 15:51:54 -08001879 final int x = p.x;
1880 final int y = p.y;
Romain Guy3e141682010-03-08 17:44:40 -08001881 if (updateLocation) {
Alan Viverette75d83792015-01-07 15:51:54 -08001882 updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff, mAnchoredGravity));
Romain Guy3e141682010-03-08 17:44:40 -08001883 } else {
Adam Powell54c94de2013-09-26 15:36:34 -07001884 updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
1885 mAnchoredGravity));
Romain Guy3e141682010-03-08 17:44:40 -08001886 }
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001887
Romain Guy3e141682010-03-08 17:44:40 -08001888 update(p.x, p.y, width, height, x != p.x || y != p.y);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001889 }
1890
1891 /**
1892 * Listener that is called when this popup window is dismissed.
1893 */
1894 public interface OnDismissListener {
1895 /**
1896 * Called when this popup window is dismissed.
1897 */
1898 public void onDismiss();
1899 }
1900
1901 private void unregisterForScrollChanged() {
Alan Viverettee025ed22015-02-02 11:27:21 -08001902 final WeakReference<View> anchorRef = mAnchor;
1903 final View anchor = anchorRef == null ? null : anchorRef.get();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001904 if (anchor != null) {
Alan Viverettee025ed22015-02-02 11:27:21 -08001905 final ViewTreeObserver vto = anchor.getViewTreeObserver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001906 vto.removeOnScrollChangedListener(mOnScrollChangedListener);
1907 }
Alan Viverettee025ed22015-02-02 11:27:21 -08001908
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001909 mAnchor = null;
1910 }
1911
Adam Powell54c94de2013-09-26 15:36:34 -07001912 private void registerForScrollChanged(View anchor, int xoff, int yoff, int gravity) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001913 unregisterForScrollChanged();
1914
Alan Viverette5435a302015-01-29 10:25:34 -08001915 mAnchor = new WeakReference<>(anchor);
Alan Viverettee025ed22015-02-02 11:27:21 -08001916
1917 final ViewTreeObserver vto = anchor.getViewTreeObserver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001918 if (vto != null) {
1919 vto.addOnScrollChangedListener(mOnScrollChangedListener);
1920 }
1921
1922 mAnchorXoff = xoff;
1923 mAnchorYoff = yoff;
Adam Powell54c94de2013-09-26 15:36:34 -07001924 mAnchoredGravity = gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001925 }
1926
Alan Viverette5435a302015-01-29 10:25:34 -08001927 private class PopupDecorView extends FrameLayout {
Alan Viverette8fd949e2015-03-11 12:21:30 -07001928 private TransitionListenerAdapter mPendingExitListener;
1929
Alan Viverette5435a302015-01-29 10:25:34 -08001930 public PopupDecorView(Context context) {
1931 super(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001932 }
1933
1934 @Override
1935 public boolean dispatchKeyEvent(KeyEvent event) {
1936 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
Per Andersson4ae02b32011-01-17 11:16:23 +01001937 if (getKeyDispatcherState() == null) {
1938 return super.dispatchKeyEvent(event);
1939 }
1940
Alan Viverette5435a302015-01-29 10:25:34 -08001941 if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
1942 final KeyEvent.DispatcherState state = getKeyDispatcherState();
Jeff Brownb3ea9222011-01-10 16:26:36 -08001943 if (state != null) {
1944 state.startTracking(event, this);
1945 }
Dianne Hackborn8d374262009-09-14 21:21:52 -07001946 return true;
Jeff Brownb3ea9222011-01-10 16:26:36 -08001947 } else if (event.getAction() == KeyEvent.ACTION_UP) {
Alan Viverette5435a302015-01-29 10:25:34 -08001948 final KeyEvent.DispatcherState state = getKeyDispatcherState();
Jeff Brownb3ea9222011-01-10 16:26:36 -08001949 if (state != null && state.isTracking(event) && !event.isCanceled()) {
1950 dismiss();
1951 return true;
1952 }
Dianne Hackborn8d374262009-09-14 21:21:52 -07001953 }
1954 return super.dispatchKeyEvent(event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001955 } else {
1956 return super.dispatchKeyEvent(event);
1957 }
1958 }
1959
1960 @Override
1961 public boolean dispatchTouchEvent(MotionEvent ev) {
1962 if (mTouchInterceptor != null && mTouchInterceptor.onTouch(this, ev)) {
1963 return true;
1964 }
1965 return super.dispatchTouchEvent(ev);
1966 }
1967
1968 @Override
1969 public boolean onTouchEvent(MotionEvent event) {
1970 final int x = (int) event.getX();
1971 final int y = (int) event.getY();
Alan Viverette5435a302015-01-29 10:25:34 -08001972
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001973 if ((event.getAction() == MotionEvent.ACTION_DOWN)
1974 && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
1975 dismiss();
1976 return true;
1977 } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
1978 dismiss();
1979 return true;
1980 } else {
1981 return super.onTouchEvent(event);
1982 }
1983 }
Alan Viverette8fd949e2015-03-11 12:21:30 -07001984
1985 /**
1986 * Requests that an enter transition run after the next layout pass.
1987 */
1988 public void requestEnterTransition(Transition transition) {
1989 final ViewTreeObserver observer = getViewTreeObserver();
1990 if (observer != null && transition != null) {
1991 final Transition enterTransition = transition.clone();
1992
1993 // Postpone the enter transition after the first layout pass.
1994 observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
1995 @Override
1996 public void onGlobalLayout() {
1997 final ViewTreeObserver observer = getViewTreeObserver();
1998 if (observer != null) {
1999 observer.removeOnGlobalLayoutListener(this);
2000 }
2001
Alan Viverette95888c02015-04-16 13:27:50 -07002002 final Rect epicenter = getRelativeAnchorBounds();
2003 enterTransition.setEpicenterCallback(new EpicenterCallback() {
2004 @Override
2005 public Rect onGetEpicenter(Transition transition) {
2006 return epicenter;
2007 }
2008 });
Alan Viverette8fd949e2015-03-11 12:21:30 -07002009 startEnterTransition(enterTransition);
2010 }
2011 });
2012 }
2013 }
2014
2015 /**
2016 * Starts the pending enter transition, if one is set.
2017 */
2018 private void startEnterTransition(Transition enterTransition) {
2019 final int count = getChildCount();
2020 for (int i = 0; i < count; i++) {
2021 final View child = getChildAt(i);
2022 enterTransition.addTarget(child);
2023 child.setVisibility(View.INVISIBLE);
2024 }
2025
2026 TransitionManager.beginDelayedTransition(this, enterTransition);
2027
2028 for (int i = 0; i < count; i++) {
2029 final View child = getChildAt(i);
2030 child.setVisibility(View.VISIBLE);
2031 }
2032 }
2033
2034 /**
2035 * Starts an exit transition immediately.
2036 * <p>
2037 * <strong>Note:</strong> The transition listener is guaranteed to have
2038 * its {@code onTransitionEnd} method called even if the transition
2039 * never starts; however, it may be called with a {@code null} argument.
2040 */
2041 public void startExitTransition(Transition transition, final TransitionListener listener) {
2042 if (transition == null) {
2043 return;
2044 }
2045
2046 // The exit listener MUST be called for cleanup, even if the
2047 // transition never starts or ends. Stash it for later.
2048 mPendingExitListener = new TransitionListenerAdapter() {
2049 @Override
2050 public void onTransitionEnd(Transition transition) {
2051 listener.onTransitionEnd(transition);
2052
2053 // The listener was called. Our job here is done.
2054 mPendingExitListener = null;
2055 }
2056 };
2057
2058 final Transition exitTransition = transition.clone();
2059 exitTransition.addListener(mPendingExitListener);
2060
2061 final int count = getChildCount();
2062 for (int i = 0; i < count; i++) {
2063 final View child = getChildAt(i);
2064 exitTransition.addTarget(child);
2065 }
2066
2067 TransitionManager.beginDelayedTransition(this, exitTransition);
2068
2069 for (int i = 0; i < count; i++) {
2070 final View child = getChildAt(i);
2071 child.setVisibility(View.INVISIBLE);
2072 }
2073 }
2074
2075 /**
2076 * Cancels all pending or current transitions.
2077 */
2078 public void cancelTransitions() {
2079 TransitionManager.endTransitions(this);
2080
2081 if (mPendingExitListener != null) {
2082 mPendingExitListener.onTransitionEnd(null);
2083 }
2084 }
Alan Viverette5435a302015-01-29 10:25:34 -08002085 }
svetoslavganov75986cf2009-05-14 22:28:01 -07002086
Alan Viverette5435a302015-01-29 10:25:34 -08002087 private class PopupBackgroundView extends FrameLayout {
2088 public PopupBackgroundView(Context context) {
2089 super(context);
2090 }
2091
svetoslavganov75986cf2009-05-14 22:28:01 -07002092 @Override
Alan Viverette5435a302015-01-29 10:25:34 -08002093 protected int[] onCreateDrawableState(int extraSpace) {
2094 if (mAboveAnchor) {
2095 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
2096 View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET);
2097 return drawableState;
svetoslavganov75986cf2009-05-14 22:28:01 -07002098 } else {
Alan Viverette5435a302015-01-29 10:25:34 -08002099 return super.onCreateDrawableState(extraSpace);
svetoslavganov75986cf2009-05-14 22:28:01 -07002100 }
2101 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002102 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002103}