PopupWindow.java revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
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
17package android.widget;
18
19import com.android.internal.R;
20
21import android.view.KeyEvent;
22import android.view.MotionEvent;
23import android.view.View;
24import android.view.WindowManager;
25import android.view.WindowManagerImpl;
26import android.view.Gravity;
27import android.view.ViewGroup;
28import android.graphics.PixelFormat;
29import android.graphics.Rect;
30import android.graphics.drawable.Drawable;
31import android.os.IBinder;
32import android.content.Context;
33import android.content.res.TypedArray;
34import android.util.AttributeSet;
35
36/**
37 * <p>A popup window that can be used to display an arbitrary view. The popup
38 * windows is a floating container that appears on top of the current
39 * activity.</p>
40 *
41 * @see android.widget.AutoCompleteTextView
42 * @see android.widget.Spinner
43 */
44public class PopupWindow {
45    /**
46     * The height of the status bar so we know how much of the screen we can
47     * actually be displayed in.
48     * <p>
49     * TODO: This IS NOT the right way to do this.
50     * Instead of knowing how much of the screen is available, a popup that
51     * wants anchor and maximize space shouldn't be setting a height, instead
52     * the PopupViewContainer should have its layout height as fill_parent and
53     * properly position the popup.
54     */
55    private static final int STATUS_BAR_HEIGHT = 30;
56
57    private boolean mIsShowing;
58
59    private View mContentView;
60    private View mPopupView;
61    private boolean mFocusable;
62
63    private int mWidth;
64    private int mHeight;
65
66    private int[] mDrawingLocation = new int[2];
67    private int[] mRootLocation = new int[2];
68    private Rect mTempRect = new Rect();
69
70    private Context mContext;
71    private Drawable mBackground;
72
73    private boolean mAboveAnchor;
74
75    private OnDismissListener mOnDismissListener;
76    private boolean mIgnoreCheekPress = false;
77
78    private int mAnimationStyle = -1;
79
80    private static final int[] ABOVE_ANCHOR_STATE_SET = new int[] {
81        com.android.internal.R.attr.state_above_anchor
82    };
83
84    /**
85     * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
86     *
87     * <p>The popup does provide a background.</p>
88     */
89    public PopupWindow(Context context) {
90        this(context, null);
91    }
92
93    /**
94     * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
95     *
96     * <p>The popup does provide a background.</p>
97     */
98    public PopupWindow(Context context, AttributeSet attrs) {
99        this(context, attrs, com.android.internal.R.attr.popupWindowStyle);
100    }
101
102    /**
103     * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
104     *
105     * <p>The popup does provide a background.</p>
106     */
107    public PopupWindow(Context context, AttributeSet attrs, int defStyle) {
108        mContext = context;
109
110        TypedArray a =
111            context.obtainStyledAttributes(
112                attrs, com.android.internal.R.styleable.PopupWindow, defStyle, 0);
113
114        mBackground = a.getDrawable(R.styleable.PopupWindow_popupBackground);
115
116        a.recycle();
117    }
118
119    /**
120     * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
121     *
122     * <p>The popup does not provide any background. This should be handled
123     * by the content view.</p>
124     */
125    public PopupWindow() {
126        this(null, 0, 0);
127    }
128
129    /**
130     * <p>Create a new non focusable popup window which can display the
131     * <tt>contentView</tt>. The dimension of the window are (0,0).</p>
132     *
133     * <p>The popup does not provide any background. This should be handled
134     * by the content view.</p>
135     *
136     * @param contentView the popup's content
137     */
138    public PopupWindow(View contentView) {
139        this(contentView, 0, 0);
140    }
141
142    /**
143     * <p>Create a new empty, non focusable popup window. The dimension of the
144     * window must be passed to this constructor.</p>
145     *
146     * <p>The popup does not provide any background. This should be handled
147     * by the content view.</p>
148     *
149     * @param width the popup's width
150     * @param height the popup's height
151     */
152    public PopupWindow(int width, int height) {
153        this(null, width, height);
154    }
155
156    /**
157     * <p>Create a new non focusable popup window which can display the
158     * <tt>contentView</tt>. The dimension of the window must be passed to
159     * this constructor.</p>
160     *
161     * <p>The popup does not provide any background. This should be handled
162     * by the content view.</p>
163     *
164     * @param contentView the popup's content
165     * @param width the popup's width
166     * @param height the popup's height
167     */
168    public PopupWindow(View contentView, int width, int height) {
169        this(contentView, width, height, false);
170    }
171
172    /**
173     * <p>Create a new popup window which can display the <tt>contentView</tt>.
174     * The dimension of the window must be passed to this constructor.</p>
175     *
176     * <p>The popup does not provide any background. This should be handled
177     * by the content view.</p>
178     *
179     * @param contentView the popup's content
180     * @param width the popup's width
181     * @param height the popup's height
182     * @param focusable true if the popup can be focused, false otherwise
183     */
184    public PopupWindow(View contentView, int width, int height,
185            boolean focusable) {
186        setContentView(contentView);
187        setWidth(width);
188        setHeight(height);
189        setFocusable(focusable);
190    }
191
192    /**
193     * <p>Return the drawable used as the popup window's background.</p>
194     *
195     * @return the background drawable or null
196     */
197    public Drawable getBackground() {
198        return mBackground;
199    }
200
201    /**
202     * <p>Change the background drawable for this popup window. The background
203     * can be set to null.</p>
204     *
205     * @param background the popup's background
206     */
207    public void setBackgroundDrawable(Drawable background) {
208        mBackground = background;
209    }
210
211    /**
212     * <p>Return the animation style to use the popup appears and disappears</p>
213     *
214     * @return the animation style to use the popup appears and disappears
215     */
216    public int getAnimationStyle() {
217        return mAnimationStyle;
218    }
219
220    /**
221     * set the flag on popup to ignore cheek press events
222     * This method has to be invoked before displaying the content view
223     * of the popup for the window flags to take effect and will be ignored
224     * if the pop up is already displayed. By default this flag is set to false
225     * which means the pop wont ignore cheek press dispatch events.
226     */
227    public void setIgnoreCheekPress() {
228        mIgnoreCheekPress = true;
229    }
230
231
232    /**
233     * <p>Change the animation style for this popup.</p>
234     *
235     * @param animationStyle animation style to use when the popup appears and disappears
236     */
237    public void setAnimationStyle(int animationStyle) {
238        mAnimationStyle = animationStyle;
239    }
240
241    /**
242     * <p>Return the view used as the content of the popup window.</p>
243     *
244     * @return a {@link android.view.View} representing the popup's content
245     *
246     * @see #setContentView(android.view.View)
247     */
248    public View getContentView() {
249        return mContentView;
250    }
251
252    /**
253     * <p>Change the popup's content. The content is represented by an instance
254     * of {@link android.view.View}.</p>
255     *
256     * <p>This method has no effect if called when the popup is showing.</p>
257     *
258     * @param contentView the new content for the popup
259     *
260     * @see #getContentView()
261     * @see #isShowing()
262     */
263    public void setContentView(View contentView) {
264        if (isShowing()) {
265            return;
266        }
267
268        mContentView = contentView;
269    }
270
271    /**
272     * <p>Indicate whether the popup window can grab the focus.</p>
273     *
274     * @return true if the popup is focusable, false otherwise
275     *
276     * @see #setFocusable(boolean)
277     */
278    public boolean isFocusable() {
279        return mFocusable;
280    }
281
282    /**
283     * <p>Changes the focusability of the popup window. When focusable, the
284     * window will grab the focus from the current focused widget if the popup
285     * contains a focusable {@link android.view.View}.</p>
286     *
287     * <p>If the popup is showing, calling this method will take effect only
288     * the next time the popup is shown.</p>
289     *
290     * @param focusable true if the popup should grab focus, false otherwise
291     *
292     * @see #isFocusable()
293     * @see #isShowing()
294     */
295    public void setFocusable(boolean focusable) {
296        mFocusable = focusable;
297    }
298
299    /**
300     * <p>Return this popup's height MeasureSpec</p>
301     *
302     * @return the height MeasureSpec of the popup
303     *
304     * @see #setHeight(int)
305     */
306    public int getHeight() {
307        return mHeight;
308    }
309
310    /**
311     * <p>Change the popup's height MeasureSpec</p>
312     *
313     * <p>If the popup is showing, calling this method will take effect only
314     * the next time the popup is shown.</p>
315     *
316     * @param height the height MeasureSpec of the popup
317     *
318     * @see #getHeight()
319     * @see #isShowing()
320     */
321    public void setHeight(int height) {
322        mHeight = height;
323    }
324
325    /**
326     * <p>Return this popup's width MeasureSpec</p>
327     *
328     * @return the width MeasureSpec of the popup
329     *
330     * @see #setWidth(int)
331     */
332    public int getWidth() {
333        return mWidth;
334    }
335
336    /**
337     * <p>Change the popup's width MeasureSpec</p>
338     *
339     * <p>If the popup is showing, calling this method will take effect only
340     * the next time the popup is shown.</p>
341     *
342     * @param width the width MeasureSpec of the popup
343     *
344     * @see #getWidth()
345     * @see #isShowing()
346     */
347    public void setWidth(int width) {
348        mWidth = width;
349    }
350
351    /**
352     * <p>Indicate whether this popup window is showing on screen.</p>
353     *
354     * @return true if the popup is showing, false otherwise
355     */
356    public boolean isShowing() {
357        return mIsShowing;
358    }
359
360    /**
361     * <p>
362     * Display the content view in a popup window at the specified location. If the popup window
363     * cannot fit on screen, it will be clipped. See {@link android.view.WindowManager.LayoutParams}
364     * for more information on how gravity and the x and y parameters are related. Specifying
365     * a gravity of {@link android.view.Gravity#NO_GRAVITY} is similar to specifying
366     * <code>Gravity.LEFT | Gravity.TOP</code>.
367     * </p>
368     *
369     * @param parent a parent view to get the {@link android.view.View#getWindowToken()} token from
370     * @param gravity the gravity which controls the placement of the popup window
371     * @param x the popup's x location offset
372     * @param y the popup's y location offset
373     */
374    public void showAtLocation(View parent, int gravity, int x, int y) {
375        if (isShowing() || mContentView == null) {
376            return;
377        }
378
379        mIsShowing = true;
380
381        WindowManager.LayoutParams p = createPopupLayout(parent.getWindowToken());
382        if (mAnimationStyle != -1) {
383            p.windowAnimations = mAnimationStyle;
384        }
385
386        preparePopup(p);
387        if (gravity == Gravity.NO_GRAVITY) {
388            gravity = Gravity.TOP | Gravity.LEFT;
389        }
390        p.gravity = gravity;
391        p.x = x;
392        p.y = y;
393        invokePopup(p);
394    }
395
396    /**
397     * <p>Display the content view in a popup window anchored to the bottom-left
398     * corner of the anchor view. If there is not enough room on screen to show
399     * the popup in its entirety, this method tries to find a parent scroll
400     * view to scroll. If no parent scroll view can be scrolled, the bottom-left
401     * corner of the popup is pinned at the top left corner of the anchor view.</p>
402     *
403     * @param anchor the view on which to pin the popup window
404     *
405     * @see #dismiss()
406     */
407    public void showAsDropDown(View anchor) {
408        showAsDropDown(anchor, 0, 0);
409    }
410
411    /**
412     * <p>Display the content view in a popup window anchored to the bottom-left
413     * corner of the anchor view offset by the specified x and y coordinates.
414     * If there is not enough room on screen to show
415     * the popup in its entirety, this method tries to find a parent scroll
416     * view to scroll. If no parent scroll view can be scrolled, the bottom-left
417     * corner of the popup is pinned at the top left corner of the anchor view.</p>
418     *
419     * @param anchor the view on which to pin the popup window
420     *
421     * @see #dismiss()
422     */
423    public void showAsDropDown(View anchor, int xoff, int yoff) {
424        if (isShowing() || mContentView == null) {
425            return;
426        }
427
428        mIsShowing = true;
429
430        WindowManager.LayoutParams p = createPopupLayout(anchor.getWindowToken());
431        preparePopup(p);
432        if (mBackground != null) {
433            mPopupView.refreshDrawableState();
434        }
435        mAboveAnchor = findDropDownPosition(anchor, p, xoff, yoff);
436        if (mAnimationStyle == -1) {
437            p.windowAnimations = mAboveAnchor
438                    ? com.android.internal.R.style.Animation_DropDownUp
439                    : com.android.internal.R.style.Animation_DropDownDown;
440        } else {
441            p.windowAnimations = mAnimationStyle;
442        }
443        invokePopup(p);
444    }
445
446    /**
447     * <p>Prepare the popup by embedding in into a new ViewGroup if the
448     * background drawable is not null. If embedding is required, the layout
449     * parameters' height is mnodified to take into account the background's
450     * padding.</p>
451     *
452     * @param p the layout parameters of the popup's content view
453     */
454    private void preparePopup(WindowManager.LayoutParams p) {
455        if (mBackground != null) {
456            // when a background is available, we embed the content view
457            // within another view that owns the background drawable
458            PopupViewContainer popupViewContainer = new PopupViewContainer(mContext);
459            PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(
460                    ViewGroup.LayoutParams.FILL_PARENT,
461                    ViewGroup.LayoutParams.FILL_PARENT
462            );
463            popupViewContainer.setBackgroundDrawable(mBackground);
464            popupViewContainer.addView(mContentView, listParams);
465
466            if (p.height >= 0) {
467                // accomodate the popup's height to take into account the
468                // background's padding
469                p.height += popupViewContainer.getPaddingTop() +
470                        popupViewContainer.getPaddingBottom();
471            }
472            if (p.width >= 0) {
473                // accomodate the popup's width to take into account the
474                // background's padding
475                p.width += popupViewContainer.getPaddingLeft() +
476                        popupViewContainer.getPaddingRight();
477            }
478            mPopupView = popupViewContainer;
479        } else {
480            mPopupView = mContentView;
481        }
482
483    }
484
485    /**
486     * <p>Invoke the popup window by adding the content view to the window
487     * manager.</p>
488     *
489     * <p>The content view must be non-null when this method is invoked.</p>
490     *
491     * @param p the layout parameters of the popup's content view
492     */
493    private void invokePopup(WindowManager.LayoutParams p) {
494        WindowManagerImpl wm = WindowManagerImpl.getDefault();
495        wm.addView(mPopupView, p);
496    }
497
498    /**
499     * <p>Generate the layout parameters for the popup window.</p>
500     *
501     * @param token the window token used to bind the popup's window
502     *
503     * @return the layout parameters to pass to the window manager
504     */
505    private WindowManager.LayoutParams createPopupLayout(IBinder token) {
506        // generates the layout parameters for the drop down
507        // we want a fixed size view located at the bottom left of the anchor
508        WindowManager.LayoutParams p = new WindowManager.LayoutParams();
509        // these gravity settings put the view at the top left corner of the
510        // screen. The view is then positioned to the appropriate location
511        // by setting the x and y offsets to match the anchor's bottom
512        // left corner
513        p.gravity = Gravity.LEFT | Gravity.TOP;
514        p.width = mWidth;
515        p.height = mHeight;
516        if (mBackground != null) {
517            p.format = mBackground.getOpacity();
518        } else {
519            p.format = PixelFormat.TRANSLUCENT;
520        }
521        if(mIgnoreCheekPress) {
522            p.flags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
523        }
524        if (!mFocusable) {
525            p.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
526        }
527        p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
528        p.token = token;
529
530        return p;
531    }
532
533    /**
534     * <p>Positions the popup window on screen. When the popup window is too
535     * tall to fit under the anchor, a parent scroll view is seeked and scrolled
536     * up to reclaim space. If scrolling is not possible or not enough, the
537     * popup window gets moved on top of the anchor.</p>
538     *
539     * <p>The height must have been set on the layout parameters prior to
540     * calling this method.</p>
541     *
542     * @param anchor the view on which the popup window must be anchored
543     * @param p the layout parameters used to display the drop down
544     *
545     * @return true if the popup is translated upwards to fit on screen
546     */
547    private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p, int xoff, int yoff) {
548        anchor.getLocationInWindow(mDrawingLocation);
549        p.x = mDrawingLocation[0] + xoff;
550        p.y = mDrawingLocation[1] + anchor.getMeasuredHeight() + yoff;
551
552        boolean onTop = false;
553
554        if (p.y + p.height > WindowManagerImpl.getDefault().getDefaultDisplay().getHeight()) {
555            // if the drop down disappears at the bottom of the screen. we try to
556            // scroll a parent scrollview or move the drop down back up on top of
557            // the edit box
558            View root = anchor.getRootView();
559            root.getLocationInWindow(mRootLocation);
560            int delta = p.y + p.height - mRootLocation[1] - root.getHeight();
561
562            if (delta > 0 || p.x + p.width - mRootLocation[0] - root.getWidth() > 0) {
563                Rect r = new Rect(anchor.getScrollX(), anchor.getScrollY(),
564                        p.width, p.height + anchor.getMeasuredHeight());
565
566                onTop = !anchor.requestRectangleOnScreen(r, true);
567
568                if (onTop) {
569                    p.y -= anchor.getMeasuredHeight() + p.height;
570                } else {
571                    anchor.getLocationOnScreen(mDrawingLocation);
572                    p.x = mDrawingLocation[0] + xoff;
573                    p.y = mDrawingLocation[1] + anchor.getMeasuredHeight() + yoff;
574                }
575            }
576        }
577
578        return onTop;
579    }
580
581    /**
582     * Returns the maximum height that is available for the popup to be
583     * completely shown. It is recommended that this height be the maximum for
584     * the popup's height, otherwise it is possible that the popup will be
585     * clipped.
586     *
587     * @param anchor The view on which the popup window must be anchored.
588     * @return The maximum available height for the popup to be completely
589     *         shown.
590     */
591    public int getMaxAvailableHeight(View anchor) {
592        // TODO: read comment on STATUS_BAR_HEIGHT
593        final int screenHeight = WindowManagerImpl.getDefault().getDefaultDisplay().getHeight()
594                - STATUS_BAR_HEIGHT;
595
596        final int[] anchorPos = mDrawingLocation;
597        anchor.getLocationOnScreen(anchorPos);
598        anchorPos[1] -= STATUS_BAR_HEIGHT;
599
600        final int distanceFromAnchorToBottom = screenHeight - (anchorPos[1] + anchor.getHeight());
601
602        // anchorPos[1] is distance from anchor to top of screen
603        int returnedHeight = Math.max(anchorPos[1], distanceFromAnchorToBottom);
604        if (mBackground != null) {
605            mBackground.getPadding(mTempRect);
606            returnedHeight -= mTempRect.top + mTempRect.bottom;
607        }
608
609        return returnedHeight;
610    }
611
612    /**
613     * <p>Dispose of the popup window. This method can be invoked only after
614     * {@link #showAsDropDown(android.view.View)} has been executed. Failing that, calling
615     * this method will have no effect.</p>
616     *
617     * @see #showAsDropDown(android.view.View)
618     */
619    public void dismiss() {
620        if (isShowing() && mPopupView != null) {
621            WindowManagerImpl wm = WindowManagerImpl.getDefault();
622            wm.removeView(mPopupView);
623            if (mPopupView != mContentView && mPopupView instanceof ViewGroup) {
624                ((ViewGroup) mPopupView).removeView(mContentView);
625            }
626            mIsShowing = false;
627
628            if (mOnDismissListener != null) {
629                mOnDismissListener.onDismiss();
630            }
631        }
632    }
633
634    /**
635     * Sets the listener to be called when the window is dismissed.
636     *
637     * @param onDismissListener The listener.
638     */
639    public void setOnDismissListener(OnDismissListener onDismissListener) {
640        mOnDismissListener = onDismissListener;
641    }
642
643    /**
644     * <p>Updates the position and the dimension of the popup window. Width and
645     * height can be set to -1 to update location only.</p>
646     *
647     * @param x the new x location
648     * @param y the new y location
649     * @param width the new width, can be -1 to ignore
650     * @param height the new height, can be -1 to ignore
651     */
652    public void update(int x, int y, int width, int height) {
653        if (width != -1) {
654            setWidth(width);
655        }
656
657        if (height != -1) {
658            setHeight(height);
659        }
660
661        if (!isShowing() || mContentView == null) {
662            return;
663        }
664
665        WindowManager.LayoutParams p = (WindowManager.LayoutParams)
666                mPopupView.getLayoutParams();
667
668        boolean update = false;
669
670        if (width != -1 && p.width != width) {
671            p.width = width;
672            update = true;
673        }
674
675        if (height != -1 && p.height != height) {
676            p.height = height;
677            update = true;
678        }
679
680        if (p.x != x) {
681            p.x = x;
682            update = true;
683        }
684
685        if (p.y != y) {
686            p.y = y;
687            update = true;
688        }
689
690        if (update) {
691            if (mPopupView != mContentView) {
692                final View popupViewContainer = mPopupView;
693                if (p.height >= 0) {
694                    // accomodate the popup's height to take into account the
695                    // background's padding
696                    p.height += popupViewContainer.getPaddingTop() +
697                            popupViewContainer.getPaddingBottom();
698                }
699                if (p.width >= 0) {
700                    // accomodate the popup's width to take into account the
701                    // background's padding
702                    p.width += popupViewContainer.getPaddingLeft() +
703                            popupViewContainer.getPaddingRight();
704                }
705            }
706
707            WindowManagerImpl wm = WindowManagerImpl.getDefault();
708            wm.updateViewLayout(mPopupView, p);
709        }
710    }
711
712    /**
713     * <p>Updates the position and the dimension of the popup window. Width and
714     * height can be set to -1 to update location only.</p>
715     *
716     * @param anchor the popup's anchor view
717     * @param width the new width, can be -1 to ignore
718     * @param height the new height, can be -1 to ignore
719     */
720    public void update(View anchor, int width, int height) {
721        update(anchor, 0, 0, width, height);
722    }
723
724    /**
725     * <p>Updates the position and the dimension of the popup window. Width and
726     * height can be set to -1 to update location only.</p>
727     *
728     * @param anchor the popup's anchor view
729     * @param xoff x offset from the view's left edge
730     * @param yoff y offset from the view's bottom edge
731     * @param width the new width, can be -1 to ignore
732     * @param height the new height, can be -1 to ignore
733     */
734    public void update(View anchor, int xoff, int yoff, int width, int height) {
735        if (!isShowing() || mContentView == null) {
736            return;
737        }
738
739        WindowManager.LayoutParams p = (WindowManager.LayoutParams)
740                mPopupView.getLayoutParams();
741
742        int x = p.x;
743        int y = p.y;
744        findDropDownPosition(anchor, p, xoff, yoff);
745
746        update(x, y, width, height);
747    }
748
749    /**
750     * Listener that is called when this popup window is dismissed.
751     */
752    interface OnDismissListener {
753        /**
754         * Called when this popup window is dismissed.
755         */
756        public void onDismiss();
757    }
758
759    private class PopupViewContainer extends FrameLayout {
760
761        public PopupViewContainer(Context context) {
762            super(context);
763        }
764
765        @Override
766        protected int[] onCreateDrawableState(int extraSpace) {
767            if (mAboveAnchor) {
768                // 1 more needed for the above anchor state
769                final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
770                View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET);
771                return drawableState;
772            } else {
773                return super.onCreateDrawableState(extraSpace);
774            }
775        }
776
777        @Override
778        public boolean dispatchKeyEvent(KeyEvent event) {
779            if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
780                dismiss();
781                return true;
782            } else {
783                return super.dispatchKeyEvent(event);
784            }
785        }
786
787        @Override
788        public boolean onTouchEvent(MotionEvent event) {
789            final int x = (int) event.getX();
790            final int y = (int) event.getY();
791
792            if ((event.getAction() == MotionEvent.ACTION_DOWN)
793                    && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
794                dismiss();
795                return true;
796            } else {
797                return super.onTouchEvent(event);
798            }
799        }
800
801    }
802
803}
804