1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.chrome.browser.appmenu; 6 7 import android.app.Activity; 8 import android.content.res.TypedArray; 9 import android.graphics.Rect; 10 import android.graphics.drawable.Drawable; 11 import android.view.ContextThemeWrapper; 12 import android.view.Menu; 13 import android.view.MenuItem; 14 import android.view.View; 15 import android.widget.PopupMenu; 16 17 import com.google.common.annotations.VisibleForTesting; 18 19 import org.chromium.chrome.browser.UmaBridge; 20 21 import java.util.ArrayList; 22 23 /** 24 * Object responsible for handling the creation, showing, hiding of the AppMenu and notifying the 25 * AppMenuObservers about these actions. 26 */ 27 public class AppMenuHandler { 28 private AppMenu mAppMenu; 29 private AppMenuDragHelper mAppMenuDragHelper; 30 private Menu mMenu; 31 private final ArrayList<AppMenuObserver> mObservers; 32 private final int mMenuResourceId; 33 34 private final AppMenuPropertiesDelegate mDelegate; 35 private final Activity mActivity; 36 37 /** 38 * Constructs an AppMenuHandler object. 39 * @param activity Activity that is using the AppMenu. 40 * @param delegate Delegate used to check the desired AppMenu properties on show. 41 * @param menuResourceId Resource Id that should be used as the source for the menu items. 42 * It is assumed to have back_menu_id, forward_menu_id, bookmark_this_page_id. 43 */ AppMenuHandler(Activity activity, AppMenuPropertiesDelegate delegate, int menuResourceId)44 public AppMenuHandler(Activity activity, AppMenuPropertiesDelegate delegate, 45 int menuResourceId) { 46 mActivity = activity; 47 mDelegate = delegate; 48 mObservers = new ArrayList<AppMenuObserver>(); 49 mMenuResourceId = menuResourceId; 50 } 51 52 /** 53 * Show the app menu. 54 * @param anchorView Anchor view (usually a menu button) to be used for the popup. 55 * @param isByHardwareButton True if hardware button triggered it. (oppose to software 56 * button) 57 * @param startDragging Whether dragging is started. For example, if the app menu is 58 * showed by tapping on a button, this should be false. If it is 59 * showed by start dragging down on the menu button, this should 60 * be true. Note that if isByHardwareButton is true, this must 61 * be false since we no longer support hardware menu button 62 * dragging. 63 * @return True, if the menu is shown, false, if menu is not shown, example reasons: 64 * the menu is not yet available to be shown, or the menu is already showing. 65 */ showAppMenu(View anchorView, boolean isByHardwareButton, boolean startDragging)66 public boolean showAppMenu(View anchorView, boolean isByHardwareButton, boolean startDragging) { 67 assert !(isByHardwareButton && startDragging); 68 if (!mDelegate.shouldShowAppMenu() || isAppMenuShowing()) return false; 69 70 if (mMenu == null) { 71 // Use a PopupMenu to create the Menu object. Note this is not the same as the 72 // AppMenu (mAppMenu) created below. 73 PopupMenu tempMenu = new PopupMenu(mActivity, anchorView); 74 tempMenu.inflate(mMenuResourceId); 75 mMenu = tempMenu.getMenu(); 76 } 77 mDelegate.prepareMenu(mMenu); 78 79 ContextThemeWrapper wrapper = new ContextThemeWrapper(mActivity, 80 mDelegate.getMenuThemeResourceId()); 81 82 if (mAppMenu == null) { 83 TypedArray a = wrapper.obtainStyledAttributes(new int[] 84 {android.R.attr.listPreferredItemHeightSmall, android.R.attr.listDivider}); 85 int itemRowHeight = a.getDimensionPixelSize(0, 0); 86 Drawable itemDivider = a.getDrawable(1); 87 int itemDividerHeight = itemDivider != null ? itemDivider.getIntrinsicHeight() : 0; 88 a.recycle(); 89 mAppMenu = new AppMenu(mMenu, itemRowHeight, itemDividerHeight, this, 90 mActivity.getResources()); 91 mAppMenuDragHelper = new AppMenuDragHelper(mActivity, mAppMenu, itemRowHeight); 92 } 93 94 // Get the height and width of the display. 95 Rect appRect = new Rect(); 96 mActivity.getWindow().getDecorView().getWindowVisibleDisplayFrame(appRect); 97 int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation(); 98 mAppMenu.show(wrapper, anchorView, isByHardwareButton, rotation, appRect); 99 mAppMenuDragHelper.onShow(startDragging); 100 UmaBridge.menuShow(); 101 return true; 102 } 103 appMenuDismissed()104 void appMenuDismissed() { 105 mAppMenuDragHelper.onDismiss(); 106 } 107 108 /** 109 * @return Whether the App Menu is currently showing. 110 */ isAppMenuShowing()111 public boolean isAppMenuShowing() { 112 return mAppMenu != null && mAppMenu.isShowing(); 113 } 114 115 /** 116 * @return The App Menu that the menu handler is interacting with. 117 */ 118 @VisibleForTesting getAppMenu()119 AppMenu getAppMenu() { 120 return mAppMenu; 121 } 122 getAppMenuDragHelper()123 AppMenuDragHelper getAppMenuDragHelper() { 124 return mAppMenuDragHelper; 125 } 126 127 /** 128 * Requests to hide the App Menu. 129 */ hideAppMenu()130 public void hideAppMenu() { 131 if (mAppMenu != null && mAppMenu.isShowing()) mAppMenu.dismiss(); 132 } 133 134 /** 135 * Adds the observer to App Menu. 136 * @param observer Observer that should be notified about App Menu changes. 137 */ addObserver(AppMenuObserver observer)138 public void addObserver(AppMenuObserver observer) { 139 mObservers.add(observer); 140 } 141 142 /** 143 * Removes the observer from the App Menu. 144 * @param observer Observer that should no longer be notified about App Menu changes. 145 */ removeObserver(AppMenuObserver observer)146 public void removeObserver(AppMenuObserver observer) { 147 mObservers.remove(observer); 148 } 149 onOptionsItemSelected(MenuItem item)150 void onOptionsItemSelected(MenuItem item) { 151 mActivity.onOptionsItemSelected(item); 152 } 153 154 /** 155 * Called by AppMenu to report that the App Menu visibility has changed. 156 * @param isVisible Whether the App Menu is showing. 157 */ onMenuVisibilityChanged(boolean isVisible)158 void onMenuVisibilityChanged(boolean isVisible) { 159 for (int i = 0; i < mObservers.size(); ++i) { 160 mObservers.get(i).onMenuVisibilityChanged(isVisible); 161 } 162 } 163 } 164