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;
26c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport android.support.annotation.RestrictTo;
276142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.annotation.StyleRes;
286142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.v4.view.GravityCompat;
296142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.v4.view.ViewCompat;
30da10fdd1400ecfd8d7f2e55651dd528d0614dfc5Jeff Brownimport android.support.v7.appcompat.R;
31d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banesimport android.view.Display;
3249c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.view.Gravity;
33bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powellimport android.view.View;
34d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banesimport android.view.WindowManager;
356142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.widget.PopupWindow.OnDismissListener;
36bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
37c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport static android.support.annotation.RestrictTo.Scope.GROUP_ID;
38c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette
39bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell/**
40bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * Presents a menu as a small, simple popup anchored to another view.
41ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani *
42bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * @hide
43bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell */
44c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette@RestrictTo(GROUP_ID)
456142a54baae3289f734947c6b5375b12eb0fb722Chris Banespublic class MenuPopupHelper implements MenuHelper {
466142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private static final int TOUCH_EPICENTER_SIZE_DP = 48;
47bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
4849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    private final Context mContext;
496142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
506142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    // Immutable cached popup menu properties.
5149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    private final MenuBuilder mMenu;
5249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    private final boolean mOverflowOnly;
5349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    private final int mPopupStyleAttr;
54a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette    private final int mPopupStyleRes;
5549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
566142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    // Mutable cached popup menu properties.
57ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    private View mAnchorView;
586142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private int mDropDownGravity = Gravity.START;
596142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private boolean mForceShowIcon;
606142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private MenuPresenter.Callback mPresenterCallback;
6149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
626142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private MenuPopup mPopup;
636142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private OnDismissListener mOnDismissListener;
6449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
656142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu) {
666142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        this(context, menu, null, false, R.attr.popupMenuStyle, 0);
67ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    }
68bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
696142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu,
706142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            @NonNull View anchorView) {
716142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        this(context, menu, anchorView, false, R.attr.popupMenuStyle, 0);
72ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    }
73bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
746142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu,
756142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            @NonNull View anchorView,
766142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            boolean overflowOnly, @AttrRes int popupStyleAttr) {
77a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette        this(context, menu, anchorView, overflowOnly, popupStyleAttr, 0);
78a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette    }
79a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette
806142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu,
816142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            @NonNull View anchorView, boolean overflowOnly, @AttrRes int popupStyleAttr,
826142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            @StyleRes int popupStyleRes) {
83ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        mContext = context;
84ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        mMenu = menu;
856142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        mAnchorView = anchorView;
86ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        mOverflowOnly = overflowOnly;
8749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        mPopupStyleAttr = popupStyleAttr;
88a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette        mPopupStyleRes = popupStyleRes;
896142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    }
90bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
916142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public void setOnDismissListener(@Nullable OnDismissListener listener) {
926142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        mOnDismissListener = listener;
93ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    }
94bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
956142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
966142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      * Sets the view to which the popup window is anchored.
976142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      * <p>
986142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      * Changes take effect on the next call to show().
996142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      *
1006142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      * @param anchor the view to which the popup window should be anchored
1016142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      */
1026142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public void setAnchorView(@NonNull View anchor) {
103ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        mAnchorView = anchor;
104ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    }
105bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
1066142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
1076142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * Sets whether the popup menu's adapter is forced to show icons in the
1086142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * menu item views.
1096142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * <p>
1106142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * Changes take effect on the next call to show().
1116142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     *
1126142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @param forceShowIcon {@code true} to force icons to be shown, or
1136142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     *                  {@code false} for icons to be optionally shown
1146142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     */
1156142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public void setForceShowIcon(boolean forceShowIcon) {
1166142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        mForceShowIcon = forceShowIcon;
117d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        if (mPopup != null) {
118d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            mPopup.setForceShowIcon(forceShowIcon);
119d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        }
120bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell    }
121ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani
1226142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
1236142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      * Sets the alignment of the popup window relative to the anchor view.
1246142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      * <p>
1256142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      * Changes take effect on the next call to show().
1266142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      *
1276142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      * @param gravity alignment of the popup relative to the anchor
1286142a54baae3289f734947c6b5375b12eb0fb722Chris Banes      */
12949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    public void setGravity(int gravity) {
13049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        mDropDownGravity = gravity;
13149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    }
13249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
1336142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
1346142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @return alignment of the popup relative to the anchor
1356142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     */
13644918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes    public int getGravity() {
13744918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes        return mDropDownGravity;
13844918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes    }
13944918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes
140ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    public void show() {
141ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        if (!tryShow()) {
142ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani            throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
143ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        }
144bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell    }
145bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
1466142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public void show(int x, int y) {
1476142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (!tryShow(x, y)) {
1486142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
1496142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        }
1506142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    }
1516142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
1526142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    @NonNull
1536142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public MenuPopup getPopup() {
1546142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (mPopup == null) {
1556142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            mPopup = createPopup();
1566142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        }
15749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        return mPopup;
15849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    }
15949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
1606142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
1616142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * Attempts to show the popup anchored to the view specified by {@link #setAnchorView(View)}.
1626142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     *
1636142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @return {@code true} if the popup was shown or was already showing prior to calling this
1646142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     *         method, {@code false} otherwise
1656142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     */
166ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    public boolean tryShow() {
1676142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (isShowing()) {
1686142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            return true;
169ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        }
170bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
1716142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (mAnchorView == null) {
1726142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            return false;
17349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        }
17449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
1756142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        showPopup(0, 0, false, false);
176ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        return true;
177bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell    }
178ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani
1796142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
1806142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * Shows the popup menu and makes a best-effort to anchor it to the
1816142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * specified (x,y) coordinate relative to the anchor view.
1826142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * <p>
1836142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * Additionally, the popup's transition epicenter (see
1846142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * {@link android.widget.PopupWindow#setEpicenterBounds(Rect)} will be
1856142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * centered on the specified coordinate, rather than using the bounds of
1866142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * the anchor view.
1876142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * <p>
1886142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * If the popup's resolved gravity is {@link Gravity#LEFT}, this will
1896142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * display the popup with its top-left corner at (x,y) relative to the
1906142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * anchor view. If the resolved gravity is {@link Gravity#RIGHT}, the
1916142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * popup's top-right corner will be at (x,y).
1926142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * <p>
1936142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * If the popup cannot be displayed fully on-screen, this method will
1946142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * attempt to scroll the anchor view's ancestors and/or offset the popup
1956142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * such that it may be displayed fully on-screen.
1966142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     *
1976142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @param x x coordinate relative to the anchor view
1986142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @param y y coordinate relative to the anchor view
1996142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @return {@code true} if the popup was shown or was already showing prior
2006142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     *         to calling this method, {@code false} otherwise
2016142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     */
2026142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public boolean tryShow(int x, int y) {
203ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        if (isShowing()) {
2046142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            return true;
205ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        }
206ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani
2076142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (mAnchorView == null) {
2086142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            return false;
209ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        }
210ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani
2116142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        showPopup(x, y, true, true);
2126142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        return true;
213ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    }
214bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
2156142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
2166142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * Creates the popup and assigns cached properties.
2176142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     *
2186142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @return an initialized popup
2196142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     */
2206142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    @NonNull
2216142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private MenuPopup createPopup() {
222d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        final WindowManager windowManager = (WindowManager) mContext.getSystemService(
223d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes                Context.WINDOW_SERVICE);
224d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        final Display display = windowManager.getDefaultDisplay();
225d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        final Point displaySize = new Point();
226d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
227d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        if (Build.VERSION.SDK_INT >= 17) {
228d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            display.getRealSize(displaySize);
229d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        } else if (Build.VERSION.SDK_INT >= 13) {
230d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            display.getSize(displaySize);
231d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        } else {
232d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            displaySize.set(display.getWidth(), display.getHeight());
233d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        }
234d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
235d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        final int smallestWidth = Math.min(displaySize.x, displaySize.y);
236d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        final int minSmallestWidthCascading = mContext.getResources().getDimensionPixelSize(
237d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes                R.dimen.abc_cascading_menus_min_smallest_width);
238d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        final boolean enableCascadingSubmenus = smallestWidth >= minSmallestWidthCascading;
2396142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
2406142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        final MenuPopup popup;
2416142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (enableCascadingSubmenus) {
2426142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            popup = new CascadingMenuPopup(mContext, mAnchorView, mPopupStyleAttr,
2436142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                    mPopupStyleRes, mOverflowOnly);
2446142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        } else {
2456142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            popup = new StandardMenuPopup(mContext, mMenu, mAnchorView, mPopupStyleAttr,
2466142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                    mPopupStyleRes, mOverflowOnly);
247ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        }
248ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani
2496142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        // Assign immutable properties.
2506142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        popup.addMenu(mMenu);
2516142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        popup.setOnDismissListener(mInternalOnDismissListener);
25249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
2536142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        // Assign mutable properties. These may be reassigned later.
2546142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        popup.setAnchorView(mAnchorView);
2556142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        popup.setCallback(mPresenterCallback);
2566142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        popup.setForceShowIcon(mForceShowIcon);
2576142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        popup.setGravity(mDropDownGravity);
25849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
2596142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        return popup;
260ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    }
261bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
2626142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private void showPopup(int xOffset, int yOffset, boolean useOffsets, boolean showTitle) {
2636142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        final MenuPopup popup = getPopup();
2646142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        popup.setShowTitle(showTitle);
2656142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
2666142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (useOffsets) {
2676142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            // If the resolved drop-down gravity is RIGHT, the popup's right
2686142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            // edge will be aligned with the anchor view. Adjust by the anchor
2696142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            // width such that the top-right corner is at the X offset.
2706142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            final int hgrav = GravityCompat.getAbsoluteGravity(mDropDownGravity,
2716142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                    ViewCompat.getLayoutDirection(mAnchorView)) & Gravity.HORIZONTAL_GRAVITY_MASK;
2726142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            if (hgrav == Gravity.RIGHT) {
2736142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                xOffset -= mAnchorView.getWidth();
274ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani            }
275bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
2766142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            popup.setHorizontalOffset(xOffset);
2776142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            popup.setVerticalOffset(yOffset);
2786142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
2796142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            // Set the transition epicenter to be roughly finger (or mouse
2806142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            // cursor) sized and centered around the offset position. This
2816142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            // will give the appearance that the window is emerging from
2826142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            // the touch point.
2836142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            final float density = mContext.getResources().getDisplayMetrics().density;
2846142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            final int halfSize = (int) (TOUCH_EPICENTER_SIZE_DP * density / 2);
2856142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            final Rect epicenter = new Rect(xOffset - halfSize, yOffset - halfSize,
2866142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                    xOffset + halfSize, yOffset + halfSize);
2876142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            popup.setEpicenterBounds(epicenter);
28820ac724a3a836bfd362c911f7dc55a61c02b4d44Trevor Johns        }
289bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
2906142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        popup.show();
291bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell    }
292bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
2936142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
2946142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * Dismisses the popup, if showing.
2956142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     */
296ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    @Override
2976142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public void dismiss() {
2986142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (isShowing()) {
2996142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            mPopup.dismiss();
300ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        }
301bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell    }
302bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
3036142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
3046142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * Called after the popup has been dismissed.
3056142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * <p>
3066142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * <strong>Note:</strong> Subclasses should call the super implementation
3076142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * last to ensure that any necessary tear down has occurred before the
3086142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * listener specified by {@link #setOnDismissListener(OnDismissListener)}
3096142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * is called.
3106142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     */
3116142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    protected void onDismiss() {
3126142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        mPopup = null;
313ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani
3146142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (mOnDismissListener != null) {
3156142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            mOnDismissListener.onDismiss();
316bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell        }
317bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell    }
318bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell
3196142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public boolean isShowing() {
3206142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        return mPopup != null && mPopup.isShowing();
321ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    }
322ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani
323ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani    @Override
3246142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public void setPresenterCallback(@Nullable MenuPresenter.Callback cb) {
3256142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        mPresenterCallback = cb;
3266142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (mPopup != null) {
3276142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            mPopup.setCallback(cb);
328ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        }
3296142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    }
330ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani
3316142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
3326142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * Listener used to proxy dismiss callbacks to the helper's owner.
3336142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     */
3346142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private final OnDismissListener mInternalOnDismissListener = new OnDismissListener() {
335ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        @Override
3366142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        public void onDismiss() {
3376142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            MenuPopupHelper.this.onDismiss();
338ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani        }
3396142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    };
340bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell}
341