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