• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.widget;
18 
19 import com.android.internal.R;
20 
21 import android.content.Context;
22 import android.content.res.TypedArray;
23 import android.view.KeyEvent;
24 import android.view.MotionEvent;
25 import android.view.View;
26 import android.view.WindowManager;
27 import android.view.Gravity;
28 import android.view.ViewGroup;
29 import android.view.ViewTreeObserver;
30 import android.view.ViewTreeObserver.OnScrollChangedListener;
31 import android.view.View.OnTouchListener;
32 import android.graphics.PixelFormat;
33 import android.graphics.Rect;
34 import android.graphics.drawable.Drawable;
35 import android.graphics.drawable.StateListDrawable;
36 import android.os.IBinder;
37 import android.util.AttributeSet;
38 
39 import java.lang.ref.WeakReference;
40 
41 /**
42  * <p>A popup window that can be used to display an arbitrary view. The popup
43  * windows is a floating container that appears on top of the current
44  * activity.</p>
45  *
46  * @see android.widget.AutoCompleteTextView
47  * @see android.widget.Spinner
48  */
49 public class PopupWindow {
50     /**
51      * Mode for {@link #setInputMethodMode(int)}: the requirements for the
52      * input method should be based on the focusability of the popup.  That is
53      * if it is focusable than it needs to work with the input method, else
54      * it doesn't.
55      */
56     public static final int INPUT_METHOD_FROM_FOCUSABLE = 0;
57 
58     /**
59      * Mode for {@link #setInputMethodMode(int)}: this popup always needs to
60      * work with an input method, regardless of whether it is focusable.  This
61      * means that it will always be displayed so that the user can also operate
62      * the input method while it is shown.
63      */
64     public static final int INPUT_METHOD_NEEDED = 1;
65 
66     /**
67      * Mode for {@link #setInputMethodMode(int)}: this popup never needs to
68      * work with an input method, regardless of whether it is focusable.  This
69      * means that it will always be displayed to use as much space on the
70      * screen as needed, regardless of whether this covers the input method.
71      */
72     public static final int INPUT_METHOD_NOT_NEEDED = 2;
73 
74     private Context mContext;
75     private WindowManager mWindowManager;
76 
77     private boolean mIsShowing;
78     private boolean mIsDropdown;
79 
80     private View mContentView;
81     private View mPopupView;
82     private boolean mFocusable;
83     private int mInputMethodMode = INPUT_METHOD_FROM_FOCUSABLE;
84     private int mSoftInputMode;
85     private boolean mTouchable = true;
86     private boolean mOutsideTouchable = false;
87     private boolean mClippingEnabled = true;
88 
89     private OnTouchListener mTouchInterceptor;
90 
91     private int mWidthMode;
92     private int mWidth;
93     private int mLastWidth;
94     private int mHeightMode;
95     private int mHeight;
96     private int mLastHeight;
97 
98     private int mPopupWidth;
99     private int mPopupHeight;
100 
101     private int[] mDrawingLocation = new int[2];
102     private int[] mScreenLocation = new int[2];
103     private Rect mTempRect = new Rect();
104 
105     private Drawable mBackground;
106     private Drawable mAboveAnchorBackgroundDrawable;
107     private Drawable mBelowAnchorBackgroundDrawable;
108 
109     private boolean mAboveAnchor;
110 
111     private OnDismissListener mOnDismissListener;
112     private boolean mIgnoreCheekPress = false;
113 
114     private int mAnimationStyle = -1;
115 
116     private static final int[] ABOVE_ANCHOR_STATE_SET = new int[] {
117         com.android.internal.R.attr.state_above_anchor
118     };
119 
120     private WeakReference<View> mAnchor;
121     private OnScrollChangedListener mOnScrollChangedListener =
122         new OnScrollChangedListener() {
123             public void onScrollChanged() {
124                 View anchor = mAnchor.get();
125                 if (anchor != null && mPopupView != null) {
126                     WindowManager.LayoutParams p = (WindowManager.LayoutParams)
127                             mPopupView.getLayoutParams();
128 
129                     mAboveAnchor = findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff);
130                     update(p.x, p.y, -1, -1, true);
131                 }
132             }
133         };
134     private int mAnchorXoff, mAnchorYoff;
135 
136     /**
137      * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
138      *
139      * <p>The popup does provide a background.</p>
140      */
PopupWindow(Context context)141     public PopupWindow(Context context) {
142         this(context, null);
143     }
144 
145     /**
146      * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
147      *
148      * <p>The popup does provide a background.</p>
149      */
PopupWindow(Context context, AttributeSet attrs)150     public PopupWindow(Context context, AttributeSet attrs) {
151         this(context, attrs, com.android.internal.R.attr.popupWindowStyle);
152     }
153 
154     /**
155      * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
156      *
157      * <p>The popup does provide a background.</p>
158      */
PopupWindow(Context context, AttributeSet attrs, int defStyle)159     public PopupWindow(Context context, AttributeSet attrs, int defStyle) {
160         mContext = context;
161         mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
162 
163         TypedArray a =
164             context.obtainStyledAttributes(
165                 attrs, com.android.internal.R.styleable.PopupWindow, defStyle, 0);
166 
167         mBackground = a.getDrawable(R.styleable.PopupWindow_popupBackground);
168 
169         // If this is a StateListDrawable, try to find and store the drawable to be
170         // used when the drop-down is placed above its anchor view, and the one to be
171         // used when the drop-down is placed below its anchor view. We extract
172         // the drawables ourselves to work around a problem with using refreshDrawableState
173         // that it will take into account the padding of all drawables specified in a
174         // StateListDrawable, thus adding superfluous padding to drop-down views.
175         //
176         // We assume a StateListDrawable will have a drawable for ABOVE_ANCHOR_STATE_SET and
177         // at least one other drawable, intended for the 'below-anchor state'.
178         if (mBackground instanceof StateListDrawable) {
179             StateListDrawable background = (StateListDrawable) mBackground;
180 
181             // Find the above-anchor view - this one's easy, it should be labeled as such.
182             int aboveAnchorStateIndex = background.getStateDrawableIndex(ABOVE_ANCHOR_STATE_SET);
183 
184             // Now, for the below-anchor view, look for any other drawable specified in the
185             // StateListDrawable which is not for the above-anchor state and use that.
186             int count = background.getStateCount();
187             int belowAnchorStateIndex = -1;
188             for (int i = 0; i < count; i++) {
189                 if (i != aboveAnchorStateIndex) {
190                     belowAnchorStateIndex = i;
191                     break;
192                 }
193             }
194 
195             // Store the drawables we found, if we found them. Otherwise, set them both
196             // to null so that we'll just use refreshDrawableState.
197             if (aboveAnchorStateIndex != -1 && belowAnchorStateIndex != -1) {
198                 mAboveAnchorBackgroundDrawable = background.getStateDrawable(aboveAnchorStateIndex);
199                 mBelowAnchorBackgroundDrawable = background.getStateDrawable(belowAnchorStateIndex);
200             } else {
201                 mBelowAnchorBackgroundDrawable = null;
202                 mAboveAnchorBackgroundDrawable = null;
203             }
204         }
205 
206         a.recycle();
207     }
208 
209     /**
210      * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
211      *
212      * <p>The popup does not provide any background. This should be handled
213      * by the content view.</p>
214      */
PopupWindow()215     public PopupWindow() {
216         this(null, 0, 0);
217     }
218 
219     /**
220      * <p>Create a new non focusable popup window which can display the
221      * <tt>contentView</tt>. The dimension of the window are (0,0).</p>
222      *
223      * <p>The popup does not provide any background. This should be handled
224      * by the content view.</p>
225      *
226      * @param contentView the popup's content
227      */
PopupWindow(View contentView)228     public PopupWindow(View contentView) {
229         this(contentView, 0, 0);
230     }
231 
232     /**
233      * <p>Create a new empty, non focusable popup window. The dimension of the
234      * window must be passed to this constructor.</p>
235      *
236      * <p>The popup does not provide any background. This should be handled
237      * by the content view.</p>
238      *
239      * @param width the popup's width
240      * @param height the popup's height
241      */
PopupWindow(int width, int height)242     public PopupWindow(int width, int height) {
243         this(null, width, height);
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 must be passed to
249      * this constructor.</p>
250      *
251      * <p>The popup does not provide any background. This should be handled
252      * by the content view.</p>
253      *
254      * @param contentView the popup's content
255      * @param width the popup's width
256      * @param height the popup's height
257      */
PopupWindow(View contentView, int width, int height)258     public PopupWindow(View contentView, int width, int height) {
259         this(contentView, width, height, false);
260     }
261 
262     /**
263      * <p>Create a new popup window which can display the <tt>contentView</tt>.
264      * The dimension of the window must be passed to this constructor.</p>
265      *
266      * <p>The popup does not provide any background. This should be handled
267      * by the content view.</p>
268      *
269      * @param contentView the popup's content
270      * @param width the popup's width
271      * @param height the popup's height
272      * @param focusable true if the popup can be focused, false otherwise
273      */
PopupWindow(View contentView, int width, int height, boolean focusable)274     public PopupWindow(View contentView, int width, int height, boolean focusable) {
275         if (contentView != null) {
276             mContext = contentView.getContext();
277             mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
278         }
279         setContentView(contentView);
280         setWidth(width);
281         setHeight(height);
282         setFocusable(focusable);
283     }
284 
285     /**
286      * <p>Return the drawable used as the popup window's background.</p>
287      *
288      * @return the background drawable or null
289      */
getBackground()290     public Drawable getBackground() {
291         return mBackground;
292     }
293 
294     /**
295      * <p>Change the background drawable for this popup window. The background
296      * can be set to null.</p>
297      *
298      * @param background the popup's background
299      */
setBackgroundDrawable(Drawable background)300     public void setBackgroundDrawable(Drawable background) {
301         mBackground = background;
302     }
303 
304     /**
305      * <p>Return the animation style to use the popup appears and disappears</p>
306      *
307      * @return the animation style to use the popup appears and disappears
308      */
getAnimationStyle()309     public int getAnimationStyle() {
310         return mAnimationStyle;
311     }
312 
313     /**
314      * Set the flag on popup to ignore cheek press eventt; by default this flag
315      * is set to false
316      * which means the pop wont ignore cheek press dispatch events.
317      *
318      * <p>If the popup is showing, calling this method will take effect only
319      * the next time the popup is shown or through a manual call to one of
320      * the {@link #update()} methods.</p>
321      *
322      * @see #update()
323      */
setIgnoreCheekPress()324     public void setIgnoreCheekPress() {
325         mIgnoreCheekPress = true;
326     }
327 
328 
329     /**
330      * <p>Change the animation style resource for this popup.</p>
331      *
332      * <p>If the popup is showing, calling this method will take effect only
333      * the next time the popup is shown or through a manual call to one of
334      * the {@link #update()} methods.</p>
335      *
336      * @param animationStyle animation style to use when the popup appears
337      *      and disappears.  Set to -1 for the default animation, 0 for no
338      *      animation, or a resource identifier for an explicit animation.
339      *
340      * @see #update()
341      */
setAnimationStyle(int animationStyle)342     public void setAnimationStyle(int animationStyle) {
343         mAnimationStyle = animationStyle;
344     }
345 
346     /**
347      * <p>Return the view used as the content of the popup window.</p>
348      *
349      * @return a {@link android.view.View} representing the popup's content
350      *
351      * @see #setContentView(android.view.View)
352      */
getContentView()353     public View getContentView() {
354         return mContentView;
355     }
356 
357     /**
358      * <p>Change the popup's content. The content is represented by an instance
359      * of {@link android.view.View}.</p>
360      *
361      * <p>This method has no effect if called when the popup is showing.  To
362      * apply it while a popup is showing, call </p>
363      *
364      * @param contentView the new content for the popup
365      *
366      * @see #getContentView()
367      * @see #isShowing()
368      */
setContentView(View contentView)369     public void setContentView(View contentView) {
370         if (isShowing()) {
371             return;
372         }
373 
374         mContentView = contentView;
375 
376         if (mContext == null) {
377             mContext = mContentView.getContext();
378         }
379 
380         if (mWindowManager == null) {
381             mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
382         }
383     }
384 
385     /**
386      * Set a callback for all touch events being dispatched to the popup
387      * window.
388      */
setTouchInterceptor(OnTouchListener l)389     public void setTouchInterceptor(OnTouchListener l) {
390         mTouchInterceptor = l;
391     }
392 
393     /**
394      * <p>Indicate whether the popup window can grab the focus.</p>
395      *
396      * @return true if the popup is focusable, false otherwise
397      *
398      * @see #setFocusable(boolean)
399      */
isFocusable()400     public boolean isFocusable() {
401         return mFocusable;
402     }
403 
404     /**
405      * <p>Changes the focusability of the popup window. When focusable, the
406      * window will grab the focus from the current focused widget if the popup
407      * contains a focusable {@link android.view.View}.  By default a popup
408      * window is not focusable.</p>
409      *
410      * <p>If the popup is showing, calling this method will take effect only
411      * the next time the popup is shown or through a manual call to one of
412      * the {@link #update()} methods.</p>
413      *
414      * @param focusable true if the popup should grab focus, false otherwise.
415      *
416      * @see #isFocusable()
417      * @see #isShowing()
418      * @see #update()
419      */
setFocusable(boolean focusable)420     public void setFocusable(boolean focusable) {
421         mFocusable = focusable;
422     }
423 
424     /**
425      * Return the current value in {@link #setInputMethodMode(int)}.
426      *
427      * @see #setInputMethodMode(int)
428      */
getInputMethodMode()429     public int getInputMethodMode() {
430         return mInputMethodMode;
431 
432     }
433 
434     /**
435      * Control how the popup operates with an input method: one of
436      * {@link #INPUT_METHOD_FROM_FOCUSABLE}, {@link #INPUT_METHOD_NEEDED},
437      * or {@link #INPUT_METHOD_NOT_NEEDED}.
438      *
439      * <p>If the popup is showing, calling this method will take effect only
440      * the next time the popup is shown or through a manual call to one of
441      * the {@link #update()} methods.</p>
442      *
443      * @see #getInputMethodMode()
444      * @see #update()
445      */
setInputMethodMode(int mode)446     public void setInputMethodMode(int mode) {
447         mInputMethodMode = mode;
448     }
449 
450     /**
451      * Sets the operating mode for the soft input area.
452      *
453      * @param mode The desired mode, see
454      *        {@link android.view.WindowManager.LayoutParams#softInputMode}
455      *        for the full list
456      *
457      * @see android.view.WindowManager.LayoutParams#softInputMode
458      * @see #getSoftInputMode()
459      */
setSoftInputMode(int mode)460     public void setSoftInputMode(int mode) {
461         mSoftInputMode = mode;
462     }
463 
464     /**
465      * Returns the current value in {@link #setSoftInputMode(int)}.
466      *
467      * @see #setSoftInputMode(int)
468      * @see android.view.WindowManager.LayoutParams#softInputMode
469      */
getSoftInputMode()470     public int getSoftInputMode() {
471         return mSoftInputMode;
472     }
473 
474     /**
475      * <p>Indicates whether the popup window receives touch events.</p>
476      *
477      * @return true if the popup is touchable, false otherwise
478      *
479      * @see #setTouchable(boolean)
480      */
isTouchable()481     public boolean isTouchable() {
482         return mTouchable;
483     }
484 
485     /**
486      * <p>Changes the touchability of the popup window. When touchable, the
487      * window will receive touch events, otherwise touch events will go to the
488      * window below it. By default the window is touchable.</p>
489      *
490      * <p>If the popup is showing, calling this method will take effect only
491      * the next time the popup is shown or through a manual call to one of
492      * the {@link #update()} methods.</p>
493      *
494      * @param touchable true if the popup should receive touch events, false otherwise
495      *
496      * @see #isTouchable()
497      * @see #isShowing()
498      * @see #update()
499      */
setTouchable(boolean touchable)500     public void setTouchable(boolean touchable) {
501         mTouchable = touchable;
502     }
503 
504     /**
505      * <p>Indicates whether the popup window will be informed of touch events
506      * outside of its window.</p>
507      *
508      * @return true if the popup is outside touchable, false otherwise
509      *
510      * @see #setOutsideTouchable(boolean)
511      */
isOutsideTouchable()512     public boolean isOutsideTouchable() {
513         return mOutsideTouchable;
514     }
515 
516     /**
517      * <p>Controls whether the pop-up will be informed of touch events outside
518      * of its window.  This only makes sense for pop-ups that are touchable
519      * but not focusable, which means touches outside of the window will
520      * be delivered to the window behind.  The default is false.</p>
521      *
522      * <p>If the popup is showing, calling this method will take effect only
523      * the next time the popup is shown or through a manual call to one of
524      * the {@link #update()} methods.</p>
525      *
526      * @param touchable true if the popup should receive outside
527      * touch events, false otherwise
528      *
529      * @see #isOutsideTouchable()
530      * @see #isShowing()
531      * @see #update()
532      */
setOutsideTouchable(boolean touchable)533     public void setOutsideTouchable(boolean touchable) {
534         mOutsideTouchable = touchable;
535     }
536 
537     /**
538      * <p>Indicates whether clipping of the popup window is enabled.</p>
539      *
540      * @return true if the clipping is enabled, false otherwise
541      *
542      * @see #setClippingEnabled(boolean)
543      */
isClippingEnabled()544     public boolean isClippingEnabled() {
545         return mClippingEnabled;
546     }
547 
548     /**
549      * <p>Allows the popup window to extend beyond the bounds of the screen. By default the
550      * window is clipped to the screen boundaries. Setting this to false will allow windows to be
551      * accurately positioned.</p>
552      *
553      * <p>If the popup is showing, calling this method will take effect only
554      * the next time the popup is shown or through a manual call to one of
555      * the {@link #update()} methods.</p>
556      *
557      * @param enabled false if the window should be allowed to extend outside of the screen
558      * @see #isShowing()
559      * @see #isClippingEnabled()
560      * @see #update()
561      */
setClippingEnabled(boolean enabled)562     public void setClippingEnabled(boolean enabled) {
563         mClippingEnabled = enabled;
564     }
565 
566     /**
567      * <p>Change the width and height measure specs that are given to the
568      * window manager by the popup.  By default these are 0, meaning that
569      * the current width or height is requested as an explicit size from
570      * the window manager.  You can supply
571      * {@link ViewGroup.LayoutParams#WRAP_CONTENT} or
572      * {@link ViewGroup.LayoutParams#FILL_PARENT} to have that measure
573      * spec supplied instead, replacing the absolute width and height that
574      * has been set in the popup.</p>
575      *
576      * <p>If the popup is showing, calling this method will take effect only
577      * the next time the popup is shown.</p>
578      *
579      * @param widthSpec an explicit width measure spec mode, either
580      * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
581      * {@link ViewGroup.LayoutParams#FILL_PARENT}, or 0 to use the absolute
582      * width.
583      * @param heightSpec an explicit height measure spec mode, either
584      * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
585      * {@link ViewGroup.LayoutParams#FILL_PARENT}, or 0 to use the absolute
586      * height.
587      */
setWindowLayoutMode(int widthSpec, int heightSpec)588     public void setWindowLayoutMode(int widthSpec, int heightSpec) {
589         mWidthMode = widthSpec;
590         mHeightMode = heightSpec;
591     }
592 
593     /**
594      * <p>Return this popup's height MeasureSpec</p>
595      *
596      * @return the height MeasureSpec of the popup
597      *
598      * @see #setHeight(int)
599      */
getHeight()600     public int getHeight() {
601         return mHeight;
602     }
603 
604     /**
605      * <p>Change the popup's height MeasureSpec</p>
606      *
607      * <p>If the popup is showing, calling this method will take effect only
608      * the next time the popup is shown.</p>
609      *
610      * @param height the height MeasureSpec of the popup
611      *
612      * @see #getHeight()
613      * @see #isShowing()
614      */
setHeight(int height)615     public void setHeight(int height) {
616         mHeight = height;
617     }
618 
619     /**
620      * <p>Return this popup's width MeasureSpec</p>
621      *
622      * @return the width MeasureSpec of the popup
623      *
624      * @see #setWidth(int)
625      */
getWidth()626     public int getWidth() {
627         return mWidth;
628     }
629 
630     /**
631      * <p>Change the popup's width MeasureSpec</p>
632      *
633      * <p>If the popup is showing, calling this method will take effect only
634      * the next time the popup is shown.</p>
635      *
636      * @param width the width MeasureSpec of the popup
637      *
638      * @see #getWidth()
639      * @see #isShowing()
640      */
setWidth(int width)641     public void setWidth(int width) {
642         mWidth = width;
643     }
644 
645     /**
646      * <p>Indicate whether this popup window is showing on screen.</p>
647      *
648      * @return true if the popup is showing, false otherwise
649      */
isShowing()650     public boolean isShowing() {
651         return mIsShowing;
652     }
653 
654     /**
655      * <p>
656      * Display the content view in a popup window at the specified location. If the popup window
657      * cannot fit on screen, it will be clipped. See {@link android.view.WindowManager.LayoutParams}
658      * for more information on how gravity and the x and y parameters are related. Specifying
659      * a gravity of {@link android.view.Gravity#NO_GRAVITY} is similar to specifying
660      * <code>Gravity.LEFT | Gravity.TOP</code>.
661      * </p>
662      *
663      * @param parent a parent view to get the {@link android.view.View#getWindowToken()} token from
664      * @param gravity the gravity which controls the placement of the popup window
665      * @param x the popup's x location offset
666      * @param y the popup's y location offset
667      */
showAtLocation(View parent, int gravity, int x, int y)668     public void showAtLocation(View parent, int gravity, int x, int y) {
669         if (isShowing() || mContentView == null) {
670             return;
671         }
672 
673         unregisterForScrollChanged();
674 
675         mIsShowing = true;
676         mIsDropdown = false;
677 
678         WindowManager.LayoutParams p = createPopupLayout(parent.getWindowToken());
679         p.windowAnimations = computeAnimationResource();
680 
681         preparePopup(p);
682         if (gravity == Gravity.NO_GRAVITY) {
683             gravity = Gravity.TOP | Gravity.LEFT;
684         }
685         p.gravity = gravity;
686         p.x = x;
687         p.y = y;
688         invokePopup(p);
689     }
690 
691     /**
692      * <p>Display the content view in a popup window anchored to the bottom-left
693      * corner of the anchor view. If there is not enough room on screen to show
694      * the popup in its entirety, this method tries to find a parent scroll
695      * view to scroll. If no parent scroll view can be scrolled, the bottom-left
696      * corner of the popup is pinned at the top left corner of the anchor view.</p>
697      *
698      * @param anchor the view on which to pin the popup window
699      *
700      * @see #dismiss()
701      */
showAsDropDown(View anchor)702     public void showAsDropDown(View anchor) {
703         showAsDropDown(anchor, 0, 0);
704     }
705 
706     /**
707      * <p>Display the content view in a popup window anchored to the bottom-left
708      * corner of the anchor view offset by the specified x and y coordinates.
709      * If there is not enough room on screen to show
710      * the popup in its entirety, this method tries to find a parent scroll
711      * view to scroll. If no parent scroll view can be scrolled, the bottom-left
712      * corner of the popup is pinned at the top left corner of the anchor view.</p>
713      * <p>If the view later scrolls to move <code>anchor</code> to a different
714      * location, the popup will be moved correspondingly.</p>
715      *
716      * @param anchor the view on which to pin the popup window
717      *
718      * @see #dismiss()
719      */
showAsDropDown(View anchor, int xoff, int yoff)720     public void showAsDropDown(View anchor, int xoff, int yoff) {
721         if (isShowing() || mContentView == null) {
722             return;
723         }
724 
725         registerForScrollChanged(anchor, xoff, yoff);
726 
727         mIsShowing = true;
728         mIsDropdown = true;
729 
730         WindowManager.LayoutParams p = createPopupLayout(anchor.getWindowToken());
731         preparePopup(p);
732         mAboveAnchor = findDropDownPosition(anchor, p, xoff, yoff);
733 
734         if (mBackground != null) {
735             // If the background drawable provided was a StateListDrawable with above-anchor
736             // and below-anchor states, use those. Otherwise rely on refreshDrawableState to
737             // do the job.
738             if (mAboveAnchorBackgroundDrawable != null) {
739                 if (mAboveAnchor) {
740                     mPopupView.setBackgroundDrawable(mAboveAnchorBackgroundDrawable);
741                 } else {
742                     mPopupView.setBackgroundDrawable(mBelowAnchorBackgroundDrawable);
743                 }
744             } else {
745                 mPopupView.refreshDrawableState();
746             }
747         }
748 
749         if (mHeightMode < 0) p.height = mLastHeight = mHeightMode;
750         if (mWidthMode < 0) p.width = mLastWidth = mWidthMode;
751 
752         p.windowAnimations = computeAnimationResource();
753 
754         invokePopup(p);
755     }
756 
757     /**
758      * Indicates whether the popup is showing above (the y coordinate of the popup's bottom
759      * is less than the y coordinate of the anchor) or below the anchor view (the y coordinate
760      * of the popup is greater than y coordinate of the anchor's bottom).
761      *
762      * The value returned
763      * by this method is meaningful only after {@link #showAsDropDown(android.view.View)}
764      * or {@link #showAsDropDown(android.view.View, int, int)} was invoked.
765      *
766      * @return True if this popup is showing above the anchor view, false otherwise.
767      */
isAboveAnchor()768     public boolean isAboveAnchor() {
769         return mAboveAnchor;
770     }
771 
772     /**
773      * <p>Prepare the popup by embedding in into a new ViewGroup if the
774      * background drawable is not null. If embedding is required, the layout
775      * parameters' height is mnodified to take into account the background's
776      * padding.</p>
777      *
778      * @param p the layout parameters of the popup's content view
779      */
preparePopup(WindowManager.LayoutParams p)780     private void preparePopup(WindowManager.LayoutParams p) {
781         if (mContentView == null || mContext == null || mWindowManager == null) {
782             throw new IllegalStateException("You must specify a valid content view by "
783                     + "calling setContentView() before attempting to show the popup.");
784         }
785 
786         if (mBackground != null) {
787             final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
788             int height = ViewGroup.LayoutParams.FILL_PARENT;
789             if (layoutParams != null &&
790                     layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
791                 height = ViewGroup.LayoutParams.WRAP_CONTENT;
792             }
793 
794             // when a background is available, we embed the content view
795             // within another view that owns the background drawable
796             PopupViewContainer popupViewContainer = new PopupViewContainer(mContext);
797             PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(
798                     ViewGroup.LayoutParams.FILL_PARENT, height
799             );
800             popupViewContainer.setBackgroundDrawable(mBackground);
801             popupViewContainer.addView(mContentView, listParams);
802 
803             mPopupView = popupViewContainer;
804         } else {
805             mPopupView = mContentView;
806         }
807         mPopupWidth = p.width;
808         mPopupHeight = p.height;
809     }
810 
811     /**
812      * <p>Invoke the popup window by adding the content view to the window
813      * manager.</p>
814      *
815      * <p>The content view must be non-null when this method is invoked.</p>
816      *
817      * @param p the layout parameters of the popup's content view
818      */
invokePopup(WindowManager.LayoutParams p)819     private void invokePopup(WindowManager.LayoutParams p) {
820         p.packageName = mContext.getPackageName();
821         mWindowManager.addView(mPopupView, p);
822     }
823 
824     /**
825      * <p>Generate the layout parameters for the popup window.</p>
826      *
827      * @param token the window token used to bind the popup's window
828      *
829      * @return the layout parameters to pass to the window manager
830      */
createPopupLayout(IBinder token)831     private WindowManager.LayoutParams createPopupLayout(IBinder token) {
832         // generates the layout parameters for the drop down
833         // we want a fixed size view located at the bottom left of the anchor
834         WindowManager.LayoutParams p = new WindowManager.LayoutParams();
835         // these gravity settings put the view at the top left corner of the
836         // screen. The view is then positioned to the appropriate location
837         // by setting the x and y offsets to match the anchor's bottom
838         // left corner
839         p.gravity = Gravity.LEFT | Gravity.TOP;
840         p.width = mLastWidth = mWidth;
841         p.height = mLastHeight = mHeight;
842         if (mBackground != null) {
843             p.format = mBackground.getOpacity();
844         } else {
845             p.format = PixelFormat.TRANSLUCENT;
846         }
847         p.flags = computeFlags(p.flags);
848         p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
849         p.token = token;
850         p.softInputMode = mSoftInputMode;
851         p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
852 
853         return p;
854     }
855 
computeFlags(int curFlags)856     private int computeFlags(int curFlags) {
857         curFlags &= ~(
858                 WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES |
859                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
860                 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
861                 WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
862                 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
863                 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
864         if(mIgnoreCheekPress) {
865             curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
866         }
867         if (!mFocusable) {
868             curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
869             if (mInputMethodMode == INPUT_METHOD_NEEDED) {
870                 curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
871             }
872         } else if (mInputMethodMode == INPUT_METHOD_NOT_NEEDED) {
873             curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
874         }
875         if (!mTouchable) {
876             curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
877         }
878         if (mOutsideTouchable) {
879             curFlags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
880         }
881         if (!mClippingEnabled) {
882             curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
883         }
884         return curFlags;
885     }
886 
computeAnimationResource()887     private int computeAnimationResource() {
888         if (mAnimationStyle == -1) {
889             if (mIsDropdown) {
890                 return mAboveAnchor
891                         ? com.android.internal.R.style.Animation_DropDownUp
892                         : com.android.internal.R.style.Animation_DropDownDown;
893             }
894             return 0;
895         }
896         return mAnimationStyle;
897     }
898 
899     /**
900      * <p>Positions the popup window on screen. When the popup window is too
901      * tall to fit under the anchor, a parent scroll view is seeked and scrolled
902      * up to reclaim space. If scrolling is not possible or not enough, the
903      * popup window gets moved on top of the anchor.</p>
904      *
905      * <p>The height must have been set on the layout parameters prior to
906      * calling this method.</p>
907      *
908      * @param anchor the view on which the popup window must be anchored
909      * @param p the layout parameters used to display the drop down
910      *
911      * @return true if the popup is translated upwards to fit on screen
912      */
findDropDownPosition(View anchor, WindowManager.LayoutParams p, int xoff, int yoff)913     private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p,
914             int xoff, int yoff) {
915 
916         anchor.getLocationInWindow(mDrawingLocation);
917         p.x = mDrawingLocation[0] + xoff;
918         p.y = mDrawingLocation[1] + anchor.getMeasuredHeight() + yoff;
919 
920         boolean onTop = false;
921 
922         p.gravity = Gravity.LEFT | Gravity.TOP;
923 
924         anchor.getLocationOnScreen(mScreenLocation);
925         final Rect displayFrame = new Rect();
926         anchor.getWindowVisibleDisplayFrame(displayFrame);
927 
928         final View root = anchor.getRootView();
929         if (p.y + mPopupHeight > displayFrame.bottom || p.x + mPopupWidth - root.getWidth() > 0) {
930             // if the drop down disappears at the bottom of the screen. we try to
931             // scroll a parent scrollview or move the drop down back up on top of
932             // the edit box
933             int scrollX = anchor.getScrollX();
934             int scrollY = anchor.getScrollY();
935             Rect r = new Rect(scrollX, scrollY,  scrollX + mPopupWidth,
936                     scrollY + mPopupHeight + anchor.getMeasuredHeight());
937             anchor.requestRectangleOnScreen(r, true);
938 
939             // now we re-evaluate the space available, and decide from that
940             // whether the pop-up will go above or below the anchor.
941             anchor.getLocationInWindow(mDrawingLocation);
942             p.x = mDrawingLocation[0] + xoff;
943             p.y = mDrawingLocation[1] + anchor.getMeasuredHeight() + yoff;
944 
945             // determine whether there is more space above or below the anchor
946             anchor.getLocationOnScreen(mScreenLocation);
947 
948             onTop = (displayFrame.bottom - mScreenLocation[1] - anchor.getMeasuredHeight() - yoff) <
949                     (mScreenLocation[1] - yoff - displayFrame.top);
950             if (onTop) {
951                 p.gravity = Gravity.LEFT | Gravity.BOTTOM;
952                 p.y = root.getHeight() - mDrawingLocation[1] + yoff;
953             } else {
954                 p.y = mDrawingLocation[1] + anchor.getMeasuredHeight() + yoff;
955             }
956         }
957 
958         p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
959 
960         return onTop;
961     }
962 
963     /**
964      * Returns the maximum height that is available for the popup to be
965      * completely shown. It is recommended that this height be the maximum for
966      * the popup's height, otherwise it is possible that the popup will be
967      * clipped.
968      *
969      * @param anchor The view on which the popup window must be anchored.
970      * @return The maximum available height for the popup to be completely
971      *         shown.
972      */
973     public int getMaxAvailableHeight(View anchor) {
974         return getMaxAvailableHeight(anchor, 0);
975     }
976 
977     /**
978      * Returns the maximum height that is available for the popup to be
979      * completely shown. It is recommended that this height be the maximum for
980      * the popup's height, otherwise it is possible that the popup will be
981      * clipped.
982      *
983      * @param anchor The view on which the popup window must be anchored.
984      * @param yOffset y offset from the view's bottom edge
985      * @return The maximum available height for the popup to be completely
986      *         shown.
987      */
988     public int getMaxAvailableHeight(View anchor, int yOffset) {
989         return getMaxAvailableHeight(anchor, yOffset, false);
990     }
991 
992     /**
993      * Returns the maximum height that is available for the popup to be
994      * completely shown, optionally ignoring any bottom decorations such as
995      * the input method. It is recommended that this height be the maximum for
996      * the popup's height, otherwise it is possible that the popup will be
997      * clipped.
998      *
999      * @param anchor The view on which the popup window must be anchored.
1000      * @param yOffset y offset from the view's bottom edge
1001      * @param ignoreBottomDecorations if true, the height returned will be
1002      *        all the way to the bottom of the display, ignoring any
1003      *        bottom decorations
1004      * @return The maximum available height for the popup to be completely
1005      *         shown.
1006      *
1007      * @hide Pending API council approval.
1008      */
1009     public int getMaxAvailableHeight(View anchor, int yOffset, boolean ignoreBottomDecorations) {
1010         final Rect displayFrame = new Rect();
1011         anchor.getWindowVisibleDisplayFrame(displayFrame);
1012 
1013         final int[] anchorPos = mDrawingLocation;
1014         anchor.getLocationOnScreen(anchorPos);
1015 
1016         int bottomEdge = displayFrame.bottom;
1017         if (ignoreBottomDecorations) {
1018             bottomEdge = anchor.getContext().getResources().getDisplayMetrics().heightPixels;
1019         }
1020         final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
1021         final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
1022 
1023         // anchorPos[1] is distance from anchor to top of screen
1024         int returnedHeight = Math.max(distanceToBottom, distanceToTop);
1025         if (mBackground != null) {
1026             mBackground.getPadding(mTempRect);
1027             returnedHeight -= mTempRect.top + mTempRect.bottom;
1028         }
1029 
1030         return returnedHeight;
1031     }
1032 
1033     /**
1034      * <p>Dispose of the popup window. This method can be invoked only after
1035      * {@link #showAsDropDown(android.view.View)} has been executed. Failing that, calling
1036      * this method will have no effect.</p>
1037      *
1038      * @see #showAsDropDown(android.view.View)
1039      */
1040     public void dismiss() {
1041         if (isShowing() && mPopupView != null) {
1042             unregisterForScrollChanged();
1043 
1044             mWindowManager.removeView(mPopupView);
1045 
1046             if (mPopupView != mContentView && mPopupView instanceof ViewGroup) {
1047                 ((ViewGroup) mPopupView).removeView(mContentView);
1048             }
1049             mPopupView = null;
1050             mIsShowing = false;
1051 
1052             if (mOnDismissListener != null) {
1053                 mOnDismissListener.onDismiss();
1054             }
1055         }
1056     }
1057 
1058     /**
1059      * Sets the listener to be called when the window is dismissed.
1060      *
1061      * @param onDismissListener The listener.
1062      */
1063     public void setOnDismissListener(OnDismissListener onDismissListener) {
1064         mOnDismissListener = onDismissListener;
1065     }
1066 
1067     /**
1068      * Updates the state of the popup window, if it is currently being displayed,
1069      * from the currently set state.  This include:
1070      * {@link #setClippingEnabled(boolean)}, {@link #setFocusable(boolean)},
1071      * {@link #setIgnoreCheekPress()}, {@link #setInputMethodMode(int)},
1072      * {@link #setTouchable(boolean)}, and {@link #setAnimationStyle(int)}.
1073      */
1074     public void update() {
1075         if (!isShowing() || mContentView == null) {
1076             return;
1077         }
1078 
1079         WindowManager.LayoutParams p = (WindowManager.LayoutParams)
1080                 mPopupView.getLayoutParams();
1081 
1082         boolean update = false;
1083 
1084         final int newAnim = computeAnimationResource();
1085         if (newAnim != p.windowAnimations) {
1086             p.windowAnimations = newAnim;
1087             update = true;
1088         }
1089 
1090         final int newFlags = computeFlags(p.flags);
1091         if (newFlags != p.flags) {
1092             p.flags = newFlags;
1093             update = true;
1094         }
1095 
1096         if (update) {
1097             mWindowManager.updateViewLayout(mPopupView, p);
1098         }
1099     }
1100 
1101     /**
1102      * <p>Updates the dimension of the popup window. Calling this function
1103      * also updates the window with the current popup state as described
1104      * for {@link #update()}.</p>
1105      *
1106      * @param width the new width
1107      * @param height the new height
1108      */
1109     public void update(int width, int height) {
1110         WindowManager.LayoutParams p = (WindowManager.LayoutParams)
1111                 mPopupView.getLayoutParams();
1112         update(p.x, p.y, width, height, false);
1113     }
1114 
1115     /**
1116      * <p>Updates the position and the dimension of the popup window. Width and
1117      * height can be set to -1 to update location only.  Calling this function
1118      * also updates the window with the current popup state as
1119      * described for {@link #update()}.</p>
1120      *
1121      * @param x the new x location
1122      * @param y the new y location
1123      * @param width the new width, can be -1 to ignore
1124      * @param height the new height, can be -1 to ignore
1125      */
1126     public void update(int x, int y, int width, int height) {
1127         update(x, y, width, height, false);
1128     }
1129 
1130     /**
1131      * <p>Updates the position and the dimension of the popup window. Width and
1132      * height can be set to -1 to update location only.  Calling this function
1133      * also updates the window with the current popup state as
1134      * described for {@link #update()}.</p>
1135      *
1136      * @param x the new x location
1137      * @param y the new y location
1138      * @param width the new width, can be -1 to ignore
1139      * @param height the new height, can be -1 to ignore
1140      * @param force reposition the window even if the specified position
1141      *              already seems to correspond to the LayoutParams
1142      */
1143     public void update(int x, int y, int width, int height, boolean force) {
1144         if (width != -1) {
1145             mLastWidth = width;
1146             setWidth(width);
1147         }
1148 
1149         if (height != -1) {
1150             mLastHeight = height;
1151             setHeight(height);
1152         }
1153 
1154         if (!isShowing() || mContentView == null) {
1155             return;
1156         }
1157 
1158         WindowManager.LayoutParams p = (WindowManager.LayoutParams) mPopupView.getLayoutParams();
1159 
1160         boolean update = force;
1161 
1162         final int finalWidth = mWidthMode < 0 ? mWidthMode : mLastWidth;
1163         if (width != -1 && p.width != finalWidth) {
1164             p.width = mLastWidth = finalWidth;
1165             update = true;
1166         }
1167 
1168         final int finalHeight = mHeightMode < 0 ? mHeightMode : mLastHeight;
1169         if (height != -1 && p.height != finalHeight) {
1170             p.height = mLastHeight = finalHeight;
1171             update = true;
1172         }
1173 
1174         if (p.x != x) {
1175             p.x = x;
1176             update = true;
1177         }
1178 
1179         if (p.y != y) {
1180             p.y = y;
1181             update = true;
1182         }
1183 
1184         final int newAnim = computeAnimationResource();
1185         if (newAnim != p.windowAnimations) {
1186             p.windowAnimations = newAnim;
1187             update = true;
1188         }
1189 
1190         final int newFlags = computeFlags(p.flags);
1191         if (newFlags != p.flags) {
1192             p.flags = newFlags;
1193             update = true;
1194         }
1195 
1196         if (update) {
1197             mWindowManager.updateViewLayout(mPopupView, p);
1198         }
1199     }
1200 
1201     /**
1202      * <p>Updates the position and the dimension of the popup window. Calling this
1203      * function also updates the window with the current popup state as described
1204      * for {@link #update()}.</p>
1205      *
1206      * @param anchor the popup's anchor view
1207      * @param width the new width, can be -1 to ignore
1208      * @param height the new height, can be -1 to ignore
1209      */
1210     public void update(View anchor, int width, int height) {
1211         update(anchor, false, 0, 0, true, width, height);
1212     }
1213 
1214     /**
1215      * <p>Updates the position and the dimension of the popup window. Width and
1216      * height can be set to -1 to update location only.  Calling this function
1217      * also updates the window with the current popup state as
1218      * described for {@link #update()}.</p>
1219      * <p>If the view later scrolls to move <code>anchor</code> to a different
1220      * location, the popup will be moved correspondingly.</p>
1221      *
1222      * @param anchor the popup's anchor view
1223      * @param xoff x offset from the view's left edge
1224      * @param yoff y offset from the view's bottom edge
1225      * @param width the new width, can be -1 to ignore
1226      * @param height the new height, can be -1 to ignore
1227      */
1228     public void update(View anchor, int xoff, int yoff, int width, int height) {
1229         update(anchor, true, xoff, yoff, true, width, height);
1230     }
1231 
1232     private void update(View anchor, boolean updateLocation, int xoff, int yoff,
1233             boolean updateDimension, int width, int height) {
1234 
1235         if (!isShowing() || mContentView == null) {
1236             return;
1237         }
1238 
1239         WeakReference<View> oldAnchor = mAnchor;
1240         if (oldAnchor == null || oldAnchor.get() != anchor ||
1241                 (updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff))) {
1242             registerForScrollChanged(anchor, xoff, yoff);
1243         }
1244 
1245         WindowManager.LayoutParams p = (WindowManager.LayoutParams) mPopupView.getLayoutParams();
1246 
1247         if (updateDimension) {
1248             if (width == -1) {
1249                 width = mPopupWidth;
1250             } else {
1251                 mPopupWidth = width;
1252             }
1253             if (height == -1) {
1254                 height = mPopupHeight;
1255             } else {
1256                 mPopupHeight = height;
1257             }
1258         }
1259 
1260         if (updateLocation) {
1261             mAboveAnchor = findDropDownPosition(anchor, p, xoff, yoff);
1262         } else {
1263             mAboveAnchor = findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff);
1264         }
1265 
1266         update(p.x, p.y, width, height);
1267     }
1268 
1269     /**
1270      * Listener that is called when this popup window is dismissed.
1271      */
1272     public interface OnDismissListener {
1273         /**
1274          * Called when this popup window is dismissed.
1275          */
1276         public void onDismiss();
1277     }
1278 
1279     private void unregisterForScrollChanged() {
1280         WeakReference<View> anchorRef = mAnchor;
1281         View anchor = null;
1282         if (anchorRef != null) {
1283             anchor = anchorRef.get();
1284         }
1285         if (anchor != null) {
1286             ViewTreeObserver vto = anchor.getViewTreeObserver();
1287             vto.removeOnScrollChangedListener(mOnScrollChangedListener);
1288         }
1289         mAnchor = null;
1290     }
1291 
1292     private void registerForScrollChanged(View anchor, int xoff, int yoff) {
1293         unregisterForScrollChanged();
1294 
1295         mAnchor = new WeakReference<View>(anchor);
1296         ViewTreeObserver vto = anchor.getViewTreeObserver();
1297         if (vto != null) {
1298             vto.addOnScrollChangedListener(mOnScrollChangedListener);
1299         }
1300 
1301         mAnchorXoff = xoff;
1302         mAnchorYoff = yoff;
1303     }
1304 
1305     private class PopupViewContainer extends FrameLayout {
1306 
1307         public PopupViewContainer(Context context) {
1308             super(context);
1309         }
1310 
1311         @Override
1312         protected int[] onCreateDrawableState(int extraSpace) {
1313             if (mAboveAnchor) {
1314                 // 1 more needed for the above anchor state
1315                 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
1316                 View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET);
1317                 return drawableState;
1318             } else {
1319                 return super.onCreateDrawableState(extraSpace);
1320             }
1321         }
1322 
1323         @Override
1324         public boolean dispatchKeyEvent(KeyEvent event) {
1325             if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
1326                 if (event.getAction() == KeyEvent.ACTION_DOWN
1327                         && event.getRepeatCount() == 0) {
1328                     getKeyDispatcherState().startTracking(event, this);
1329                     return true;
1330                 } else if (event.getAction() == KeyEvent.ACTION_UP
1331                         && getKeyDispatcherState().isTracking(event) && !event.isCanceled()) {
1332                     dismiss();
1333                     return true;
1334                 }
1335                 return super.dispatchKeyEvent(event);
1336             } else {
1337                 return super.dispatchKeyEvent(event);
1338             }
1339         }
1340 
1341         @Override
1342         public boolean dispatchTouchEvent(MotionEvent ev) {
1343             if (mTouchInterceptor != null && mTouchInterceptor.onTouch(this, ev)) {
1344                 return true;
1345             }
1346             return super.dispatchTouchEvent(ev);
1347         }
1348 
1349         @Override
1350         public boolean onTouchEvent(MotionEvent event) {
1351             final int x = (int) event.getX();
1352             final int y = (int) event.getY();
1353 
1354             if ((event.getAction() == MotionEvent.ACTION_DOWN)
1355                     && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
1356                 dismiss();
1357                 return true;
1358             } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
1359                 dismiss();
1360                 return true;
1361             } else {
1362                 return super.onTouchEvent(event);
1363             }
1364         }
1365 
1366         @Override
1367         public void sendAccessibilityEvent(int eventType) {
1368             // clinets are interested in the content not the container, make it event source
1369             if (mContentView != null) {
1370                 mContentView.sendAccessibilityEvent(eventType);
1371             } else {
1372                 super.sendAccessibilityEvent(eventType);
1373             }
1374         }
1375     }
1376 
1377 }
1378