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