blob: be20d2d836f8b17e057fff1e8eaa80bf7a2bba28 [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.util.AttributeSet;
Adam Powellc3fa6302010-05-18 11:36:27 -070031import android.view.Gravity;
32import android.view.KeyEvent;
33import android.view.MotionEvent;
34import android.view.View;
Adam Powella7287f42010-08-17 21:17:04 -070035import android.view.View.OnTouchListener;
Adam Powellc3fa6302010-05-18 11:36:27 -070036import android.view.ViewGroup;
37import android.view.ViewTreeObserver;
Adam Powellc3fa6302010-05-18 11:36:27 -070038import android.view.ViewTreeObserver.OnScrollChangedListener;
Adam Powella7287f42010-08-17 21:17:04 -070039import android.view.WindowManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040
Adam Powella7287f42010-08-17 21:17:04 -070041import java.lang.ref.WeakReference;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042
43/**
44 * <p>A popup window that can be used to display an arbitrary view. The popup
Scott Kennedy7ed189e2013-01-11 22:31:43 -080045 * window is a floating container that appears on top of the current
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046 * activity.</p>
47 *
48 * @see android.widget.AutoCompleteTextView
49 * @see android.widget.Spinner
50 */
51public class PopupWindow {
52 /**
Romain Guye29f0642009-06-23 21:27:02 -070053 * Mode for {@link #setInputMethodMode(int)}: the requirements for the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054 * input method should be based on the focusability of the popup. That is
55 * if it is focusable than it needs to work with the input method, else
56 * it doesn't.
57 */
58 public static final int INPUT_METHOD_FROM_FOCUSABLE = 0;
59
60 /**
Romain Guye29f0642009-06-23 21:27:02 -070061 * Mode for {@link #setInputMethodMode(int)}: this popup always needs to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062 * work with an input method, regardless of whether it is focusable. This
63 * means that it will always be displayed so that the user can also operate
64 * the input method while it is shown.
65 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066 public static final int INPUT_METHOD_NEEDED = 1;
67
68 /**
Romain Guye29f0642009-06-23 21:27:02 -070069 * Mode for {@link #setInputMethodMode(int)}: this popup never needs to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 * work with an input method, regardless of whether it is focusable. This
71 * means that it will always be displayed to use as much space on the
72 * screen as needed, regardless of whether this covers the input method.
73 */
74 public static final int INPUT_METHOD_NOT_NEEDED = 2;
Adam Powell54c94de2013-09-26 15:36:34 -070075
76 private static final int DEFAULT_ANCHORED_GRAVITY = Gravity.TOP | Gravity.START;
77
Romain Guy448ecf52009-05-14 16:03:42 -070078 private Context mContext;
79 private WindowManager mWindowManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080
81 private boolean mIsShowing;
82 private boolean mIsDropdown;
83
84 private View mContentView;
85 private View mPopupView;
86 private boolean mFocusable;
87 private int mInputMethodMode = INPUT_METHOD_FROM_FOCUSABLE;
Dianne Hackborn7eab0942011-01-01 13:21:50 -080088 private int mSoftInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 private boolean mTouchable = true;
90 private boolean mOutsideTouchable = false;
91 private boolean mClippingEnabled = true;
Jeff Brown46e75292010-11-10 16:53:45 -080092 private int mSplitTouchEnabled = -1;
Adam Powellba0a2c32010-09-28 17:41:23 -070093 private boolean mLayoutInScreen;
Adam Powell56c2d332010-11-05 20:03:03 -070094 private boolean mClipToScreen;
Adam Powell348e69c2011-02-16 16:49:50 -080095 private boolean mAllowScrollingAnchorParent = true;
Adam Powell0bd1d0a2011-07-22 19:35:06 -070096 private boolean mLayoutInsetDecor = false;
Adam Powelle0b6cd12011-09-28 22:06:11 -070097 private boolean mNotTouchModal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098
99 private OnTouchListener mTouchInterceptor;
100
101 private int mWidthMode;
102 private int mWidth;
103 private int mLastWidth;
104 private int mHeightMode;
105 private int mHeight;
106 private int mLastHeight;
107
108 private int mPopupWidth;
109 private int mPopupHeight;
Adam Powell56c2d332010-11-05 20:03:03 -0700110
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 private int[] mDrawingLocation = new int[2];
112 private int[] mScreenLocation = new int[2];
113 private Rect mTempRect = new Rect();
114
115 private Drawable mBackground;
116 private Drawable mAboveAnchorBackgroundDrawable;
117 private Drawable mBelowAnchorBackgroundDrawable;
118
119 private boolean mAboveAnchor;
Adam Powell574b37e2010-10-07 11:15:19 -0700120 private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121
122 private OnDismissListener mOnDismissListener;
123 private boolean mIgnoreCheekPress = false;
124
125 private int mAnimationStyle = -1;
126
127 private static final int[] ABOVE_ANCHOR_STATE_SET = new int[] {
128 com.android.internal.R.attr.state_above_anchor
129 };
130
131 private WeakReference<View> mAnchor;
132 private OnScrollChangedListener mOnScrollChangedListener =
133 new OnScrollChangedListener() {
134 public void onScrollChanged() {
Adam Powell749b0eb2010-08-10 15:31:02 -0700135 View anchor = mAnchor != null ? mAnchor.get() : null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 if (anchor != null && mPopupView != null) {
137 WindowManager.LayoutParams p = (WindowManager.LayoutParams)
138 mPopupView.getLayoutParams();
139
Adam Powell54c94de2013-09-26 15:36:34 -0700140 updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
141 mAnchoredGravity));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800142 update(p.x, p.y, -1, -1, true);
143 }
144 }
145 };
Adam Powell54c94de2013-09-26 15:36:34 -0700146 private int mAnchorXoff, mAnchorYoff, mAnchoredGravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147
Fabrice Di Megliob003e282012-10-17 17:20:19 -0700148 private boolean mPopupViewInitialLayoutDirectionInherited;
149
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 /**
151 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
152 *
153 * <p>The popup does provide a background.</p>
154 */
155 public PopupWindow(Context context) {
156 this(context, null);
157 }
158
159 /**
160 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
161 *
162 * <p>The popup does provide a background.</p>
163 */
164 public PopupWindow(Context context, AttributeSet attrs) {
165 this(context, attrs, com.android.internal.R.attr.popupWindowStyle);
166 }
167
168 /**
169 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
170 *
171 * <p>The popup does provide a background.</p>
172 */
173 public PopupWindow(Context context, AttributeSet attrs, int defStyle) {
Adam Powellc3fa6302010-05-18 11:36:27 -0700174 this(context, attrs, defStyle, 0);
175 }
176
177 /**
178 * <p>Create a new, empty, non focusable popup window of dimension (0,0).</p>
179 *
180 * <p>The popup does not provide a background.</p>
181 */
182 public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 mContext = context;
Romain Guy448ecf52009-05-14 16:03:42 -0700184 mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185
186 TypedArray a =
187 context.obtainStyledAttributes(
Adam Powellc3fa6302010-05-18 11:36:27 -0700188 attrs, com.android.internal.R.styleable.PopupWindow, defStyleAttr, defStyleRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189
190 mBackground = a.getDrawable(R.styleable.PopupWindow_popupBackground);
Adam Powellc3808b52010-10-04 10:06:59 -0700191
192 final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, -1);
193 mAnimationStyle = animStyle == com.android.internal.R.style.Animation_PopupWindow ? -1 :
194 animStyle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195
196 // If this is a StateListDrawable, try to find and store the drawable to be
197 // used when the drop-down is placed above its anchor view, and the one to be
198 // used when the drop-down is placed below its anchor view. We extract
199 // the drawables ourselves to work around a problem with using refreshDrawableState
200 // that it will take into account the padding of all drawables specified in a
201 // StateListDrawable, thus adding superfluous padding to drop-down views.
202 //
203 // We assume a StateListDrawable will have a drawable for ABOVE_ANCHOR_STATE_SET and
204 // at least one other drawable, intended for the 'below-anchor state'.
205 if (mBackground instanceof StateListDrawable) {
206 StateListDrawable background = (StateListDrawable) mBackground;
207
208 // Find the above-anchor view - this one's easy, it should be labeled as such.
209 int aboveAnchorStateIndex = background.getStateDrawableIndex(ABOVE_ANCHOR_STATE_SET);
210
211 // Now, for the below-anchor view, look for any other drawable specified in the
212 // StateListDrawable which is not for the above-anchor state and use that.
213 int count = background.getStateCount();
214 int belowAnchorStateIndex = -1;
215 for (int i = 0; i < count; i++) {
216 if (i != aboveAnchorStateIndex) {
217 belowAnchorStateIndex = i;
218 break;
219 }
220 }
221
222 // Store the drawables we found, if we found them. Otherwise, set them both
223 // to null so that we'll just use refreshDrawableState.
224 if (aboveAnchorStateIndex != -1 && belowAnchorStateIndex != -1) {
225 mAboveAnchorBackgroundDrawable = background.getStateDrawable(aboveAnchorStateIndex);
226 mBelowAnchorBackgroundDrawable = background.getStateDrawable(belowAnchorStateIndex);
227 } else {
228 mBelowAnchorBackgroundDrawable = null;
229 mAboveAnchorBackgroundDrawable = null;
230 }
231 }
232
233 a.recycle();
234 }
235
236 /**
237 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
238 *
239 * <p>The popup does not provide any background. This should be handled
240 * by the content view.</p>
241 */
242 public PopupWindow() {
243 this(null, 0, 0);
244 }
245
246 /**
247 * <p>Create a new non focusable popup window which can display the
248 * <tt>contentView</tt>. The dimension of the window are (0,0).</p>
249 *
250 * <p>The popup does not provide any background. This should be handled
251 * by the content view.</p>
252 *
253 * @param contentView the popup's content
254 */
255 public PopupWindow(View contentView) {
256 this(contentView, 0, 0);
257 }
258
259 /**
260 * <p>Create a new empty, non focusable popup window. The dimension of the
261 * window must be passed to this constructor.</p>
262 *
263 * <p>The popup does not provide any background. This should be handled
264 * by the content view.</p>
265 *
266 * @param width the popup's width
267 * @param height the popup's height
268 */
269 public PopupWindow(int width, int height) {
270 this(null, width, height);
271 }
272
273 /**
274 * <p>Create a new non focusable popup window which can display the
275 * <tt>contentView</tt>. The dimension of the window must be passed to
276 * this constructor.</p>
277 *
278 * <p>The popup does not provide any background. This should be handled
279 * by the content view.</p>
280 *
281 * @param contentView the popup's content
282 * @param width the popup's width
283 * @param height the popup's height
284 */
285 public PopupWindow(View contentView, int width, int height) {
286 this(contentView, width, height, false);
287 }
288
289 /**
290 * <p>Create a new popup window which can display the <tt>contentView</tt>.
291 * The dimension of the window must be passed to this constructor.</p>
292 *
293 * <p>The popup does not provide any background. This should be handled
294 * by the content view.</p>
295 *
296 * @param contentView the popup's content
297 * @param width the popup's width
298 * @param height the popup's height
299 * @param focusable true if the popup can be focused, false otherwise
300 */
Romain Guy448ecf52009-05-14 16:03:42 -0700301 public PopupWindow(View contentView, int width, int height, boolean focusable) {
302 if (contentView != null) {
303 mContext = contentView.getContext();
304 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
305 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 setContentView(contentView);
307 setWidth(width);
308 setHeight(height);
309 setFocusable(focusable);
310 }
311
312 /**
313 * <p>Return the drawable used as the popup window's background.</p>
314 *
315 * @return the background drawable or null
316 */
317 public Drawable getBackground() {
318 return mBackground;
319 }
320
321 /**
322 * <p>Change the background drawable for this popup window. The background
323 * can be set to null.</p>
324 *
325 * @param background the popup's background
326 */
327 public void setBackgroundDrawable(Drawable background) {
328 mBackground = background;
329 }
330
331 /**
332 * <p>Return the animation style to use the popup appears and disappears</p>
333 *
334 * @return the animation style to use the popup appears and disappears
335 */
336 public int getAnimationStyle() {
337 return mAnimationStyle;
338 }
339
340 /**
341 * Set the flag on popup to ignore cheek press eventt; by default this flag
342 * is set to false
343 * which means the pop wont ignore cheek press dispatch events.
344 *
345 * <p>If the popup is showing, calling this method will take effect only
346 * the next time the popup is shown or through a manual call to one of
347 * the {@link #update()} methods.</p>
348 *
349 * @see #update()
350 */
351 public void setIgnoreCheekPress() {
352 mIgnoreCheekPress = true;
353 }
354
355
356 /**
357 * <p>Change the animation style resource for this popup.</p>
358 *
359 * <p>If the popup is showing, calling this method will take effect only
360 * the next time the popup is shown or through a manual call to one of
361 * the {@link #update()} methods.</p>
362 *
363 * @param animationStyle animation style to use when the popup appears
364 * and disappears. Set to -1 for the default animation, 0 for no
365 * animation, or a resource identifier for an explicit animation.
366 *
367 * @see #update()
368 */
369 public void setAnimationStyle(int animationStyle) {
370 mAnimationStyle = animationStyle;
371 }
372
373 /**
374 * <p>Return the view used as the content of the popup window.</p>
375 *
376 * @return a {@link android.view.View} representing the popup's content
377 *
378 * @see #setContentView(android.view.View)
379 */
380 public View getContentView() {
381 return mContentView;
382 }
383
384 /**
385 * <p>Change the popup's content. The content is represented by an instance
386 * of {@link android.view.View}.</p>
387 *
Gilles Debunne81f08082011-02-17 14:07:19 -0800388 * <p>This method has no effect if called when the popup is showing.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 *
390 * @param contentView the new content for the popup
391 *
392 * @see #getContentView()
393 * @see #isShowing()
394 */
395 public void setContentView(View contentView) {
396 if (isShowing()) {
397 return;
398 }
399
400 mContentView = contentView;
Romain Guy448ecf52009-05-14 16:03:42 -0700401
Romain Guy0c0b7682011-05-16 11:54:09 -0700402 if (mContext == null && mContentView != null) {
Romain Guy448ecf52009-05-14 16:03:42 -0700403 mContext = mContentView.getContext();
404 }
405
Romain Guy0c0b7682011-05-16 11:54:09 -0700406 if (mWindowManager == null && mContentView != null) {
Romain Guy448ecf52009-05-14 16:03:42 -0700407 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
408 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 }
410
411 /**
412 * Set a callback for all touch events being dispatched to the popup
413 * window.
414 */
415 public void setTouchInterceptor(OnTouchListener l) {
416 mTouchInterceptor = l;
417 }
418
419 /**
420 * <p>Indicate whether the popup window can grab the focus.</p>
421 *
422 * @return true if the popup is focusable, false otherwise
423 *
424 * @see #setFocusable(boolean)
425 */
426 public boolean isFocusable() {
427 return mFocusable;
428 }
429
430 /**
431 * <p>Changes the focusability of the popup window. When focusable, the
432 * window will grab the focus from the current focused widget if the popup
433 * contains a focusable {@link android.view.View}. By default a popup
434 * window is not focusable.</p>
435 *
436 * <p>If the popup is showing, calling this method will take effect only
437 * the next time the popup is shown or through a manual call to one of
438 * the {@link #update()} methods.</p>
439 *
440 * @param focusable true if the popup should grab focus, false otherwise.
441 *
442 * @see #isFocusable()
443 * @see #isShowing()
444 * @see #update()
445 */
446 public void setFocusable(boolean focusable) {
447 mFocusable = focusable;
448 }
449
450 /**
451 * Return the current value in {@link #setInputMethodMode(int)}.
452 *
453 * @see #setInputMethodMode(int)
454 */
455 public int getInputMethodMode() {
456 return mInputMethodMode;
457
458 }
459
460 /**
461 * Control how the popup operates with an input method: one of
462 * {@link #INPUT_METHOD_FROM_FOCUSABLE}, {@link #INPUT_METHOD_NEEDED},
463 * or {@link #INPUT_METHOD_NOT_NEEDED}.
464 *
465 * <p>If the popup is showing, calling this method will take effect only
466 * the next time the popup is shown or through a manual call to one of
467 * the {@link #update()} methods.</p>
468 *
469 * @see #getInputMethodMode()
470 * @see #update()
471 */
472 public void setInputMethodMode(int mode) {
473 mInputMethodMode = mode;
474 }
Romain Guy374aaaed32009-07-14 15:11:59 -0700475
476 /**
477 * Sets the operating mode for the soft input area.
478 *
479 * @param mode The desired mode, see
480 * {@link android.view.WindowManager.LayoutParams#softInputMode}
481 * for the full list
482 *
483 * @see android.view.WindowManager.LayoutParams#softInputMode
484 * @see #getSoftInputMode()
485 */
486 public void setSoftInputMode(int mode) {
487 mSoftInputMode = mode;
488 }
489
490 /**
491 * Returns the current value in {@link #setSoftInputMode(int)}.
492 *
493 * @see #setSoftInputMode(int)
494 * @see android.view.WindowManager.LayoutParams#softInputMode
495 */
496 public int getSoftInputMode() {
497 return mSoftInputMode;
498 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499
500 /**
501 * <p>Indicates whether the popup window receives touch events.</p>
502 *
503 * @return true if the popup is touchable, false otherwise
504 *
505 * @see #setTouchable(boolean)
506 */
507 public boolean isTouchable() {
508 return mTouchable;
509 }
510
511 /**
512 * <p>Changes the touchability of the popup window. When touchable, the
513 * window will receive touch events, otherwise touch events will go to the
514 * window below it. By default the window is touchable.</p>
515 *
516 * <p>If the popup is showing, calling this method will take effect only
517 * the next time the popup is shown or through a manual call to one of
518 * the {@link #update()} methods.</p>
519 *
520 * @param touchable true if the popup should receive touch events, false otherwise
521 *
522 * @see #isTouchable()
523 * @see #isShowing()
524 * @see #update()
525 */
526 public void setTouchable(boolean touchable) {
527 mTouchable = touchable;
528 }
529
530 /**
531 * <p>Indicates whether the popup window will be informed of touch events
532 * outside of its window.</p>
533 *
534 * @return true if the popup is outside touchable, false otherwise
535 *
536 * @see #setOutsideTouchable(boolean)
537 */
538 public boolean isOutsideTouchable() {
539 return mOutsideTouchable;
540 }
541
542 /**
543 * <p>Controls whether the pop-up will be informed of touch events outside
544 * of its window. This only makes sense for pop-ups that are touchable
545 * but not focusable, which means touches outside of the window will
546 * be delivered to the window behind. The default is false.</p>
547 *
548 * <p>If the popup is showing, calling this method will take effect only
549 * the next time the popup is shown or through a manual call to one of
550 * the {@link #update()} methods.</p>
551 *
552 * @param touchable true if the popup should receive outside
553 * touch events, false otherwise
554 *
555 * @see #isOutsideTouchable()
556 * @see #isShowing()
557 * @see #update()
558 */
559 public void setOutsideTouchable(boolean touchable) {
560 mOutsideTouchable = touchable;
561 }
562
563 /**
564 * <p>Indicates whether clipping of the popup window is enabled.</p>
565 *
566 * @return true if the clipping is enabled, false otherwise
567 *
568 * @see #setClippingEnabled(boolean)
569 */
570 public boolean isClippingEnabled() {
571 return mClippingEnabled;
572 }
573
574 /**
575 * <p>Allows the popup window to extend beyond the bounds of the screen. By default the
576 * window is clipped to the screen boundaries. Setting this to false will allow windows to be
577 * accurately positioned.</p>
578 *
579 * <p>If the popup is showing, calling this method will take effect only
580 * the next time the popup is shown or through a manual call to one of
581 * the {@link #update()} methods.</p>
582 *
583 * @param enabled false if the window should be allowed to extend outside of the screen
584 * @see #isShowing()
585 * @see #isClippingEnabled()
586 * @see #update()
587 */
588 public void setClippingEnabled(boolean enabled) {
589 mClippingEnabled = enabled;
590 }
591
592 /**
Adam Powell56c2d332010-11-05 20:03:03 -0700593 * Clip this popup window to the screen, but not to the containing window.
594 *
595 * @param enabled True to clip to the screen.
596 * @hide
597 */
598 public void setClipToScreenEnabled(boolean enabled) {
599 mClipToScreen = enabled;
600 setClippingEnabled(!enabled);
601 }
Adam Powell348e69c2011-02-16 16:49:50 -0800602
603 /**
604 * Allow PopupWindow to scroll the anchor's parent to provide more room
605 * for the popup. Enabled by default.
606 *
607 * @param enabled True to scroll the anchor's parent when more room is desired by the popup.
608 */
609 void setAllowScrollingAnchorParent(boolean enabled) {
610 mAllowScrollingAnchorParent = enabled;
611 }
Adam Powell56c2d332010-11-05 20:03:03 -0700612
613 /**
Jeff Brown01ce2e92010-09-26 22:20:12 -0700614 * <p>Indicates whether the popup window supports splitting touches.</p>
615 *
616 * @return true if the touch splitting is enabled, false otherwise
617 *
618 * @see #setSplitTouchEnabled(boolean)
Jeff Brown01ce2e92010-09-26 22:20:12 -0700619 */
620 public boolean isSplitTouchEnabled() {
Jeff Brown46e75292010-11-10 16:53:45 -0800621 if (mSplitTouchEnabled < 0 && mContext != null) {
622 return mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB;
623 }
624 return mSplitTouchEnabled == 1;
Jeff Brown01ce2e92010-09-26 22:20:12 -0700625 }
626
627 /**
628 * <p>Allows the popup window to split touches across other windows that also
Jeff Brown46e75292010-11-10 16:53:45 -0800629 * support split touch. When this flag is false, the first pointer
Jeff Brown01ce2e92010-09-26 22:20:12 -0700630 * that goes down determines the window to which all subsequent touches
Jeff Brown46e75292010-11-10 16:53:45 -0800631 * go until all pointers go up. When this flag is true, each pointer
Jeff Brown01ce2e92010-09-26 22:20:12 -0700632 * (not necessarily the first) that goes down determines the window
633 * to which all subsequent touches of that pointer will go until that
634 * pointer goes up thereby enabling touches with multiple pointers
635 * to be split across multiple windows.</p>
636 *
637 * @param enabled true if the split touches should be enabled, false otherwise
638 * @see #isSplitTouchEnabled()
Jeff Brown01ce2e92010-09-26 22:20:12 -0700639 */
640 public void setSplitTouchEnabled(boolean enabled) {
Jeff Brown46e75292010-11-10 16:53:45 -0800641 mSplitTouchEnabled = enabled ? 1 : 0;
Jeff Brown01ce2e92010-09-26 22:20:12 -0700642 }
643
644 /**
Adam Powellba0a2c32010-09-28 17:41:23 -0700645 * <p>Indicates whether the popup window will be forced into using absolute screen coordinates
646 * for positioning.</p>
647 *
648 * @return true if the window will always be positioned in screen coordinates.
649 * @hide
650 */
651 public boolean isLayoutInScreenEnabled() {
652 return mLayoutInScreen;
653 }
654
655 /**
656 * <p>Allows the popup window to force the flag
657 * {@link WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN}, overriding default behavior.
658 * This will cause the popup to be positioned in absolute screen coordinates.</p>
659 *
660 * @param enabled true if the popup should always be positioned in screen coordinates
661 * @hide
662 */
663 public void setLayoutInScreenEnabled(boolean enabled) {
664 mLayoutInScreen = enabled;
665 }
666
667 /**
Adam Powell0bd1d0a2011-07-22 19:35:06 -0700668 * Allows the popup window to force the flag
669 * {@link WindowManager.LayoutParams#FLAG_LAYOUT_INSET_DECOR}, overriding default behavior.
670 * This will cause the popup to inset its content to account for system windows overlaying
671 * the screen, such as the status bar.
672 *
673 * <p>This will often be combined with {@link #setLayoutInScreenEnabled(boolean)}.
674 *
675 * @param enabled true if the popup's views should inset content to account for system windows,
676 * the way that decor views behave for full-screen windows.
677 * @hide
678 */
679 public void setLayoutInsetDecor(boolean enabled) {
680 mLayoutInsetDecor = enabled;
681 }
682
683 /**
Adam Powell574b37e2010-10-07 11:15:19 -0700684 * Set the layout type for this window. Should be one of the TYPE constants defined in
685 * {@link WindowManager.LayoutParams}.
686 *
687 * @param layoutType Layout type for this window.
688 * @hide
689 */
690 public void setWindowLayoutType(int layoutType) {
691 mWindowLayoutType = layoutType;
692 }
693
694 /**
695 * @return The layout type for this window.
696 * @hide
697 */
698 public int getWindowLayoutType() {
699 return mWindowLayoutType;
700 }
701
702 /**
Adam Powelle0b6cd12011-09-28 22:06:11 -0700703 * Set whether this window is touch modal or if outside touches will be sent to
704 * other windows behind it.
705 * @hide
706 */
707 public void setTouchModal(boolean touchModal) {
708 mNotTouchModal = !touchModal;
709 }
710
711 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712 * <p>Change the width and height measure specs that are given to the
713 * window manager by the popup. By default these are 0, meaning that
714 * the current width or height is requested as an explicit size from
715 * the window manager. You can supply
716 * {@link ViewGroup.LayoutParams#WRAP_CONTENT} or
Romain Guy980a9382010-01-08 15:06:28 -0800717 * {@link ViewGroup.LayoutParams#MATCH_PARENT} to have that measure
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 * spec supplied instead, replacing the absolute width and height that
719 * has been set in the popup.</p>
720 *
721 * <p>If the popup is showing, calling this method will take effect only
722 * the next time the popup is shown.</p>
723 *
724 * @param widthSpec an explicit width measure spec mode, either
725 * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
Romain Guy980a9382010-01-08 15:06:28 -0800726 * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800727 * width.
728 * @param heightSpec an explicit height measure spec mode, either
729 * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
Romain Guy980a9382010-01-08 15:06:28 -0800730 * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800731 * height.
732 */
733 public void setWindowLayoutMode(int widthSpec, int heightSpec) {
734 mWidthMode = widthSpec;
735 mHeightMode = heightSpec;
736 }
737
738 /**
739 * <p>Return this popup's height MeasureSpec</p>
740 *
741 * @return the height MeasureSpec of the popup
742 *
743 * @see #setHeight(int)
744 */
745 public int getHeight() {
746 return mHeight;
747 }
748
749 /**
750 * <p>Change the popup's height MeasureSpec</p>
751 *
752 * <p>If the popup is showing, calling this method will take effect only
753 * the next time the popup is shown.</p>
754 *
755 * @param height the height MeasureSpec of the popup
756 *
757 * @see #getHeight()
758 * @see #isShowing()
759 */
760 public void setHeight(int height) {
761 mHeight = height;
762 }
763
764 /**
765 * <p>Return this popup's width MeasureSpec</p>
766 *
767 * @return the width MeasureSpec of the popup
768 *
769 * @see #setWidth(int)
770 */
771 public int getWidth() {
772 return mWidth;
773 }
774
775 /**
776 * <p>Change the popup's width MeasureSpec</p>
777 *
778 * <p>If the popup is showing, calling this method will take effect only
779 * the next time the popup is shown.</p>
780 *
781 * @param width the width MeasureSpec of the popup
782 *
783 * @see #getWidth()
784 * @see #isShowing()
785 */
786 public void setWidth(int width) {
787 mWidth = width;
788 }
789
790 /**
791 * <p>Indicate whether this popup window is showing on screen.</p>
792 *
793 * @return true if the popup is showing, false otherwise
794 */
795 public boolean isShowing() {
796 return mIsShowing;
797 }
798
799 /**
800 * <p>
801 * Display the content view in a popup window at the specified location. If the popup window
802 * cannot fit on screen, it will be clipped. See {@link android.view.WindowManager.LayoutParams}
803 * for more information on how gravity and the x and y parameters are related. Specifying
804 * a gravity of {@link android.view.Gravity#NO_GRAVITY} is similar to specifying
805 * <code>Gravity.LEFT | Gravity.TOP</code>.
806 * </p>
807 *
808 * @param parent a parent view to get the {@link android.view.View#getWindowToken()} token from
809 * @param gravity the gravity which controls the placement of the popup window
810 * @param x the popup's x location offset
811 * @param y the popup's y location offset
812 */
813 public void showAtLocation(View parent, int gravity, int x, int y) {
Adam Powell8ee6d7c2011-09-18 14:59:28 -0700814 showAtLocation(parent.getWindowToken(), gravity, x, y);
815 }
816
817 /**
818 * Display the content view in a popup window at the specified location.
819 *
820 * @param token Window token to use for creating the new window
821 * @param gravity the gravity which controls the placement of the popup window
822 * @param x the popup's x location offset
823 * @param y the popup's y location offset
824 *
825 * @hide Internal use only. Applications should use
826 * {@link #showAtLocation(View, int, int, int)} instead.
827 */
828 public void showAtLocation(IBinder token, int gravity, int x, int y) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800829 if (isShowing() || mContentView == null) {
830 return;
831 }
832
833 unregisterForScrollChanged();
834
835 mIsShowing = true;
836 mIsDropdown = false;
837
Adam Powell8ee6d7c2011-09-18 14:59:28 -0700838 WindowManager.LayoutParams p = createPopupLayout(token);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800839 p.windowAnimations = computeAnimationResource();
840
841 preparePopup(p);
842 if (gravity == Gravity.NO_GRAVITY) {
Fabrice Di Meglioaac0d4e2012-07-19 19:21:26 -0700843 gravity = Gravity.TOP | Gravity.START;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800844 }
845 p.gravity = gravity;
846 p.x = x;
847 p.y = y;
Dianne Hackbornab36acb2010-11-05 14:12:11 -0700848 if (mHeightMode < 0) p.height = mLastHeight = mHeightMode;
849 if (mWidthMode < 0) p.width = mLastWidth = mWidthMode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800850 invokePopup(p);
851 }
852
853 /**
854 * <p>Display the content view in a popup window anchored to the bottom-left
855 * corner of the anchor view. If there is not enough room on screen to show
856 * the popup in its entirety, this method tries to find a parent scroll
857 * view to scroll. If no parent scroll view can be scrolled, the bottom-left
858 * corner of the popup is pinned at the top left corner of the anchor view.</p>
859 *
860 * @param anchor the view on which to pin the popup window
861 *
862 * @see #dismiss()
863 */
864 public void showAsDropDown(View anchor) {
865 showAsDropDown(anchor, 0, 0);
866 }
867
868 /**
869 * <p>Display the content view in a popup window anchored to the bottom-left
870 * corner of the anchor view offset by the specified x and y coordinates.
871 * If there is not enough room on screen to show
872 * the popup in its entirety, this method tries to find a parent scroll
873 * view to scroll. If no parent scroll view can be scrolled, the bottom-left
874 * corner of the popup is pinned at the top left corner of the anchor view.</p>
875 * <p>If the view later scrolls to move <code>anchor</code> to a different
876 * location, the popup will be moved correspondingly.</p>
877 *
878 * @param anchor the view on which to pin the popup window
Adam Powell54c94de2013-09-26 15:36:34 -0700879 * @param xoff A horizontal offset from the anchor in pixels
880 * @param yoff A vertical offset from the anchor in pixels
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881 *
882 * @see #dismiss()
883 */
884 public void showAsDropDown(View anchor, int xoff, int yoff) {
Adam Powell54c94de2013-09-26 15:36:34 -0700885 showAsDropDown(anchor, xoff, yoff, DEFAULT_ANCHORED_GRAVITY);
886 }
887
888 /**
889 * <p>Display the content view in a popup window anchored to the bottom-left
890 * corner of the anchor view offset by the specified x and y coordinates.
891 * If there is not enough room on screen to show
892 * the popup in its entirety, this method tries to find a parent scroll
893 * view to scroll. If no parent scroll view can be scrolled, the bottom-left
894 * corner of the popup is pinned at the top left corner of the anchor view.</p>
895 * <p>If the view later scrolls to move <code>anchor</code> to a different
896 * location, the popup will be moved correspondingly.</p>
897 *
898 * @param anchor the view on which to pin the popup window
899 * @param xoff A horizontal offset from the anchor in pixels
900 * @param yoff A vertical offset from the anchor in pixels
901 * @param gravity Alignment of the popup relative to the anchor
902 *
903 * @see #dismiss()
904 */
905 public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906 if (isShowing() || mContentView == null) {
907 return;
908 }
909
Adam Powell54c94de2013-09-26 15:36:34 -0700910 registerForScrollChanged(anchor, xoff, yoff, gravity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800911
912 mIsShowing = true;
913 mIsDropdown = true;
914
915 WindowManager.LayoutParams p = createPopupLayout(anchor.getWindowToken());
916 preparePopup(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800917
Adam Powell54c94de2013-09-26 15:36:34 -0700918 updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff, gravity));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919
920 if (mHeightMode < 0) p.height = mLastHeight = mHeightMode;
921 if (mWidthMode < 0) p.width = mLastWidth = mWidthMode;
922
923 p.windowAnimations = computeAnimationResource();
924
925 invokePopup(p);
926 }
927
Romain Guy3e141682010-03-08 17:44:40 -0800928 private void updateAboveAnchor(boolean aboveAnchor) {
929 if (aboveAnchor != mAboveAnchor) {
930 mAboveAnchor = aboveAnchor;
931
932 if (mBackground != null) {
933 // If the background drawable provided was a StateListDrawable with above-anchor
934 // and below-anchor states, use those. Otherwise rely on refreshDrawableState to
935 // do the job.
936 if (mAboveAnchorBackgroundDrawable != null) {
937 if (mAboveAnchor) {
938 mPopupView.setBackgroundDrawable(mAboveAnchorBackgroundDrawable);
939 } else {
940 mPopupView.setBackgroundDrawable(mBelowAnchorBackgroundDrawable);
941 }
942 } else {
943 mPopupView.refreshDrawableState();
944 }
945 }
946 }
947 }
948
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800949 /**
950 * Indicates whether the popup is showing above (the y coordinate of the popup's bottom
951 * is less than the y coordinate of the anchor) or below the anchor view (the y coordinate
952 * of the popup is greater than y coordinate of the anchor's bottom).
953 *
954 * The value returned
955 * by this method is meaningful only after {@link #showAsDropDown(android.view.View)}
956 * or {@link #showAsDropDown(android.view.View, int, int)} was invoked.
957 *
958 * @return True if this popup is showing above the anchor view, false otherwise.
959 */
960 public boolean isAboveAnchor() {
961 return mAboveAnchor;
962 }
963
964 /**
965 * <p>Prepare the popup by embedding in into a new ViewGroup if the
966 * background drawable is not null. If embedding is required, the layout
967 * parameters' height is mnodified to take into account the background's
968 * padding.</p>
969 *
970 * @param p the layout parameters of the popup's content view
971 */
972 private void preparePopup(WindowManager.LayoutParams p) {
Romain Guy448ecf52009-05-14 16:03:42 -0700973 if (mContentView == null || mContext == null || mWindowManager == null) {
974 throw new IllegalStateException("You must specify a valid content view by "
975 + "calling setContentView() before attempting to show the popup.");
976 }
977
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800978 if (mBackground != null) {
979 final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
Romain Guy980a9382010-01-08 15:06:28 -0800980 int height = ViewGroup.LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800981 if (layoutParams != null &&
982 layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
983 height = ViewGroup.LayoutParams.WRAP_CONTENT;
984 }
985
986 // when a background is available, we embed the content view
987 // within another view that owns the background drawable
988 PopupViewContainer popupViewContainer = new PopupViewContainer(mContext);
989 PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -0800990 ViewGroup.LayoutParams.MATCH_PARENT, height
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800991 );
992 popupViewContainer.setBackgroundDrawable(mBackground);
993 popupViewContainer.addView(mContentView, listParams);
994
995 mPopupView = popupViewContainer;
996 } else {
997 mPopupView = mContentView;
998 }
Fabrice Di Megliob003e282012-10-17 17:20:19 -0700999 mPopupViewInitialLayoutDirectionInherited =
1000 (mPopupView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001001 mPopupWidth = p.width;
1002 mPopupHeight = p.height;
1003 }
1004
1005 /**
1006 * <p>Invoke the popup window by adding the content view to the window
1007 * manager.</p>
1008 *
1009 * <p>The content view must be non-null when this method is invoked.</p>
1010 *
1011 * @param p the layout parameters of the popup's content view
1012 */
1013 private void invokePopup(WindowManager.LayoutParams p) {
Romain Guy0c0b7682011-05-16 11:54:09 -07001014 if (mContext != null) {
1015 p.packageName = mContext.getPackageName();
1016 }
Adam Powell0bd1d0a2011-07-22 19:35:06 -07001017 mPopupView.setFitsSystemWindows(mLayoutInsetDecor);
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001018 setLayoutDirectionFromAnchor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019 mWindowManager.addView(mPopupView, p);
1020 }
1021
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001022 private void setLayoutDirectionFromAnchor() {
1023 if (mAnchor != null) {
1024 View anchor = mAnchor.get();
1025 if (anchor != null && mPopupViewInitialLayoutDirectionInherited) {
1026 mPopupView.setLayoutDirection(anchor.getLayoutDirection());
1027 }
1028 }
1029 }
1030
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001031 /**
1032 * <p>Generate the layout parameters for the popup window.</p>
1033 *
1034 * @param token the window token used to bind the popup's window
1035 *
1036 * @return the layout parameters to pass to the window manager
1037 */
1038 private WindowManager.LayoutParams createPopupLayout(IBinder token) {
1039 // generates the layout parameters for the drop down
1040 // we want a fixed size view located at the bottom left of the anchor
1041 WindowManager.LayoutParams p = new WindowManager.LayoutParams();
1042 // these gravity settings put the view at the top left corner of the
1043 // screen. The view is then positioned to the appropriate location
1044 // by setting the x and y offsets to match the anchor's bottom
1045 // left corner
Fabrice Di Meglioaac0d4e2012-07-19 19:21:26 -07001046 p.gravity = Gravity.START | Gravity.TOP;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001047 p.width = mLastWidth = mWidth;
1048 p.height = mLastHeight = mHeight;
1049 if (mBackground != null) {
1050 p.format = mBackground.getOpacity();
1051 } else {
1052 p.format = PixelFormat.TRANSLUCENT;
1053 }
1054 p.flags = computeFlags(p.flags);
Adam Powell574b37e2010-10-07 11:15:19 -07001055 p.type = mWindowLayoutType;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 p.token = token;
Romain Guy374aaaed32009-07-14 15:11:59 -07001057 p.softInputMode = mSoftInputMode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001058 p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
1059
1060 return p;
1061 }
1062
1063 private int computeFlags(int curFlags) {
1064 curFlags &= ~(
1065 WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES |
1066 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
1067 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
1068 WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
1069 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
Adam Powellba0a2c32010-09-28 17:41:23 -07001070 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
1071 WindowManager.LayoutParams.FLAG_SPLIT_TOUCH);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001072 if(mIgnoreCheekPress) {
1073 curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
1074 }
1075 if (!mFocusable) {
1076 curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1077 if (mInputMethodMode == INPUT_METHOD_NEEDED) {
1078 curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1079 }
1080 } else if (mInputMethodMode == INPUT_METHOD_NOT_NEEDED) {
1081 curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1082 }
1083 if (!mTouchable) {
1084 curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
1085 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001086 if (mOutsideTouchable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001087 curFlags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
1088 }
1089 if (!mClippingEnabled) {
1090 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
1091 }
Jeff Brown46e75292010-11-10 16:53:45 -08001092 if (isSplitTouchEnabled()) {
Jeff Brown01ce2e92010-09-26 22:20:12 -07001093 curFlags |= WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
1094 }
Adam Powellba0a2c32010-09-28 17:41:23 -07001095 if (mLayoutInScreen) {
1096 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
1097 }
Adam Powell0bd1d0a2011-07-22 19:35:06 -07001098 if (mLayoutInsetDecor) {
1099 curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
1100 }
Adam Powelle0b6cd12011-09-28 22:06:11 -07001101 if (mNotTouchModal) {
1102 curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
1103 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001104 return curFlags;
1105 }
1106
1107 private int computeAnimationResource() {
1108 if (mAnimationStyle == -1) {
1109 if (mIsDropdown) {
1110 return mAboveAnchor
1111 ? com.android.internal.R.style.Animation_DropDownUp
1112 : com.android.internal.R.style.Animation_DropDownDown;
1113 }
1114 return 0;
1115 }
1116 return mAnimationStyle;
1117 }
1118
1119 /**
1120 * <p>Positions the popup window on screen. When the popup window is too
1121 * tall to fit under the anchor, a parent scroll view is seeked and scrolled
1122 * up to reclaim space. If scrolling is not possible or not enough, the
1123 * popup window gets moved on top of the anchor.</p>
1124 *
1125 * <p>The height must have been set on the layout parameters prior to
1126 * calling this method.</p>
1127 *
1128 * @param anchor the view on which the popup window must be anchored
1129 * @param p the layout parameters used to display the drop down
1130 *
1131 * @return true if the popup is translated upwards to fit on screen
1132 */
Gilles Debunnecfc22c52011-03-07 15:50:47 -08001133 private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p,
Adam Powell54c94de2013-09-26 15:36:34 -07001134 int xoff, int yoff, int gravity) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001135
Adam Powell62e2bde2011-08-15 15:50:05 -07001136 final int anchorHeight = anchor.getHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001137 anchor.getLocationInWindow(mDrawingLocation);
1138 p.x = mDrawingLocation[0] + xoff;
Adam Powell62e2bde2011-08-15 15:50:05 -07001139 p.y = mDrawingLocation[1] + anchorHeight + yoff;
Adam Powell54c94de2013-09-26 15:36:34 -07001140
1141 final int hgrav = Gravity.getAbsoluteGravity(gravity, anchor.getLayoutDirection()) &
1142 Gravity.HORIZONTAL_GRAVITY_MASK;
1143 if (hgrav == Gravity.RIGHT) {
1144 // Flip the location to align the right sides of the popup and anchor instead of left
1145 p.x -= mPopupWidth - anchor.getWidth();
1146 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001147
1148 boolean onTop = false;
1149
Adam Powell54c94de2013-09-26 15:36:34 -07001150 p.gravity = Gravity.LEFT | Gravity.TOP;
1151
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001152 anchor.getLocationOnScreen(mScreenLocation);
1153 final Rect displayFrame = new Rect();
1154 anchor.getWindowVisibleDisplayFrame(displayFrame);
Adam Powell62e2bde2011-08-15 15:50:05 -07001155
1156 int screenY = mScreenLocation[1] + anchorHeight + yoff;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001157
1158 final View root = anchor.getRootView();
Adam Powell62e2bde2011-08-15 15:50:05 -07001159 if (screenY + mPopupHeight > displayFrame.bottom ||
1160 p.x + mPopupWidth - root.getWidth() > 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001161 // if the drop down disappears at the bottom of the screen. we try to
1162 // scroll a parent scrollview or move the drop down back up on top of
1163 // the edit box
Adam Powellb7c1b202011-02-17 12:03:09 -08001164 if (mAllowScrollingAnchorParent) {
1165 int scrollX = anchor.getScrollX();
1166 int scrollY = anchor.getScrollY();
1167 Rect r = new Rect(scrollX, scrollY, scrollX + mPopupWidth + xoff,
1168 scrollY + mPopupHeight + anchor.getHeight() + yoff);
1169 anchor.requestRectangleOnScreen(r, true);
1170 }
Romain Guy3e141682010-03-08 17:44:40 -08001171
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001172 // now we re-evaluate the space available, and decide from that
1173 // whether the pop-up will go above or below the anchor.
1174 anchor.getLocationInWindow(mDrawingLocation);
1175 p.x = mDrawingLocation[0] + xoff;
Romain Guy3e141682010-03-08 17:44:40 -08001176 p.y = mDrawingLocation[1] + anchor.getHeight() + yoff;
Adam Powell54c94de2013-09-26 15:36:34 -07001177
1178 // Preserve the gravity adjustment
1179 if (hgrav == Gravity.RIGHT) {
1180 p.x -= mPopupWidth - anchor.getWidth();
1181 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001182
1183 // determine whether there is more space above or below the anchor
1184 anchor.getLocationOnScreen(mScreenLocation);
1185
Romain Guy3e141682010-03-08 17:44:40 -08001186 onTop = (displayFrame.bottom - mScreenLocation[1] - anchor.getHeight() - yoff) <
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 (mScreenLocation[1] - yoff - displayFrame.top);
1188 if (onTop) {
Adam Powell54c94de2013-09-26 15:36:34 -07001189 p.gravity = Gravity.LEFT | Gravity.BOTTOM;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001190 p.y = root.getHeight() - mDrawingLocation[1] + yoff;
1191 } else {
Romain Guy3e141682010-03-08 17:44:40 -08001192 p.y = mDrawingLocation[1] + anchor.getHeight() + yoff;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001193 }
1194 }
1195
Adam Powell56c2d332010-11-05 20:03:03 -07001196 if (mClipToScreen) {
1197 final int displayFrameWidth = displayFrame.right - displayFrame.left;
1198
1199 int right = p.x + p.width;
1200 if (right > displayFrameWidth) {
1201 p.x -= right - displayFrameWidth;
1202 }
1203 if (p.x < displayFrame.left) {
1204 p.x = displayFrame.left;
1205 p.width = Math.min(p.width, displayFrameWidth);
1206 }
1207
Adam Powell5f83a602011-01-19 17:58:04 -08001208 if (onTop) {
1209 int popupTop = mScreenLocation[1] + yoff - mPopupHeight;
1210 if (popupTop < 0) {
1211 p.y += popupTop;
1212 }
1213 } else {
1214 p.y = Math.max(p.y, displayFrame.top);
1215 }
Adam Powell56c2d332010-11-05 20:03:03 -07001216 }
1217
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001218 p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
1219
1220 return onTop;
1221 }
1222
1223 /**
1224 * Returns the maximum height that is available for the popup to be
1225 * completely shown. It is recommended that this height be the maximum for
1226 * the popup's height, otherwise it is possible that the popup will be
1227 * clipped.
1228 *
1229 * @param anchor The view on which the popup window must be anchored.
1230 * @return The maximum available height for the popup to be completely
1231 * shown.
1232 */
1233 public int getMaxAvailableHeight(View anchor) {
1234 return getMaxAvailableHeight(anchor, 0);
1235 }
1236
1237 /**
1238 * Returns the maximum height that is available for the popup to be
1239 * completely shown. It is recommended that this height be the maximum for
1240 * the popup's height, otherwise it is possible that the popup will be
1241 * clipped.
1242 *
1243 * @param anchor The view on which the popup window must be anchored.
1244 * @param yOffset y offset from the view's bottom edge
1245 * @return The maximum available height for the popup to be completely
1246 * shown.
1247 */
1248 public int getMaxAvailableHeight(View anchor, int yOffset) {
Mike LeBeau98acd542009-05-07 19:04:39 -07001249 return getMaxAvailableHeight(anchor, yOffset, false);
1250 }
1251
1252 /**
1253 * Returns the maximum height that is available for the popup to be
1254 * completely shown, optionally ignoring any bottom decorations such as
1255 * the input method. It is recommended that this height be the maximum for
1256 * the popup's height, otherwise it is possible that the popup will be
1257 * clipped.
1258 *
1259 * @param anchor The view on which the popup window must be anchored.
1260 * @param yOffset y offset from the view's bottom edge
1261 * @param ignoreBottomDecorations if true, the height returned will be
1262 * all the way to the bottom of the display, ignoring any
1263 * bottom decorations
1264 * @return The maximum available height for the popup to be completely
1265 * shown.
1266 *
1267 * @hide Pending API council approval.
1268 */
1269 public int getMaxAvailableHeight(View anchor, int yOffset, boolean ignoreBottomDecorations) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001270 final Rect displayFrame = new Rect();
1271 anchor.getWindowVisibleDisplayFrame(displayFrame);
1272
1273 final int[] anchorPos = mDrawingLocation;
1274 anchor.getLocationOnScreen(anchorPos);
1275
Mike LeBeau98acd542009-05-07 19:04:39 -07001276 int bottomEdge = displayFrame.bottom;
1277 if (ignoreBottomDecorations) {
Adam Powella7287f42010-08-17 21:17:04 -07001278 Resources res = anchor.getContext().getResources();
Adam Powell3f4a7642011-05-20 15:56:25 -07001279 bottomEdge = res.getDisplayMetrics().heightPixels;
Mike LeBeau98acd542009-05-07 19:04:39 -07001280 }
1281 final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001282 final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
1283
1284 // anchorPos[1] is distance from anchor to top of screen
1285 int returnedHeight = Math.max(distanceToBottom, distanceToTop);
1286 if (mBackground != null) {
1287 mBackground.getPadding(mTempRect);
1288 returnedHeight -= mTempRect.top + mTempRect.bottom;
1289 }
1290
1291 return returnedHeight;
1292 }
1293
1294 /**
1295 * <p>Dispose of the popup window. This method can be invoked only after
1296 * {@link #showAsDropDown(android.view.View)} has been executed. Failing that, calling
1297 * this method will have no effect.</p>
1298 *
1299 * @see #showAsDropDown(android.view.View)
1300 */
1301 public void dismiss() {
1302 if (isShowing() && mPopupView != null) {
Svetoslav Ganov06f938e2011-11-10 14:31:37 -08001303 mIsShowing = false;
1304
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001305 unregisterForScrollChanged();
1306
Romain Guy92be82e2010-03-17 15:16:21 -07001307 try {
Craig Mautnerb82d0742012-05-23 08:48:39 -07001308 mWindowManager.removeViewImmediate(mPopupView);
Romain Guy92be82e2010-03-17 15:16:21 -07001309 } finally {
1310 if (mPopupView != mContentView && mPopupView instanceof ViewGroup) {
1311 ((ViewGroup) mPopupView).removeView(mContentView);
1312 }
1313 mPopupView = null;
Craig Mautnerb82d0742012-05-23 08:48:39 -07001314
Romain Guy92be82e2010-03-17 15:16:21 -07001315 if (mOnDismissListener != null) {
1316 mOnDismissListener.onDismiss();
1317 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001318 }
1319 }
1320 }
1321
1322 /**
1323 * Sets the listener to be called when the window is dismissed.
1324 *
1325 * @param onDismissListener The listener.
1326 */
1327 public void setOnDismissListener(OnDismissListener onDismissListener) {
1328 mOnDismissListener = onDismissListener;
1329 }
1330
1331 /**
1332 * Updates the state of the popup window, if it is currently being displayed,
John Spurlock33900182014-01-02 11:04:18 -05001333 * from the currently set state. This includes:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001334 * {@link #setClippingEnabled(boolean)}, {@link #setFocusable(boolean)},
1335 * {@link #setIgnoreCheekPress()}, {@link #setInputMethodMode(int)},
1336 * {@link #setTouchable(boolean)}, and {@link #setAnimationStyle(int)}.
1337 */
1338 public void update() {
1339 if (!isShowing() || mContentView == null) {
1340 return;
1341 }
1342
1343 WindowManager.LayoutParams p = (WindowManager.LayoutParams)
1344 mPopupView.getLayoutParams();
1345
1346 boolean update = false;
1347
1348 final int newAnim = computeAnimationResource();
1349 if (newAnim != p.windowAnimations) {
1350 p.windowAnimations = newAnim;
1351 update = true;
1352 }
1353
1354 final int newFlags = computeFlags(p.flags);
1355 if (newFlags != p.flags) {
1356 p.flags = newFlags;
1357 update = true;
1358 }
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001359
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001360 if (update) {
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001361 setLayoutDirectionFromAnchor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001362 mWindowManager.updateViewLayout(mPopupView, p);
1363 }
1364 }
Romain Guyd6a463a2009-05-21 23:10:10 -07001365
1366 /**
1367 * <p>Updates the dimension of the popup window. Calling this function
1368 * also updates the window with the current popup state as described
1369 * for {@link #update()}.</p>
1370 *
1371 * @param width the new width
1372 * @param height the new height
1373 */
1374 public void update(int width, int height) {
1375 WindowManager.LayoutParams p = (WindowManager.LayoutParams)
1376 mPopupView.getLayoutParams();
1377 update(p.x, p.y, width, height, false);
1378 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001379
1380 /**
1381 * <p>Updates the position and the dimension of the popup window. Width and
1382 * height can be set to -1 to update location only. Calling this function
1383 * also updates the window with the current popup state as
1384 * described for {@link #update()}.</p>
1385 *
1386 * @param x the new x location
1387 * @param y the new y location
1388 * @param width the new width, can be -1 to ignore
1389 * @param height the new height, can be -1 to ignore
1390 */
1391 public void update(int x, int y, int width, int height) {
1392 update(x, y, width, height, false);
1393 }
1394
1395 /**
1396 * <p>Updates the position and the dimension of the popup window. Width and
1397 * height can be set to -1 to update location only. Calling this function
1398 * also updates the window with the current popup state as
1399 * described for {@link #update()}.</p>
1400 *
1401 * @param x the new x location
1402 * @param y the new y location
1403 * @param width the new width, can be -1 to ignore
1404 * @param height the new height, can be -1 to ignore
1405 * @param force reposition the window even if the specified position
1406 * already seems to correspond to the LayoutParams
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001407 */
1408 public void update(int x, int y, int width, int height, boolean force) {
1409 if (width != -1) {
1410 mLastWidth = width;
1411 setWidth(width);
1412 }
1413
1414 if (height != -1) {
1415 mLastHeight = height;
1416 setHeight(height);
1417 }
1418
1419 if (!isShowing() || mContentView == null) {
1420 return;
1421 }
1422
Romain Guye29f0642009-06-23 21:27:02 -07001423 WindowManager.LayoutParams p = (WindowManager.LayoutParams) mPopupView.getLayoutParams();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001424
1425 boolean update = force;
1426
1427 final int finalWidth = mWidthMode < 0 ? mWidthMode : mLastWidth;
1428 if (width != -1 && p.width != finalWidth) {
1429 p.width = mLastWidth = finalWidth;
1430 update = true;
1431 }
1432
1433 final int finalHeight = mHeightMode < 0 ? mHeightMode : mLastHeight;
1434 if (height != -1 && p.height != finalHeight) {
1435 p.height = mLastHeight = finalHeight;
1436 update = true;
1437 }
1438
1439 if (p.x != x) {
1440 p.x = x;
1441 update = true;
1442 }
1443
1444 if (p.y != y) {
1445 p.y = y;
1446 update = true;
1447 }
1448
1449 final int newAnim = computeAnimationResource();
1450 if (newAnim != p.windowAnimations) {
1451 p.windowAnimations = newAnim;
1452 update = true;
1453 }
1454
1455 final int newFlags = computeFlags(p.flags);
1456 if (newFlags != p.flags) {
1457 p.flags = newFlags;
1458 update = true;
1459 }
Mike LeBeau98acd542009-05-07 19:04:39 -07001460
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001461 if (update) {
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001462 setLayoutDirectionFromAnchor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001463 mWindowManager.updateViewLayout(mPopupView, p);
1464 }
1465 }
1466
1467 /**
The Android Open Source Project10592532009-03-18 17:39:46 -07001468 * <p>Updates the position and the dimension of the popup window. Calling this
1469 * function also updates the window with the current popup state as described
1470 * for {@link #update()}.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001471 *
1472 * @param anchor the popup's anchor view
1473 * @param width the new width, can be -1 to ignore
1474 * @param height the new height, can be -1 to ignore
1475 */
1476 public void update(View anchor, int width, int height) {
Adam Powell54c94de2013-09-26 15:36:34 -07001477 update(anchor, false, 0, 0, true, width, height, mAnchoredGravity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001478 }
1479
1480 /**
1481 * <p>Updates the position and the dimension of the popup window. Width and
1482 * height can be set to -1 to update location only. Calling this function
1483 * also updates the window with the current popup state as
1484 * described for {@link #update()}.</p>
Gilles Debunne81f08082011-02-17 14:07:19 -08001485 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001486 * <p>If the view later scrolls to move <code>anchor</code> to a different
1487 * location, the popup will be moved correspondingly.</p>
1488 *
1489 * @param anchor the popup's anchor view
1490 * @param xoff x offset from the view's left edge
1491 * @param yoff y offset from the view's bottom edge
1492 * @param width the new width, can be -1 to ignore
1493 * @param height the new height, can be -1 to ignore
1494 */
1495 public void update(View anchor, int xoff, int yoff, int width, int height) {
Adam Powell54c94de2013-09-26 15:36:34 -07001496 update(anchor, true, xoff, yoff, true, width, height, mAnchoredGravity);
The Android Open Source Project10592532009-03-18 17:39:46 -07001497 }
1498
1499 private void update(View anchor, boolean updateLocation, int xoff, int yoff,
Adam Powell54c94de2013-09-26 15:36:34 -07001500 boolean updateDimension, int width, int height, int gravity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001501
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001502 if (!isShowing() || mContentView == null) {
1503 return;
1504 }
1505
1506 WeakReference<View> oldAnchor = mAnchor;
Gilles Debunne81f08082011-02-17 14:07:19 -08001507 final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff);
1508 if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) {
Adam Powell54c94de2013-09-26 15:36:34 -07001509 registerForScrollChanged(anchor, xoff, yoff, gravity);
Gilles Debunne81f08082011-02-17 14:07:19 -08001510 } else if (needsUpdate) {
1511 // No need to register again if this is a DropDown, showAsDropDown already did.
1512 mAnchorXoff = xoff;
1513 mAnchorYoff = yoff;
Adam Powell54c94de2013-09-26 15:36:34 -07001514 mAnchoredGravity = gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001515 }
1516
Romain Guye29f0642009-06-23 21:27:02 -07001517 WindowManager.LayoutParams p = (WindowManager.LayoutParams) mPopupView.getLayoutParams();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001518
The Android Open Source Project10592532009-03-18 17:39:46 -07001519 if (updateDimension) {
1520 if (width == -1) {
1521 width = mPopupWidth;
1522 } else {
1523 mPopupWidth = width;
1524 }
1525 if (height == -1) {
1526 height = mPopupHeight;
1527 } else {
1528 mPopupHeight = height;
1529 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001530 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001531
Romain Guy3e141682010-03-08 17:44:40 -08001532 int x = p.x;
1533 int y = p.y;
The Android Open Source Project10592532009-03-18 17:39:46 -07001534
Romain Guy3e141682010-03-08 17:44:40 -08001535 if (updateLocation) {
Adam Powell54c94de2013-09-26 15:36:34 -07001536 updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff, gravity));
Romain Guy3e141682010-03-08 17:44:40 -08001537 } else {
Adam Powell54c94de2013-09-26 15:36:34 -07001538 updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
1539 mAnchoredGravity));
Romain Guy3e141682010-03-08 17:44:40 -08001540 }
Fabrice Di Megliob003e282012-10-17 17:20:19 -07001541
Romain Guy3e141682010-03-08 17:44:40 -08001542 update(p.x, p.y, width, height, x != p.x || y != p.y);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001543 }
1544
1545 /**
1546 * Listener that is called when this popup window is dismissed.
1547 */
1548 public interface OnDismissListener {
1549 /**
1550 * Called when this popup window is dismissed.
1551 */
1552 public void onDismiss();
1553 }
1554
1555 private void unregisterForScrollChanged() {
1556 WeakReference<View> anchorRef = mAnchor;
1557 View anchor = null;
1558 if (anchorRef != null) {
1559 anchor = anchorRef.get();
1560 }
1561 if (anchor != null) {
1562 ViewTreeObserver vto = anchor.getViewTreeObserver();
1563 vto.removeOnScrollChangedListener(mOnScrollChangedListener);
1564 }
1565 mAnchor = null;
1566 }
1567
Adam Powell54c94de2013-09-26 15:36:34 -07001568 private void registerForScrollChanged(View anchor, int xoff, int yoff, int gravity) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001569 unregisterForScrollChanged();
1570
1571 mAnchor = new WeakReference<View>(anchor);
1572 ViewTreeObserver vto = anchor.getViewTreeObserver();
1573 if (vto != null) {
1574 vto.addOnScrollChangedListener(mOnScrollChangedListener);
1575 }
1576
1577 mAnchorXoff = xoff;
1578 mAnchorYoff = yoff;
Adam Powell54c94de2013-09-26 15:36:34 -07001579 mAnchoredGravity = gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001580 }
1581
1582 private class PopupViewContainer extends FrameLayout {
Adam Powellc3fa6302010-05-18 11:36:27 -07001583 private static final String TAG = "PopupWindow.PopupViewContainer";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001584
1585 public PopupViewContainer(Context context) {
1586 super(context);
1587 }
1588
1589 @Override
1590 protected int[] onCreateDrawableState(int extraSpace) {
1591 if (mAboveAnchor) {
1592 // 1 more needed for the above anchor state
1593 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
1594 View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET);
1595 return drawableState;
1596 } else {
1597 return super.onCreateDrawableState(extraSpace);
1598 }
1599 }
1600
1601 @Override
1602 public boolean dispatchKeyEvent(KeyEvent event) {
1603 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
Per Andersson4ae02b32011-01-17 11:16:23 +01001604 if (getKeyDispatcherState() == null) {
1605 return super.dispatchKeyEvent(event);
1606 }
1607
Dianne Hackborn8d374262009-09-14 21:21:52 -07001608 if (event.getAction() == KeyEvent.ACTION_DOWN
1609 && event.getRepeatCount() == 0) {
Jeff Brownb3ea9222011-01-10 16:26:36 -08001610 KeyEvent.DispatcherState state = getKeyDispatcherState();
1611 if (state != null) {
1612 state.startTracking(event, this);
1613 }
Dianne Hackborn8d374262009-09-14 21:21:52 -07001614 return true;
Jeff Brownb3ea9222011-01-10 16:26:36 -08001615 } else if (event.getAction() == KeyEvent.ACTION_UP) {
1616 KeyEvent.DispatcherState state = getKeyDispatcherState();
1617 if (state != null && state.isTracking(event) && !event.isCanceled()) {
1618 dismiss();
1619 return true;
1620 }
Dianne Hackborn8d374262009-09-14 21:21:52 -07001621 }
1622 return super.dispatchKeyEvent(event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001623 } else {
1624 return super.dispatchKeyEvent(event);
1625 }
1626 }
1627
1628 @Override
1629 public boolean dispatchTouchEvent(MotionEvent ev) {
1630 if (mTouchInterceptor != null && mTouchInterceptor.onTouch(this, ev)) {
1631 return true;
1632 }
1633 return super.dispatchTouchEvent(ev);
1634 }
1635
1636 @Override
1637 public boolean onTouchEvent(MotionEvent event) {
1638 final int x = (int) event.getX();
1639 final int y = (int) event.getY();
1640
1641 if ((event.getAction() == MotionEvent.ACTION_DOWN)
1642 && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
1643 dismiss();
1644 return true;
1645 } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
1646 dismiss();
1647 return true;
1648 } else {
1649 return super.onTouchEvent(event);
1650 }
1651 }
svetoslavganov75986cf2009-05-14 22:28:01 -07001652
1653 @Override
1654 public void sendAccessibilityEvent(int eventType) {
1655 // clinets are interested in the content not the container, make it event source
1656 if (mContentView != null) {
1657 mContentView.sendAccessibilityEvent(eventType);
1658 } else {
1659 super.sendAccessibilityEvent(eventType);
1660 }
1661 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001662 }
1663
1664}