14267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell/* 24267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * Copyright (C) 2010 The Android Open Source Project 34267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * 44267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * Licensed under the Apache License, Version 2.0 (the "License"); 54267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * you may not use this file except in compliance with the License. 64267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * You may obtain a copy of the License at 74267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * 84267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * http://www.apache.org/licenses/LICENSE-2.0 94267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * 104267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * Unless required by applicable law or agreed to in writing, software 114267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * distributed under the License is distributed on an "AS IS" BASIS, 124267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * See the License for the specific language governing permissions and 144267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * limitations under the License. 154267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell */ 164267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 174267534d1c42af847ed0cefd1c88c99f66b36571Adam Powellpackage com.android.internal.view.menu; 184267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 199916282bac8ac8fab7ce5b649c049842acffa29bOren Blasbergimport com.android.internal.view.menu.MenuPresenter.Callback; 209916282bac8ac8fab7ce5b649c049842acffa29bOren Blasberg 21708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viveretteimport android.annotation.AttrRes; 22708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viveretteimport android.annotation.NonNull; 23708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viveretteimport android.annotation.Nullable; 24708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viveretteimport android.annotation.StyleRes; 254267534d1c42af847ed0cefd1c88c99f66b36571Adam Powellimport android.content.Context; 2693c195894de323b039ff62eb5c62bf9aeed2abf0Oren Blasbergimport android.graphics.Point; 2791098574f90277128415e9593cce1e495cc51465Alan Viveretteimport android.graphics.Rect; 2891098574f90277128415e9593cce1e495cc51465Alan Viveretteimport android.util.DisplayMetrics; 2993c195894de323b039ff62eb5c62bf9aeed2abf0Oren Blasbergimport android.view.Display; 3054c94dea8a26e66fa59a31fd9170ca221052d3aaAdam Powellimport android.view.Gravity; 314267534d1c42af847ed0cefd1c88c99f66b36571Adam Powellimport android.view.View; 3293c195894de323b039ff62eb5c62bf9aeed2abf0Oren Blasbergimport android.view.WindowManager; 33708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viveretteimport android.widget.PopupWindow.OnDismissListener; 344267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 354267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell/** 36696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell * Presents a menu as a small, simple popup anchored to another view. 374267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell */ 38021627eb5875372dea57ba91fa782fffbfbbc559Alan Viverettepublic class MenuPopupHelper implements MenuHelper { 3991098574f90277128415e9593cce1e495cc51465Alan Viverette private static final int TOUCH_EPICENTER_SIZE_DP = 48; 4091098574f90277128415e9593cce1e495cc51465Alan Viverette 410bce6ab8cd8aadc9784dda2f3f4cc9dd91b796c7Alan Viverette private final Context mContext; 42708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette 43708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette // Immutable cached popup menu properties. 440bce6ab8cd8aadc9784dda2f3f4cc9dd91b796c7Alan Viverette private final MenuBuilder mMenu; 450bce6ab8cd8aadc9784dda2f3f4cc9dd91b796c7Alan Viverette private final boolean mOverflowOnly; 46560f17098f90b15c8894cce127f2fed85f7aeb6bAlan Viverette private final int mPopupStyleAttr; 4729632521c36548d71b610fc4de864281bab4dfb4Alan Viverette private final int mPopupStyleRes; 480bce6ab8cd8aadc9784dda2f3f4cc9dd91b796c7Alan Viverette 49708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette // Mutable cached popup menu properties. 504afd62b18c52a55371ab923d54f93615ad68fd7aAdam Powell private View mAnchorView; 51d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette private int mDropDownGravity = Gravity.START; 52ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg private boolean mForceShowIcon; 539916282bac8ac8fab7ce5b649c049842acffa29bOren Blasberg private Callback mPresenterCallback; 54d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette 55708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette private MenuPopup mPopup; 56708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette private OnDismissListener mOnDismissListener; 57d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette 58708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu) { 5929632521c36548d71b610fc4de864281bab4dfb4Alan Viverette this(context, menu, null, false, com.android.internal.R.attr.popupMenuStyle, 0); 608028dd32a4a04154050220dd0693583d5b750330Adam Powell } 614267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 62708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu, 63708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette @NonNull View anchorView) { 6429632521c36548d71b610fc4de864281bab4dfb4Alan Viverette this(context, menu, anchorView, false, com.android.internal.R.attr.popupMenuStyle, 0); 658028dd32a4a04154050220dd0693583d5b750330Adam Powell } 668028dd32a4a04154050220dd0693583d5b750330Adam Powell 67708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu, 68708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette @NonNull View anchorView, 69708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette boolean overflowOnly, @AttrRes int popupStyleAttr) { 7029632521c36548d71b610fc4de864281bab4dfb4Alan Viverette this(context, menu, anchorView, overflowOnly, popupStyleAttr, 0); 7129632521c36548d71b610fc4de864281bab4dfb4Alan Viverette } 7229632521c36548d71b610fc4de864281bab4dfb4Alan Viverette 73708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu, 74708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette @NonNull View anchorView, boolean overflowOnly, @AttrRes int popupStyleAttr, 75708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette @StyleRes int popupStyleRes) { 764267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell mContext = context; 778028dd32a4a04154050220dd0693583d5b750330Adam Powell mMenu = menu; 78708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette mAnchorView = anchorView; 798028dd32a4a04154050220dd0693583d5b750330Adam Powell mOverflowOnly = overflowOnly; 80560f17098f90b15c8894cce127f2fed85f7aeb6bAlan Viverette mPopupStyleAttr = popupStyleAttr; 8129632521c36548d71b610fc4de864281bab4dfb4Alan Viverette mPopupStyleRes = popupStyleRes; 82b23976efdd6ffe42cb3b8fe6650fc77bd9a161e8Oren Blasberg } 83696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell 84708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette public void setOnDismissListener(@Nullable OnDismissListener listener) { 85708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette mOnDismissListener = listener; 864267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell } 874267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 88708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette /** 89708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * Sets the view to which the popup window is anchored. 90708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * <p> 91708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * Changes take effect on the next call to show(). 92708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * 93708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * @param anchor the view to which the popup window should be anchored 94708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette */ 95708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette public void setAnchorView(@NonNull View anchor) { 964afd62b18c52a55371ab923d54f93615ad68fd7aAdam Powell mAnchorView = anchor; 97f0ad6e6eaf48ac8f4007232ad0a8511a7b5cfc0eAdam Powell } 98f0ad6e6eaf48ac8f4007232ad0a8511a7b5cfc0eAdam Powell 99708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette /** 100708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * Sets whether the popup menu's adapter is forced to show icons in the 101708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * menu item views. 102708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * <p> 103708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * Changes take effect on the next call to show(). 104708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * 105708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * @param forceShowIcon {@code true} to force icons to be shown, or 106708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * {@code false} for icons to be optionally shown 107708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette */ 108708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette public void setForceShowIcon(boolean forceShowIcon) { 109708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette mForceShowIcon = forceShowIcon; 1107b457455c40a6f159d9d82b2fe2e03bc26e9c55eOren Blasberg if (mPopup != null) { 1117b457455c40a6f159d9d82b2fe2e03bc26e9c55eOren Blasberg mPopup.setForceShowIcon(forceShowIcon); 1127b457455c40a6f159d9d82b2fe2e03bc26e9c55eOren Blasberg } 1139151103ff20d28e8db2a2cc0386d57b8dad4b5d5Adam Powell } 1149151103ff20d28e8db2a2cc0386d57b8dad4b5d5Adam Powell 115708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette /** 116708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * Sets the alignment of the popup window relative to the anchor view. 117708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * <p> 118708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * Changes take effect on the next call to show(). 119708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * 120708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * @param gravity alignment of the popup relative to the anchor 121708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette */ 12254c94dea8a26e66fa59a31fd9170ca221052d3aaAdam Powell public void setGravity(int gravity) { 12354c94dea8a26e66fa59a31fd9170ca221052d3aaAdam Powell mDropDownGravity = gravity; 12454c94dea8a26e66fa59a31fd9170ca221052d3aaAdam Powell } 12554c94dea8a26e66fa59a31fd9170ca221052d3aaAdam Powell 126708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette /** 127708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * @return alignment of the popup relative to the anchor 128708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette */ 12975d837954c3673647e3a899f03cd56c0892066e0Alan Viverette public int getGravity() { 13075d837954c3673647e3a899f03cd56c0892066e0Alan Viverette return mDropDownGravity; 13175d837954c3673647e3a899f03cd56c0892066e0Alan Viverette } 13275d837954c3673647e3a899f03cd56c0892066e0Alan Viverette 1334267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell public void show() { 1345e3f284baa271cb0fbf90e504d19fdd2e385382eAdam Powell if (!tryShow()) { 1355e3f284baa271cb0fbf90e504d19fdd2e385382eAdam Powell throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor"); 1365e3f284baa271cb0fbf90e504d19fdd2e385382eAdam Powell } 1375e3f284baa271cb0fbf90e504d19fdd2e385382eAdam Powell } 1385e3f284baa271cb0fbf90e504d19fdd2e385382eAdam Powell 139ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg public void show(int x, int y) { 140ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg if (!tryShow(x, y)) { 141ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor"); 142ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg } 143ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg } 144ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg 145708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette @NonNull 146708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette public MenuPopup getPopup() { 147708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette if (mPopup == null) { 148708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette mPopup = createPopup(); 149708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette } 150ca6a3611cdb28a514834adba35fcce2da6f2e7c2Alan Viverette return mPopup; 151ca6a3611cdb28a514834adba35fcce2da6f2e7c2Alan Viverette } 152ca6a3611cdb28a514834adba35fcce2da6f2e7c2Alan Viverette 1538fd949e680c15d397084430d4907c16cedfacddaAlan Viverette /** 154b23976efdd6ffe42cb3b8fe6650fc77bd9a161e8Oren Blasberg * Attempts to show the popup anchored to the view specified by {@link #setAnchorView(View)}. 1558fd949e680c15d397084430d4907c16cedfacddaAlan Viverette * 156b23976efdd6ffe42cb3b8fe6650fc77bd9a161e8Oren Blasberg * @return {@code true} if the popup was shown or was already showing prior to calling this 157b23976efdd6ffe42cb3b8fe6650fc77bd9a161e8Oren Blasberg * method, {@code false} otherwise 1588fd949e680c15d397084430d4907c16cedfacddaAlan Viverette */ 1595e3f284baa271cb0fbf90e504d19fdd2e385382eAdam Powell public boolean tryShow() { 1608fd949e680c15d397084430d4907c16cedfacddaAlan Viverette if (isShowing()) { 1618fd949e680c15d397084430d4907c16cedfacddaAlan Viverette return true; 1628fd949e680c15d397084430d4907c16cedfacddaAlan Viverette } 1638fd949e680c15d397084430d4907c16cedfacddaAlan Viverette 1649916282bac8ac8fab7ce5b649c049842acffa29bOren Blasberg if (mAnchorView == null) { 1655e3f284baa271cb0fbf90e504d19fdd2e385382eAdam Powell return false; 1668028dd32a4a04154050220dd0693583d5b750330Adam Powell } 1674267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 168708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette showPopup(0, 0, false, false); 169ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg return true; 170ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg } 171ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg 172d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette /** 173d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette * Shows the popup menu and makes a best-effort to anchor it to the 174d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette * specified (x,y) coordinate relative to the anchor view. 175d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette * <p> 17691098574f90277128415e9593cce1e495cc51465Alan Viverette * Additionally, the popup's transition epicenter (see 17791098574f90277128415e9593cce1e495cc51465Alan Viverette * {@link android.widget.PopupWindow#setEpicenterBounds(Rect)} will be 17891098574f90277128415e9593cce1e495cc51465Alan Viverette * centered on the specified coordinate, rather than using the bounds of 17991098574f90277128415e9593cce1e495cc51465Alan Viverette * the anchor view. 18091098574f90277128415e9593cce1e495cc51465Alan Viverette * <p> 181d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette * If the popup's resolved gravity is {@link Gravity#LEFT}, this will 182d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette * display the popup with its top-left corner at (x,y) relative to the 183d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette * anchor view. If the resolved gravity is {@link Gravity#RIGHT}, the 184d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette * popup's top-right corner will be at (x,y). 185d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette * <p> 186d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette * If the popup cannot be displayed fully on-screen, this method will 187d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette * attempt to scroll the anchor view's ancestors and/or offset the popup 188d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette * such that it may be displayed fully on-screen. 189d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette * 190d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette * @param x x coordinate relative to the anchor view 191d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette * @param y y coordinate relative to the anchor view 192d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette * @return {@code true} if the popup was shown or was already showing prior 193d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette * to calling this method, {@code false} otherwise 194d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette */ 195ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg public boolean tryShow(int x, int y) { 196ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg if (isShowing()) { 197ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg return true; 198ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg } 199ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg 200ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg if (mAnchorView == null) { 201ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg return false; 202ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg } 203ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg 204708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette showPopup(x, y, true, true); 205ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg return true; 206ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg } 207ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg 208708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette /** 209708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * Creates the popup and assigns cached properties. 210708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * 211708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * @return an initialized popup 212708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette */ 213708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette @NonNull 214708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette private MenuPopup createPopup() { 21593c195894de323b039ff62eb5c62bf9aeed2abf0Oren Blasberg final WindowManager windowManager = (WindowManager) mContext.getSystemService( 21693c195894de323b039ff62eb5c62bf9aeed2abf0Oren Blasberg Context.WINDOW_SERVICE); 21793c195894de323b039ff62eb5c62bf9aeed2abf0Oren Blasberg final Display display = windowManager.getDefaultDisplay(); 21893c195894de323b039ff62eb5c62bf9aeed2abf0Oren Blasberg final Point displaySize = new Point(); 21993c195894de323b039ff62eb5c62bf9aeed2abf0Oren Blasberg display.getRealSize(displaySize); 22093c195894de323b039ff62eb5c62bf9aeed2abf0Oren Blasberg 22193c195894de323b039ff62eb5c62bf9aeed2abf0Oren Blasberg final int smallestWidth = Math.min(displaySize.x, displaySize.y); 22293c195894de323b039ff62eb5c62bf9aeed2abf0Oren Blasberg final int minSmallestWidthCascading = mContext.getResources().getDimensionPixelSize( 22393c195894de323b039ff62eb5c62bf9aeed2abf0Oren Blasberg com.android.internal.R.dimen.cascading_menus_min_smallest_width); 22493c195894de323b039ff62eb5c62bf9aeed2abf0Oren Blasberg final boolean enableCascadingSubmenus = smallestWidth >= minSmallestWidthCascading; 225708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette 226708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette final MenuPopup popup; 227708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette if (enableCascadingSubmenus) { 228708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette popup = new CascadingMenuPopup(mContext, mAnchorView, mPopupStyleAttr, 229708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette mPopupStyleRes, mOverflowOnly); 230708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette } else { 231708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette popup = new StandardMenuPopup(mContext, mMenu, mAnchorView, mPopupStyleAttr, 232708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette mPopupStyleRes, mOverflowOnly); 233708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette } 234708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette 235708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette // Assign immutable properties. 236708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette popup.addMenu(mMenu); 237708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette popup.setOnDismissListener(mInternalOnDismissListener); 238708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette 239708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette // Assign mutable properties. These may be reassigned later. 240708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette popup.setAnchorView(mAnchorView); 241708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette popup.setCallback(mPresenterCallback); 242708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette popup.setForceShowIcon(mForceShowIcon); 243708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette popup.setGravity(mDropDownGravity); 244d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette 245708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette return popup; 246708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette } 247708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette 24891098574f90277128415e9593cce1e495cc51465Alan Viverette private void showPopup(int xOffset, int yOffset, boolean useOffsets, boolean showTitle) { 24991098574f90277128415e9593cce1e495cc51465Alan Viverette final MenuPopup popup = getPopup(); 25091098574f90277128415e9593cce1e495cc51465Alan Viverette popup.setShowTitle(showTitle); 25191098574f90277128415e9593cce1e495cc51465Alan Viverette 25291098574f90277128415e9593cce1e495cc51465Alan Viverette if (useOffsets) { 253d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette // If the resolved drop-down gravity is RIGHT, the popup's right 254d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette // edge will be aligned with the anchor view. Adjust by the anchor 255d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette // width such that the top-right corner is at the X offset. 256d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette final int hgrav = Gravity.getAbsoluteGravity(mDropDownGravity, 257d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette mAnchorView.getLayoutDirection()) & Gravity.HORIZONTAL_GRAVITY_MASK; 258d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette if (hgrav == Gravity.RIGHT) { 259d959c9d203ea41fede8f6b0a54ebb6ef2790e615Vladislav Kaznacheev xOffset -= mAnchorView.getWidth(); 260d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette } 26191098574f90277128415e9593cce1e495cc51465Alan Viverette 26291098574f90277128415e9593cce1e495cc51465Alan Viverette popup.setHorizontalOffset(xOffset); 26391098574f90277128415e9593cce1e495cc51465Alan Viverette popup.setVerticalOffset(yOffset); 26491098574f90277128415e9593cce1e495cc51465Alan Viverette 26591098574f90277128415e9593cce1e495cc51465Alan Viverette // Set the transition epicenter to be roughly finger (or mouse 26691098574f90277128415e9593cce1e495cc51465Alan Viverette // cursor) sized and centered around the offset position. This 26791098574f90277128415e9593cce1e495cc51465Alan Viverette // will give the appearance that the window is emerging from 26891098574f90277128415e9593cce1e495cc51465Alan Viverette // the touch point. 26991098574f90277128415e9593cce1e495cc51465Alan Viverette final float density = mContext.getResources().getDisplayMetrics().density; 27091098574f90277128415e9593cce1e495cc51465Alan Viverette final int halfSize = (int) (TOUCH_EPICENTER_SIZE_DP * density / 2); 27191098574f90277128415e9593cce1e495cc51465Alan Viverette final Rect epicenter = new Rect(xOffset - halfSize, yOffset - halfSize, 27291098574f90277128415e9593cce1e495cc51465Alan Viverette xOffset + halfSize, yOffset + halfSize); 27391098574f90277128415e9593cce1e495cc51465Alan Viverette popup.setEpicenterBounds(epicenter); 274d6443f60e1d556a772a8b33ec172d2bae44de63aAlan Viverette } 2759916282bac8ac8fab7ce5b649c049842acffa29bOren Blasberg 276708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette popup.show(); 2774267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell } 2784267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 279708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette /** 280708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * Dismisses the popup, if showing. 281708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette */ 282021627eb5875372dea57ba91fa782fffbfbbc559Alan Viverette @Override 2834267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell public void dismiss() { 2843d3da27ab394108fd51771616cca3279baae99d1Adam Powell if (isShowing()) { 2853d3da27ab394108fd51771616cca3279baae99d1Adam Powell mPopup.dismiss(); 2863d3da27ab394108fd51771616cca3279baae99d1Adam Powell } 2878515ee846bd76aee86ec5ddfcc4dd1e626dd999cAdam Powell } 2888515ee846bd76aee86ec5ddfcc4dd1e626dd999cAdam Powell 289708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette /** 290708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * Called after the popup has been dismissed. 291708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * <p> 292708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * <strong>Note:</strong> Subclasses should call the super implementation 293708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * last to ensure that any necessary tear down has occurred before the 294708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * listener specified by {@link #setOnDismissListener(OnDismissListener)} 295708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * is called. 296708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette */ 297708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette protected void onDismiss() { 2988515ee846bd76aee86ec5ddfcc4dd1e626dd999cAdam Powell mPopup = null; 299708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette 300708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette if (mOnDismissListener != null) { 301708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette mOnDismissListener.onDismiss(); 302708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette } 3034267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell } 3044267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 3058028dd32a4a04154050220dd0693583d5b750330Adam Powell public boolean isShowing() { 3068028dd32a4a04154050220dd0693583d5b750330Adam Powell return mPopup != null && mPopup.isShowing(); 3078028dd32a4a04154050220dd0693583d5b750330Adam Powell } 3088028dd32a4a04154050220dd0693583d5b750330Adam Powell 309021627eb5875372dea57ba91fa782fffbfbbc559Alan Viverette @Override 310021627eb5875372dea57ba91fa782fffbfbbc559Alan Viverette public void setPresenterCallback(@Nullable MenuPresenter.Callback cb) { 3119916282bac8ac8fab7ce5b649c049842acffa29bOren Blasberg mPresenterCallback = cb; 312708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette if (mPopup != null) { 313708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette mPopup.setCallback(cb); 314708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette } 315696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell } 316708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette 317708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette /** 318708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette * Listener used to proxy dismiss callbacks to the helper's owner. 319708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette */ 320708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette private final OnDismissListener mInternalOnDismissListener = new OnDismissListener() { 321708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette @Override 322708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette public void onDismiss() { 323708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette MenuPopupHelper.this.onDismiss(); 324708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette } 325708aa9d96441fe359a4c2a2c89e2ab709b367f8aAlan Viverette }; 3264267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell} 327