1 // Copyright 2019 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.directactions; 6 7 import android.os.Bundle; 8 9 import androidx.annotation.Nullable; 10 11 import org.chromium.base.Callback; 12 import org.chromium.chrome.R; 13 import org.chromium.chrome.browser.tab.Tab; 14 import org.chromium.chrome.browser.tabmodel.TabModelSelector; 15 import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController; 16 17 import java.util.Collections; 18 import java.util.HashMap; 19 import java.util.HashSet; 20 import java.util.Map; 21 import java.util.Set; 22 23 /** 24 * Exposes some well-known menu actions as direct actions. 25 * 26 * <p>This handler exposes a subset of available menu item actions, exposed by the given {@link 27 * MenuOrKeyboardActionController}. By default, no actions are exposed; call {@link 28 * #allowlistActions} or {@link #allowAllActions} to enable them. 29 */ 30 class MenuDirectActionHandler implements DirectActionHandler { 31 /** Maps some menu item actions to direct actions. */ 32 private static final Map<String, Integer> ACTION_MAP; 33 static { 34 Map<String, Integer> map = new HashMap<>(); map.put(ChromeDirectActionIds.GO_FORWARD, R.id.forward_menu_id)35 map.put(ChromeDirectActionIds.GO_FORWARD, R.id.forward_menu_id); map.put(ChromeDirectActionIds.RELOAD, R.id.reload_menu_id)36 map.put(ChromeDirectActionIds.RELOAD, R.id.reload_menu_id); map.put(ChromeDirectActionIds.BOOKMARK_THIS_PAGE, R.id.bookmark_this_page_id)37 map.put(ChromeDirectActionIds.BOOKMARK_THIS_PAGE, R.id.bookmark_this_page_id); map.put(ChromeDirectActionIds.DOWNLOADS, R.id.downloads_menu_id)38 map.put(ChromeDirectActionIds.DOWNLOADS, R.id.downloads_menu_id); map.put(ChromeDirectActionIds.HELP, R.id.help_id)39 map.put(ChromeDirectActionIds.HELP, R.id.help_id); map.put(ChromeDirectActionIds.NEW_TAB, R.id.new_tab_menu_id)40 map.put(ChromeDirectActionIds.NEW_TAB, R.id.new_tab_menu_id); map.put(ChromeDirectActionIds.OPEN_HISTORY, R.id.open_history_menu_id)41 map.put(ChromeDirectActionIds.OPEN_HISTORY, R.id.open_history_menu_id); map.put(ChromeDirectActionIds.PREFERENCES, R.id.preferences_id)42 map.put(ChromeDirectActionIds.PREFERENCES, R.id.preferences_id); map.put(ChromeDirectActionIds.CLOSE_ALL_TABS, R.id.close_all_tabs_menu_id)43 map.put(ChromeDirectActionIds.CLOSE_ALL_TABS, R.id.close_all_tabs_menu_id); 44 ACTION_MAP = Collections.unmodifiableMap(map); 45 } 46 47 private final MenuOrKeyboardActionController mMenuOrKeyboardActionController; 48 private final TabModelSelector mTabModelSelector; 49 50 /** If non-null, only actions that belong to this allowlist are available. */ 51 @Nullable 52 private Set<Integer> mActionIdAllowlist = new HashSet<>(); 53 MenuDirectActionHandler(MenuOrKeyboardActionController menuOrKeyboardActionController, TabModelSelector tabModelSelector)54 MenuDirectActionHandler(MenuOrKeyboardActionController menuOrKeyboardActionController, 55 TabModelSelector tabModelSelector) { 56 this.mMenuOrKeyboardActionController = menuOrKeyboardActionController; 57 this.mTabModelSelector = tabModelSelector; 58 } 59 60 /** Allows the use of all known actions. */ allowAllActions()61 void allowAllActions() { 62 mActionIdAllowlist = null; 63 } 64 65 /** 66 * Allows the use of the specified action, identified by their menu item id. 67 * 68 * <p>Does nothing if the actions are already available. 69 */ allowlistActions(Integer... itemIds)70 void allowlistActions(Integer... itemIds) { 71 if (mActionIdAllowlist == null) return; 72 73 for (int itemId : itemIds) { 74 mActionIdAllowlist.add(itemId); 75 } 76 } 77 78 @Override reportAvailableDirectActions(DirectActionReporter reporter)79 public void reportAvailableDirectActions(DirectActionReporter reporter) { 80 Set<Integer> availableItemIds = new HashSet<>(); 81 Tab currentTab = mTabModelSelector.getCurrentTab(); 82 if (currentTab != null && currentTab.isUserInteractable()) { 83 if (currentTab.canGoForward()) { 84 availableItemIds.add(R.id.forward_menu_id); 85 } 86 availableItemIds.add(R.id.reload_menu_id); 87 availableItemIds.add(R.id.bookmark_this_page_id); 88 availableItemIds.add(R.id.open_history_menu_id); 89 } 90 if (mTabModelSelector.getTotalTabCount() > 0) { 91 availableItemIds.add(R.id.close_all_tabs_menu_id); 92 } 93 94 availableItemIds.add(R.id.downloads_menu_id); 95 availableItemIds.add(R.id.help_id); 96 availableItemIds.add(R.id.new_tab_menu_id); 97 availableItemIds.add(R.id.preferences_id); 98 99 if (mActionIdAllowlist != null) availableItemIds.retainAll(mActionIdAllowlist); 100 101 for (Map.Entry<String, Integer> entry : ACTION_MAP.entrySet()) { 102 if (availableItemIds.contains(entry.getValue())) { 103 reporter.addDirectAction(entry.getKey()); 104 } 105 } 106 } 107 108 @Override performDirectAction( String actionId, Bundle arguments, Callback<Bundle> callback)109 public boolean performDirectAction( 110 String actionId, Bundle arguments, Callback<Bundle> callback) { 111 Integer menuId = ACTION_MAP.get(actionId); 112 if (menuId != null && (mActionIdAllowlist == null || mActionIdAllowlist.contains(menuId)) 113 && mMenuOrKeyboardActionController.onMenuOrKeyboardAction( 114 menuId, /* fromMenu= */ false)) { 115 callback.onResult(Bundle.EMPTY); 116 return true; 117 } 118 return false; 119 } 120 } 121