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 17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.appcompat.view.menu; 18bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 19ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; 208e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas 21bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powellimport android.content.Context; 22d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banesimport android.graphics.Point; 236142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.graphics.Rect; 24d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banesimport android.os.Build; 253de8a4e8305507475d7890205184946a25cf45e7Aurimas Liutikasimport android.view.Display; 263de8a4e8305507475d7890205184946a25cf45e7Aurimas Liutikasimport android.view.Gravity; 273de8a4e8305507475d7890205184946a25cf45e7Aurimas Liutikasimport android.view.View; 283de8a4e8305507475d7890205184946a25cf45e7Aurimas Liutikasimport android.view.WindowManager; 293de8a4e8305507475d7890205184946a25cf45e7Aurimas Liutikasimport android.widget.ListView; 303de8a4e8305507475d7890205184946a25cf45e7Aurimas Liutikasimport android.widget.PopupWindow.OnDismissListener; 313de8a4e8305507475d7890205184946a25cf45e7Aurimas Liutikas 32ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.AttrRes; 33ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.NonNull; 34ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.Nullable; 35ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.RestrictTo; 36ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.StyleRes; 373de8a4e8305507475d7890205184946a25cf45e7Aurimas Liutikasimport androidx.appcompat.R; 38ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.core.view.GravityCompat; 39ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.core.view.ViewCompat; 40bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 41bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell/** 42bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * Presents a menu as a small, simple popup anchored to another view. 43ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani * 44bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * @hide 45bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell */ 468e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas@RestrictTo(LIBRARY_GROUP) 476142a54baae3289f734947c6b5375b12eb0fb722Chris Banespublic class MenuPopupHelper implements MenuHelper { 486142a54baae3289f734947c6b5375b12eb0fb722Chris Banes private static final int TOUCH_EPICENTER_SIZE_DP = 48; 49bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 5049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes private final Context mContext; 516142a54baae3289f734947c6b5375b12eb0fb722Chris Banes 526142a54baae3289f734947c6b5375b12eb0fb722Chris Banes // Immutable cached popup menu properties. 5349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes private final MenuBuilder mMenu; 5449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes private final boolean mOverflowOnly; 5549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes private final int mPopupStyleAttr; 56a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette private final int mPopupStyleRes; 5749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 586142a54baae3289f734947c6b5375b12eb0fb722Chris Banes // Mutable cached popup menu properties. 59ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani private View mAnchorView; 606142a54baae3289f734947c6b5375b12eb0fb722Chris Banes private int mDropDownGravity = Gravity.START; 616142a54baae3289f734947c6b5375b12eb0fb722Chris Banes private boolean mForceShowIcon; 626142a54baae3289f734947c6b5375b12eb0fb722Chris Banes private MenuPresenter.Callback mPresenterCallback; 6349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 646142a54baae3289f734947c6b5375b12eb0fb722Chris Banes private MenuPopup mPopup; 656142a54baae3289f734947c6b5375b12eb0fb722Chris Banes private OnDismissListener mOnDismissListener; 6649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 676142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu) { 686142a54baae3289f734947c6b5375b12eb0fb722Chris Banes this(context, menu, null, false, R.attr.popupMenuStyle, 0); 69ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 70bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 716142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu, 726142a54baae3289f734947c6b5375b12eb0fb722Chris Banes @NonNull View anchorView) { 736142a54baae3289f734947c6b5375b12eb0fb722Chris Banes this(context, menu, anchorView, false, R.attr.popupMenuStyle, 0); 74ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 75bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 766142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu, 776142a54baae3289f734947c6b5375b12eb0fb722Chris Banes @NonNull View anchorView, 786142a54baae3289f734947c6b5375b12eb0fb722Chris Banes boolean overflowOnly, @AttrRes int popupStyleAttr) { 79a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette this(context, menu, anchorView, overflowOnly, popupStyleAttr, 0); 80a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette } 81a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette 826142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu, 836142a54baae3289f734947c6b5375b12eb0fb722Chris Banes @NonNull View anchorView, boolean overflowOnly, @AttrRes int popupStyleAttr, 846142a54baae3289f734947c6b5375b12eb0fb722Chris Banes @StyleRes int popupStyleRes) { 85ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani mContext = context; 86ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani mMenu = menu; 876142a54baae3289f734947c6b5375b12eb0fb722Chris Banes mAnchorView = anchorView; 88ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani mOverflowOnly = overflowOnly; 8949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes mPopupStyleAttr = popupStyleAttr; 90a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette mPopupStyleRes = popupStyleRes; 916142a54baae3289f734947c6b5375b12eb0fb722Chris Banes } 92bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 936142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public void setOnDismissListener(@Nullable OnDismissListener listener) { 946142a54baae3289f734947c6b5375b12eb0fb722Chris Banes mOnDismissListener = listener; 95ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 96bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 976142a54baae3289f734947c6b5375b12eb0fb722Chris Banes /** 986142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Sets the view to which the popup window is anchored. 996142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * <p> 1006142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Changes take effect on the next call to show(). 1016142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * 1026142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * @param anchor the view to which the popup window should be anchored 1036142a54baae3289f734947c6b5375b12eb0fb722Chris Banes */ 1046142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public void setAnchorView(@NonNull View anchor) { 105ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani mAnchorView = anchor; 106ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 107bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 1086142a54baae3289f734947c6b5375b12eb0fb722Chris Banes /** 1096142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Sets whether the popup menu's adapter is forced to show icons in the 1106142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * menu item views. 1116142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * <p> 1126142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Changes take effect on the next call to show(). 1136142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * 1146142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * @param forceShowIcon {@code true} to force icons to be shown, or 1156142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * {@code false} for icons to be optionally shown 1166142a54baae3289f734947c6b5375b12eb0fb722Chris Banes */ 1176142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public void setForceShowIcon(boolean forceShowIcon) { 1186142a54baae3289f734947c6b5375b12eb0fb722Chris Banes mForceShowIcon = forceShowIcon; 119d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes if (mPopup != null) { 120d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes mPopup.setForceShowIcon(forceShowIcon); 121d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes } 122bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell } 123ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 1246142a54baae3289f734947c6b5375b12eb0fb722Chris Banes /** 1256142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Sets the alignment of the popup window relative to the anchor view. 1266142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * <p> 1276142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Changes take effect on the next call to show(). 1286142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * 1296142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * @param gravity alignment of the popup relative to the anchor 1306142a54baae3289f734947c6b5375b12eb0fb722Chris Banes */ 13149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes public void setGravity(int gravity) { 13249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes mDropDownGravity = gravity; 13349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 13449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 1356142a54baae3289f734947c6b5375b12eb0fb722Chris Banes /** 1366142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * @return alignment of the popup relative to the anchor 1376142a54baae3289f734947c6b5375b12eb0fb722Chris Banes */ 13844918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes public int getGravity() { 13944918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes return mDropDownGravity; 14044918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes } 14144918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes 142ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public void show() { 143ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani if (!tryShow()) { 144ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor"); 145ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 146bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell } 147bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 1486142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public void show(int x, int y) { 1496142a54baae3289f734947c6b5375b12eb0fb722Chris Banes if (!tryShow(x, y)) { 1506142a54baae3289f734947c6b5375b12eb0fb722Chris Banes throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor"); 1516142a54baae3289f734947c6b5375b12eb0fb722Chris Banes } 1526142a54baae3289f734947c6b5375b12eb0fb722Chris Banes } 1536142a54baae3289f734947c6b5375b12eb0fb722Chris Banes 1546142a54baae3289f734947c6b5375b12eb0fb722Chris Banes @NonNull 1556142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public MenuPopup getPopup() { 1566142a54baae3289f734947c6b5375b12eb0fb722Chris Banes if (mPopup == null) { 1576142a54baae3289f734947c6b5375b12eb0fb722Chris Banes mPopup = createPopup(); 1586142a54baae3289f734947c6b5375b12eb0fb722Chris Banes } 15949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes return mPopup; 16049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 16149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 1626142a54baae3289f734947c6b5375b12eb0fb722Chris Banes /** 1636142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Attempts to show the popup anchored to the view specified by {@link #setAnchorView(View)}. 1646142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * 1656142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * @return {@code true} if the popup was shown or was already showing prior to calling this 1666142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * method, {@code false} otherwise 1676142a54baae3289f734947c6b5375b12eb0fb722Chris Banes */ 168ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public boolean tryShow() { 1696142a54baae3289f734947c6b5375b12eb0fb722Chris Banes if (isShowing()) { 1706142a54baae3289f734947c6b5375b12eb0fb722Chris Banes return true; 171ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 172bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 1736142a54baae3289f734947c6b5375b12eb0fb722Chris Banes if (mAnchorView == null) { 1746142a54baae3289f734947c6b5375b12eb0fb722Chris Banes return false; 17549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 17649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 1776142a54baae3289f734947c6b5375b12eb0fb722Chris Banes showPopup(0, 0, false, false); 178ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani return true; 179bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell } 180ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 1816142a54baae3289f734947c6b5375b12eb0fb722Chris Banes /** 1826142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Shows the popup menu and makes a best-effort to anchor it to the 1836142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * specified (x,y) coordinate relative to the anchor view. 1846142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * <p> 1856142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Additionally, the popup's transition epicenter (see 1866142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * {@link android.widget.PopupWindow#setEpicenterBounds(Rect)} will be 1876142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * centered on the specified coordinate, rather than using the bounds of 1886142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * the anchor view. 1896142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * <p> 1906142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * If the popup's resolved gravity is {@link Gravity#LEFT}, this will 1916142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * display the popup with its top-left corner at (x,y) relative to the 1926142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * anchor view. If the resolved gravity is {@link Gravity#RIGHT}, the 1936142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * popup's top-right corner will be at (x,y). 1946142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * <p> 1956142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * If the popup cannot be displayed fully on-screen, this method will 1966142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * attempt to scroll the anchor view's ancestors and/or offset the popup 1976142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * such that it may be displayed fully on-screen. 1986142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * 1996142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * @param x x coordinate relative to the anchor view 2006142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * @param y y coordinate relative to the anchor view 2016142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * @return {@code true} if the popup was shown or was already showing prior 2026142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * to calling this method, {@code false} otherwise 2036142a54baae3289f734947c6b5375b12eb0fb722Chris Banes */ 2046142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public boolean tryShow(int x, int y) { 205ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani if (isShowing()) { 2066142a54baae3289f734947c6b5375b12eb0fb722Chris Banes return true; 207ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 208ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 2096142a54baae3289f734947c6b5375b12eb0fb722Chris Banes if (mAnchorView == null) { 2106142a54baae3289f734947c6b5375b12eb0fb722Chris Banes return false; 211ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 212ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 2136142a54baae3289f734947c6b5375b12eb0fb722Chris Banes showPopup(x, y, true, true); 2146142a54baae3289f734947c6b5375b12eb0fb722Chris Banes return true; 215ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 216bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 2176142a54baae3289f734947c6b5375b12eb0fb722Chris Banes /** 2186142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Creates the popup and assigns cached properties. 2196142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * 2206142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * @return an initialized popup 2216142a54baae3289f734947c6b5375b12eb0fb722Chris Banes */ 2226142a54baae3289f734947c6b5375b12eb0fb722Chris Banes @NonNull 2236142a54baae3289f734947c6b5375b12eb0fb722Chris Banes private MenuPopup createPopup() { 224d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes final WindowManager windowManager = (WindowManager) mContext.getSystemService( 225d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes Context.WINDOW_SERVICE); 226d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes final Display display = windowManager.getDefaultDisplay(); 227d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes final Point displaySize = new Point(); 228d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes 229d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes if (Build.VERSION.SDK_INT >= 17) { 230d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes display.getRealSize(displaySize); 231d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes } else { 232722327e27579c196b92883c07f4b47e9efada8adKirill Grouchnikov display.getSize(displaySize); 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) { 273bda13fd1495c9162efbde73d812c5a31f811673aVladislav Kaznacheev 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 }; 340ef148f4e0f0c3e9b947f9c3b76ae85bf9777d891Vladislav Kaznacheev 341ef148f4e0f0c3e9b947f9c3b76ae85bf9777d891Vladislav Kaznacheev /** 342ef148f4e0f0c3e9b947f9c3b76ae85bf9777d891Vladislav Kaznacheev * API to get the underlying ListView of the Popup 343ef148f4e0f0c3e9b947f9c3b76ae85bf9777d891Vladislav Kaznacheev */ 344ef148f4e0f0c3e9b947f9c3b76ae85bf9777d891Vladislav Kaznacheev public ListView getListView() { 345ef148f4e0f0c3e9b947f9c3b76ae85bf9777d891Vladislav Kaznacheev return getPopup().getListView(); 346ef148f4e0f0c3e9b947f9c3b76ae85bf9777d891Vladislav Kaznacheev } 347bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell} 348