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 5package org.chromium.chrome.browser.appmenu; 6 7import android.app.Activity; 8import android.content.res.TypedArray; 9import android.graphics.Point; 10import android.graphics.Rect; 11import android.graphics.drawable.Drawable; 12import android.view.ContextThemeWrapper; 13import android.view.Menu; 14import android.view.MenuItem; 15import android.view.View; 16import android.widget.PopupMenu; 17 18import org.chromium.base.VisibleForTesting; 19import org.chromium.chrome.browser.UmaBridge; 20 21import 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 */ 27public 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 */ 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 */ 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 98 // Use full size of window for abnormal appRect. 99 if (appRect.left < 0 && appRect.top < 0) { 100 appRect.left = 0; 101 appRect.top = 0; 102 appRect.right = mActivity.getWindow().getDecorView().getWidth(); 103 appRect.bottom = mActivity.getWindow().getDecorView().getHeight(); 104 } 105 int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation(); 106 Point pt = new Point(); 107 mActivity.getWindowManager().getDefaultDisplay().getSize(pt); 108 mAppMenu.show(wrapper, anchorView, isByHardwareButton, rotation, appRect, pt.y); 109 mAppMenuDragHelper.onShow(startDragging); 110 UmaBridge.menuShow(); 111 return true; 112 } 113 114 void appMenuDismissed() { 115 mAppMenuDragHelper.finishDragging(); 116 } 117 118 /** 119 * @return Whether the App Menu is currently showing. 120 */ 121 public boolean isAppMenuShowing() { 122 return mAppMenu != null && mAppMenu.isShowing(); 123 } 124 125 /** 126 * @return The App Menu that the menu handler is interacting with. 127 */ 128 @VisibleForTesting 129 AppMenu getAppMenu() { 130 return mAppMenu; 131 } 132 133 AppMenuDragHelper getAppMenuDragHelper() { 134 return mAppMenuDragHelper; 135 } 136 137 /** 138 * Requests to hide the App Menu. 139 */ 140 public void hideAppMenu() { 141 if (mAppMenu != null && mAppMenu.isShowing()) mAppMenu.dismiss(); 142 } 143 144 /** 145 * Adds the observer to App Menu. 146 * @param observer Observer that should be notified about App Menu changes. 147 */ 148 public void addObserver(AppMenuObserver observer) { 149 mObservers.add(observer); 150 } 151 152 /** 153 * Removes the observer from the App Menu. 154 * @param observer Observer that should no longer be notified about App Menu changes. 155 */ 156 public void removeObserver(AppMenuObserver observer) { 157 mObservers.remove(observer); 158 } 159 160 void onOptionsItemSelected(MenuItem item) { 161 mActivity.onOptionsItemSelected(item); 162 } 163 164 /** 165 * Called by AppMenu to report that the App Menu visibility has changed. 166 * @param isVisible Whether the App Menu is showing. 167 */ 168 void onMenuVisibilityChanged(boolean isVisible) { 169 for (int i = 0; i < mObservers.size(); ++i) { 170 mObservers.get(i).onMenuVisibilityChanged(isVisible); 171 } 172 } 173} 174