// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.appmenu;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.view.Menu;
import android.view.MenuItem;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ShortcutHelper;
import org.chromium.chrome.browser.UrlConstants;
import org.chromium.chrome.browser.bookmarks.BookmarkBridge;
import org.chromium.chrome.browser.download.DownloadUtils;
import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper;
import org.chromium.chrome.browser.preferences.ManagedPreferencesUtils;
import org.chromium.chrome.browser.preferences.PrefServiceBridge;
import org.chromium.chrome.browser.share.ShareHelper;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
import org.chromium.ui.base.DeviceFormFactor;
/**
* App Menu helper that handles hiding and showing menu items based on activity state.
*/
public class AppMenuPropertiesDelegate {
// Indices for different levels in drawable.btn_reload_stop.
// Used only when preparing menu and refresh reload button in menu when tab
// page load status changes.
private static final int RELOAD_BUTTON_LEVEL_RELOAD = 0;
private static final int RELOAD_BUTTON_LEVEL_STOP_LOADING = 1;
protected MenuItem mReloadMenuItem;
protected final ChromeActivity mActivity;
protected BookmarkBridge mBookmarkBridge;
public AppMenuPropertiesDelegate(ChromeActivity activity) {
mActivity = activity;
}
/**
* @return Whether the App Menu should be shown.
*/
public boolean shouldShowAppMenu() {
return mActivity.shouldShowAppMenu();
}
/**
* Allows the delegate to show and hide items before the App Menu is shown. It is called every
* time the menu is shown. This assumes that the provided menu contains all the items expected
* in the application menu (i.e. that the main menu has been inflated into it).
* @param menu Menu that will be used as the source for the App Menu pop up.
*/
public void prepareMenu(Menu menu) {
// Exactly one of these will be true, depending on the type of menu showing.
boolean isPageMenu;
boolean isOverviewMenu;
boolean isTabletEmptyModeMenu;
boolean isOverview = mActivity.isInOverviewMode();
boolean isIncognito = mActivity.getCurrentTabModel().isIncognito();
Tab currentTab = mActivity.getActivityTab();
// Determine which menu to show.
if (mActivity.isTablet()) {
boolean hasTabs = mActivity.getCurrentTabModel().getCount() != 0;
isPageMenu = hasTabs && !isOverview;
isOverviewMenu = hasTabs && isOverview;
isTabletEmptyModeMenu = !hasTabs;
} else {
isPageMenu = !isOverview;
isOverviewMenu = isOverview;
isTabletEmptyModeMenu = false;
}
menu.setGroupVisible(R.id.PAGE_MENU, isPageMenu);
menu.setGroupVisible(R.id.OVERVIEW_MODE_MENU, isOverviewMenu);
menu.setGroupVisible(R.id.TABLET_EMPTY_MODE_MENU, isTabletEmptyModeMenu);
if (isPageMenu && currentTab != null) {
String url = currentTab.getUrl();
boolean isChromeScheme = url.startsWith(UrlConstants.CHROME_SCHEME)
|| url.startsWith(UrlConstants.CHROME_NATIVE_SCHEME);
boolean shouldShowIconRow = !mActivity.isTablet()
|| mActivity.getWindow().getDecorView().getWidth()
< DeviceFormFactor.getMinimumTabletWidthPx(mActivity);
// Update the icon row items (shown in narrow form factors).
menu.findItem(R.id.icon_row_menu_id).setVisible(shouldShowIconRow);
if (shouldShowIconRow) {
// Disable the "Forward" menu item if there is no page to go to.
MenuItem forwardMenuItem = menu.findItem(R.id.forward_menu_id);
forwardMenuItem.setEnabled(currentTab.canGoForward());
mReloadMenuItem = menu.findItem(R.id.reload_menu_id);
mReloadMenuItem.setIcon(R.drawable.btn_reload_stop);
loadingStateChanged(currentTab.isLoading());
MenuItem bookmarkMenuItem = menu.findItem(R.id.bookmark_this_page_id);
updateBookmarkMenuItem(bookmarkMenuItem, currentTab);
MenuItem offlineMenuItem = menu.findItem(R.id.offline_page_id);
if (offlineMenuItem != null) {
if (DownloadUtils.isDownloadHomeEnabled()) {
offlineMenuItem.setEnabled(
DownloadUtils.isAllowedToDownloadPage(currentTab));
Drawable drawable = offlineMenuItem.getIcon();
if (drawable != null) {
int iconTint = ApiCompatibilityUtils.getColor(
mActivity.getResources(), R.color.light_normal_color);
drawable.mutate();
drawable.setColorFilter(iconTint, PorterDuff.Mode.SRC_ATOP);
}
} else {
offlineMenuItem.setVisible(false);
}
}
}
menu.findItem(R.id.downloads_menu_id)
.setVisible(DownloadUtils.isDownloadHomeEnabled());
menu.findItem(R.id.update_menu_id).setVisible(
UpdateMenuItemHelper.getInstance().shouldShowMenuItem(mActivity));
menu.findItem(R.id.move_to_other_window_menu_id).setVisible(
MultiWindowUtils.getInstance().isOpenInOtherWindowSupported(mActivity));
MenuItem recentTabsMenuItem = menu.findItem(R.id.recent_tabs_menu_id);
recentTabsMenuItem.setVisible(!isIncognito);
recentTabsMenuItem.setTitle(R.string.menu_recent_tabs);
MenuItem allBookmarksMenuItem = menu.findItem(R.id.all_bookmarks_menu_id);
allBookmarksMenuItem.setTitle(mActivity.getString(R.string.menu_bookmarks));
// Don't allow "chrome://" pages to be shared.
menu.findItem(R.id.share_row_menu_id).setVisible(!isChromeScheme);
ShareHelper.configureDirectShareMenuItem(
mActivity, menu.findItem(R.id.direct_share_menu_id));
// Disable find in page on the native NTP.
menu.findItem(R.id.find_in_page_id).setVisible(
!currentTab.isNativePage() && currentTab.getWebContents() != null);
// Hide 'Add to homescreen' on all chrome:// pages -- Android doesn't know how to direct
// those URLs. Also hide it on incognito pages to avoid problems where users create
// shortcuts in incognito mode and then open the webapp in regular mode. Also check if
// creating shortcuts is supported at all.
MenuItem homescreenItem = menu.findItem(R.id.add_to_homescreen_id);
boolean canAddShortcutToHomescreen =
ShortcutHelper.isAddToHomeIntentSupported(mActivity);
homescreenItem.setVisible(
canAddShortcutToHomescreen && !isChromeScheme && !isIncognito);
// Hide request desktop site on all chrome:// pages except for the NTP. Check request
// desktop site if it's activated on this page.
MenuItem requestItem = menu.findItem(R.id.request_desktop_site_id);
requestItem.setVisible(!isChromeScheme || currentTab.isNativePage());
requestItem.setChecked(currentTab.getUseDesktopUserAgent());
requestItem.setTitleCondensed(requestItem.isChecked()
? mActivity.getString(R.string.menu_request_desktop_site_on)
: mActivity.getString(R.string.menu_request_desktop_site_off));
// Only display reader mode settings menu option if the current page is in reader mode.
menu.findItem(R.id.reader_mode_prefs_id)
.setVisible(DomDistillerUrlUtils.isDistilledPage(currentTab.getUrl()));
// Only display the Enter VR button if VR Shell is enabled.
menu.findItem(R.id.enter_vr_id).setVisible(mActivity.isVrShellEnabled());
}
if (isOverviewMenu) {
if (isIncognito) {
// Hide normal close all tabs item.
menu.findItem(R.id.close_all_tabs_menu_id).setVisible(false);
// Enable close incognito tabs only if there are incognito tabs.
menu.findItem(R.id.close_all_incognito_tabs_menu_id).setEnabled(true);
} else {
// Hide close incognito tabs item.
menu.findItem(R.id.close_all_incognito_tabs_menu_id).setVisible(false);
// Enable close all tabs if there are normal tabs or incognito tabs.
menu.findItem(R.id.close_all_tabs_menu_id).setEnabled(
mActivity.getTabModelSelector().getTotalTabCount() > 0);
}
}
// Disable new incognito tab when it is blocked (e.g. by a policy).
// findItem(...).setEnabled(...)" is not enough here, because of the inflated
// main_menu.xml contains multiple items with the same id in different groups
// e.g.: new_incognito_tab_menu_id.
disableEnableMenuItem(menu, R.id.new_incognito_tab_menu_id,
true,
PrefServiceBridge.getInstance().isIncognitoModeEnabled(),
PrefServiceBridge.getInstance().isIncognitoModeManaged());
mActivity.prepareMenu(menu);
}
/**
* Notify the delegate that the load state changed.
* @param isLoading Whether the page is currently loading.
*/
public void loadingStateChanged(boolean isLoading) {
if (mReloadMenuItem != null) {
mReloadMenuItem.getIcon().setLevel(isLoading
? RELOAD_BUTTON_LEVEL_STOP_LOADING : RELOAD_BUTTON_LEVEL_RELOAD);
mReloadMenuItem.setTitle(isLoading
? R.string.accessibility_btn_stop_loading : R.string.accessibility_btn_refresh);
}
}
/**
* Notify the delegate that menu was dismissed.
*/
public void onMenuDismissed() {
mReloadMenuItem = null;
}
// Set enabled to be |enable| for all MenuItems with |id| in |menu|.
// If |managed| is true then the "Managed By Enterprise" icon is shown next to the menu.
private void disableEnableMenuItem(
Menu menu, int id, boolean visible, boolean enabled, boolean managed) {
for (int i = 0; i < menu.size(); ++i) {
MenuItem item = menu.getItem(i);
if (item.getItemId() == id && item.isVisible()) {
item.setVisible(visible);
item.setEnabled(enabled);
if (managed) {
item.setIcon(ManagedPreferencesUtils.getManagedByEnterpriseIconId());
} else {
item.setIcon(null);
}
}
}
}
/**
* @return Resource layout id for the footer if there should be one. O otherwise.
*/
public int getFooterResourceId() {
return 0;
}
/**
* Updates the bookmarks bridge.
*
* @param bookmarkBridge The bookmarks bridge.
*/
public void setBookmarkBridge(BookmarkBridge bookmarkBridge) {
mBookmarkBridge = bookmarkBridge;
}
/**
* Updates the bookmark item's visibility.
*
* @param bookmarkMenuItem {@link MenuItem} for adding/editing the bookmark.
* @param currentTab Current tab being displayed.
*/
protected void updateBookmarkMenuItem(MenuItem bookmarkMenuItem, Tab currentTab) {
bookmarkMenuItem.setEnabled(mBookmarkBridge.isEditBookmarksEnabled());
if (currentTab.getBookmarkId() != Tab.INVALID_BOOKMARK_ID) {
bookmarkMenuItem.setIcon(R.drawable.btn_star_filled);
bookmarkMenuItem.setChecked(true);
bookmarkMenuItem.setTitleCondensed(mActivity.getString(R.string.edit_bookmark));
} else {
bookmarkMenuItem.setIcon(R.drawable.btn_star);
bookmarkMenuItem.setChecked(false);
bookmarkMenuItem.setTitleCondensed(null);
}
}
}