MenuPopupHelper.java revision d6e47228c44aaadb0d4518da6db5c3f5dffda1ab
1bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell/*
2bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * Copyright (C) 2010 The Android Open Source Project
3bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell *
4bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * Licensed under the Apache License, Version 2.0 (the "License");
5bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * you may not use this file except in compliance with the License.
6bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * You may obtain a copy of the License at
7bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell *
8bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell *      http://www.apache.org/licenses/LICENSE-2.0
9bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell *
10bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * Unless required by applicable law or agreed to in writing, software
11bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * distributed under the License is distributed on an "AS IS" BASIS,
12bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * See the License for the specific language governing permissions and
14bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * limitations under the License.
15bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell */
16bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
1766698bb15ba0f873aa1c2290cc50d6bb839a474aChris Banespackage android.support.v7.view.menu;
18bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
19bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powellimport android.content.Context;
20d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banesimport android.graphics.Point;
216142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.graphics.Rect;
22d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banesimport android.os.Build;
236142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.annotation.AttrRes;
246142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.annotation.NonNull;
256142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.annotation.Nullable;
266142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.annotation.StyleRes;
276142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.v4.view.GravityCompat;
286142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.v4.view.ViewCompat;
29da10fdd1400ecfd8d7f2e55651dd528d0614dfc5Jeff Brownimport android.support.v7.appcompat.R;
30d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banesimport android.view.Display;
3149c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.view.Gravity;
32bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powellimport android.view.View;
33d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banesimport android.view.WindowManager;
346142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.widget.PopupWindow.OnDismissListener;
35bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
36bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell/**
37bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * Presents a menu as a small, simple popup anchored to another view.
38ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani *
39bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * @hide
40bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell */
416142a54baae3289f734947c6b5375b12eb0fb722Chris Banespublic class MenuPopupHelper implements MenuHelper {
426142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private static final int TOUCH_EPICENTER_SIZE_DP = 48;
43bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
4449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    private final Context mContext;
456142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
466142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    // Immutable cached popup menu properties.
4749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    private final MenuBuilder mMenu;
4849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    private final boolean mOverflowOnly;
4949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    private final int mPopupStyleAttr;
50a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette    private final int mPopupStyleRes;
5149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
526142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    // Mutable cached popup menu properties.
53ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    private View mAnchorView;
546142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private int mDropDownGravity = Gravity.START;
556142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private boolean mForceShowIcon;
566142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private MenuPresenter.Callback mPresenterCallback;
5749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
586142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private MenuPopup mPopup;
596142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private OnDismissListener mOnDismissListener;
6049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
616142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu) {
626142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        this(context, menu, null, false, R.attr.popupMenuStyle, 0);
63ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    }
64bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
656142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu,
666142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            @NonNull View anchorView) {
676142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        this(context, menu, anchorView, false, R.attr.popupMenuStyle, 0);
68ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    }
69bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
706142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu,
716142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            @NonNull View anchorView,
726142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            boolean overflowOnly, @AttrRes int popupStyleAttr) {
73a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette        this(context, menu, anchorView, overflowOnly, popupStyleAttr, 0);
74a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette    }
75a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette
766142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu,
776142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            @NonNull View anchorView, boolean overflowOnly, @AttrRes int popupStyleAttr,
786142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            @StyleRes int popupStyleRes) {
79ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        mContext = context;
80ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        mMenu = menu;
816142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        mAnchorView = anchorView;
82ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        mOverflowOnly = overflowOnly;
8349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        mPopupStyleAttr = popupStyleAttr;
84a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette        mPopupStyleRes = popupStyleRes;
856142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    }
86bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
876142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public void setOnDismissListener(@Nullable OnDismissListener listener) {
886142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        mOnDismissListener = listener;
89ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    }
90bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
916142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
926142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      * Sets the view to which the popup window is anchored.
936142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      * <p>
946142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      * Changes take effect on the next call to show().
956142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      *
966142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      * @param anchor the view to which the popup window should be anchored
976142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      */
986142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public void setAnchorView(@NonNull View anchor) {
99ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        mAnchorView = anchor;
100ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    }
101bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
1026142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
1036142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * Sets whether the popup menu's adapter is forced to show icons in the
1046142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * menu item views.
1056142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * <p>
1066142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * Changes take effect on the next call to show().
1076142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     *
1086142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @param forceShowIcon {@code true} to force icons to be shown, or
1096142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     *                  {@code false} for icons to be optionally shown
1106142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     */
1116142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public void setForceShowIcon(boolean forceShowIcon) {
1126142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        mForceShowIcon = forceShowIcon;
113d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        if (mPopup != null) {
114d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            mPopup.setForceShowIcon(forceShowIcon);
115d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        }
116bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell    }
117ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani
1186142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
1196142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      * Sets the alignment of the popup window relative to the anchor view.
1206142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      * <p>
1216142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      * Changes take effect on the next call to show().
1226142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      *
1236142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      * @param gravity alignment of the popup relative to the anchor
1246142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      */
12549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    public void setGravity(int gravity) {
12649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        mDropDownGravity = gravity;
12749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    }
12849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
1296142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
1306142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @return alignment of the popup relative to the anchor
1316142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     */
13244918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes    public int getGravity() {
13344918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes        return mDropDownGravity;
13444918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes    }
13544918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes
136ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    public void show() {
137ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        if (!tryShow()) {
138ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani            throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
139ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        }
140bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell    }
141bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
1426142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public void show(int x, int y) {
1436142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (!tryShow(x, y)) {
1446142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
1456142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        }
1466142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    }
1476142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
1486142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    @NonNull
1496142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public MenuPopup getPopup() {
1506142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (mPopup == null) {
1516142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            mPopup = createPopup();
1526142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        }
15349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        return mPopup;
15449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    }
15549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
1566142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
1576142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * Attempts to show the popup anchored to the view specified by {@link #setAnchorView(View)}.
1586142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     *
1596142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @return {@code true} if the popup was shown or was already showing prior to calling this
1606142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     *         method, {@code false} otherwise
1616142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     */
162ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    public boolean tryShow() {
1636142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (isShowing()) {
1646142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            return true;
165ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        }
166bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
1676142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (mAnchorView == null) {
1686142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            return false;
16949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        }
17049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
1716142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        showPopup(0, 0, false, false);
172ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        return true;
173bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell    }
174ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani
1756142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
1766142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * Shows the popup menu and makes a best-effort to anchor it to the
1776142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * specified (x,y) coordinate relative to the anchor view.
1786142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * <p>
1796142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * Additionally, the popup's transition epicenter (see
1806142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * {@link android.widget.PopupWindow#setEpicenterBounds(Rect)} will be
1816142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * centered on the specified coordinate, rather than using the bounds of
1826142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * the anchor view.
1836142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * <p>
1846142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * If the popup's resolved gravity is {@link Gravity#LEFT}, this will
1856142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * display the popup with its top-left corner at (x,y) relative to the
1866142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * anchor view. If the resolved gravity is {@link Gravity#RIGHT}, the
1876142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * popup's top-right corner will be at (x,y).
1886142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * <p>
1896142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * If the popup cannot be displayed fully on-screen, this method will
1906142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * attempt to scroll the anchor view's ancestors and/or offset the popup
1916142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * such that it may be displayed fully on-screen.
1926142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     *
1936142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @param x x coordinate relative to the anchor view
1946142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @param y y coordinate relative to the anchor view
1956142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @return {@code true} if the popup was shown or was already showing prior
1966142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     *         to calling this method, {@code false} otherwise
1976142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     */
1986142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public boolean tryShow(int x, int y) {
199ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        if (isShowing()) {
2006142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            return true;
201ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        }
202ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani
2036142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (mAnchorView == null) {
2046142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            return false;
205ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        }
206ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani
2076142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        showPopup(x, y, true, true);
2086142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        return true;
209ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    }
210bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
2116142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
2126142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * Creates the popup and assigns cached properties.
2136142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     *
2146142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @return an initialized popup
2156142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     */
2166142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    @NonNull
2176142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private MenuPopup createPopup() {
218d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        final WindowManager windowManager = (WindowManager) mContext.getSystemService(
219d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes                Context.WINDOW_SERVICE);
220d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        final Display display = windowManager.getDefaultDisplay();
221d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        final Point displaySize = new Point();
222d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
223d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        if (Build.VERSION.SDK_INT >= 17) {
224d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            display.getRealSize(displaySize);
225d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        } else if (Build.VERSION.SDK_INT >= 13) {
226d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            display.getSize(displaySize);
227d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        } else {
228d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            displaySize.set(display.getWidth(), display.getHeight());
229d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        }
230d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
231d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        final int smallestWidth = Math.min(displaySize.x, displaySize.y);
232d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        final int minSmallestWidthCascading = mContext.getResources().getDimensionPixelSize(
233d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes                R.dimen.abc_cascading_menus_min_smallest_width);
234d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        final boolean enableCascadingSubmenus = smallestWidth >= minSmallestWidthCascading;
2356142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
2366142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        final MenuPopup popup;
2376142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (enableCascadingSubmenus) {
2386142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            popup = new CascadingMenuPopup(mContext, mAnchorView, mPopupStyleAttr,
2396142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                    mPopupStyleRes, mOverflowOnly);
2406142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        } else {
2416142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            popup = new StandardMenuPopup(mContext, mMenu, mAnchorView, mPopupStyleAttr,
2426142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                    mPopupStyleRes, mOverflowOnly);
243ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        }
244ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani
2456142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        // Assign immutable properties.
2466142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        popup.addMenu(mMenu);
2476142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        popup.setOnDismissListener(mInternalOnDismissListener);
24849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
2496142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        // Assign mutable properties. These may be reassigned later.
2506142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        popup.setAnchorView(mAnchorView);
2516142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        popup.setCallback(mPresenterCallback);
2526142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        popup.setForceShowIcon(mForceShowIcon);
2536142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        popup.setGravity(mDropDownGravity);
25449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
2556142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        return popup;
256ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    }
257bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
2586142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private void showPopup(int xOffset, int yOffset, boolean useOffsets, boolean showTitle) {
2596142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        final MenuPopup popup = getPopup();
2606142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        popup.setShowTitle(showTitle);
2616142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
2626142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (useOffsets) {
2636142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            // If the resolved drop-down gravity is RIGHT, the popup's right
2646142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            // edge will be aligned with the anchor view. Adjust by the anchor
2656142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            // width such that the top-right corner is at the X offset.
2666142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            final int hgrav = GravityCompat.getAbsoluteGravity(mDropDownGravity,
2676142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                    ViewCompat.getLayoutDirection(mAnchorView)) & Gravity.HORIZONTAL_GRAVITY_MASK;
2686142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            if (hgrav == Gravity.RIGHT) {
2696142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                xOffset -= mAnchorView.getWidth();
270ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani            }
271bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
2726142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            popup.setHorizontalOffset(xOffset);
2736142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            popup.setVerticalOffset(yOffset);
2746142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
2756142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            // Set the transition epicenter to be roughly finger (or mouse
2766142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            // cursor) sized and centered around the offset position. This
2776142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            // will give the appearance that the window is emerging from
2786142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            // the touch point.
2796142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            final float density = mContext.getResources().getDisplayMetrics().density;
2806142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            final int halfSize = (int) (TOUCH_EPICENTER_SIZE_DP * density / 2);
2816142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            final Rect epicenter = new Rect(xOffset - halfSize, yOffset - halfSize,
2826142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                    xOffset + halfSize, yOffset + halfSize);
2836142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            popup.setEpicenterBounds(epicenter);
28420ac724a3a836bfd362c911f7dc55a61c02b4d44Trevor Johns        }
285bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
2866142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        popup.show();
287bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell    }
288bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
2896142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
2906142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * Dismisses the popup, if showing.
2916142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     */
292ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    @Override
2936142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public void dismiss() {
2946142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (isShowing()) {
2956142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            mPopup.dismiss();
296ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        }
297bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell    }
298bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
2996142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
3006142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * Called after the popup has been dismissed.
3016142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * <p>
3026142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * <strong>Note:</strong> Subclasses should call the super implementation
3036142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * last to ensure that any necessary tear down has occurred before the
3046142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * listener specified by {@link #setOnDismissListener(OnDismissListener)}
3056142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * is called.
3066142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     */
3076142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    protected void onDismiss() {
3086142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        mPopup = null;
309ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani
3106142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (mOnDismissListener != null) {
3116142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            mOnDismissListener.onDismiss();
312bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell        }
313bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell    }
314bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
3156142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public boolean isShowing() {
3166142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        return mPopup != null && mPopup.isShowing();
317ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    }
318ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani
319ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    @Override
3206142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public void setPresenterCallback(@Nullable MenuPresenter.Callback cb) {
3216142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        mPresenterCallback = cb;
3226142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (mPopup != null) {
3236142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            mPopup.setCallback(cb);
324ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        }
3256142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    }
326ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani
3276142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
3286142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * Listener used to proxy dismiss callbacks to the helper's owner.
3296142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     */
3306142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private final OnDismissListener mInternalOnDismissListener = new OnDismissListener() {
331ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        @Override
3326142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        public void onDismiss() {
3336142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            MenuPopupHelper.this.onDismiss();
334ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        }
3356142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    };
336bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell}
337