Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(136)

Side by Side Diff: chrome/browser/renderer_context_menu/arc_app_menu_observer_chromeos.cc

Issue 1760773004: Add "Open with <ARC-app-name>" items to the context menu (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: address comments Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 #include "chrome/browser/renderer_context_menu/arc_app_menu_observer_chromeos.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/app/chrome_command_ids.h"
13 #include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
14 #include "chrome/grit/generated_resources.h"
15 #include "components/arc/arc_bridge_service.h"
16 #include "content/public/common/context_menu_params.h"
17 #include "ui/base/l10n/l10n_util.h"
18 #include "ui/base/models/simple_menu_model.h"
19
20 namespace {
21
22 const int kMaxArcAppsInMainMenu =
23 IDC_CONTENT_CONTEXT_ARC_APP_LAST - IDC_CONTENT_CONTEXT_ARC_APP1 + 1;
24 const int kMaxArcAppsInSubMenu =
25 IDC_CONTENT_CONTEXT_ARC_SUB_APP_LAST - IDC_CONTENT_CONTEXT_ARC_SUB_APP1 + 1;
26
27 const int kMinInstanceVersion = 2; // see intent_helper.mojom
28
29 std::set<int> GetAllCommandIds() {
30 std::set<int> ids;
31
32 for (int i = 0; i < kMaxArcAppsInMainMenu; ++i) {
33 ids.insert(IDC_CONTENT_CONTEXT_ARC_APP1 + i);
34 }
35 ids.insert(IDC_CONTENT_CONTEXT_ARC_SUB_MENU);
36 for (int i = 0; i < kMaxArcAppsInSubMenu; ++i) {
37 ids.insert(IDC_CONTENT_CONTEXT_ARC_SUB_APP1 + i);
38 }
39
40 return ids;
41 }
42
43 // Returns a set of command IDs to enable when the total number of ARC apps to
44 // show in the main and sub menus is |num_apps|.
45 std::set<int> GetCommandIdsToEnable(size_t num_apps) {
46 std::set<int> ids;
47
48 if (num_apps <= kMaxArcAppsInMainMenu) {
49 // When |num_apps| fits in the main menu, do not enable
50 // IDC_CONTENT_CONTEXT_ARC_SUB_MENU and IDC_CONTENT_CONTEXT_ARC_SUB_APP*.
51 for (size_t i = 0; i < num_apps; ++i) {
52 ids.insert(IDC_CONTENT_CONTEXT_ARC_APP1 + i);
53 }
54 } else {
55 // When |num_apps| does not fit in the main menu, enable
56 // IDC_CONTENT_CONTEXT_ARC_SUB_MENU and some of
57 // IDC_CONTENT_CONTEXT_ARC_SUB_APP* IDs. In this case, do NOT enable
58 // IDC_CONTENT_CONTEXT_ARC_APP_LAST, hence the '-1'.
59 for (size_t i = 0; i < kMaxArcAppsInMainMenu - 1; ++i) {
60 ids.insert(IDC_CONTENT_CONTEXT_ARC_APP1 + i);
61 }
62
63 ids.insert(IDC_CONTENT_CONTEXT_ARC_SUB_MENU);
64 size_t sub_items = std::min<size_t>(num_apps - (kMaxArcAppsInMainMenu - 1),
dcheng 2016/03/05 05:06:17 Just make these constants size_t so you don't have
Yusuke Sato 2016/03/05 20:49:51 Done.
65 kMaxArcAppsInSubMenu);
66 for (size_t i = 0; i < sub_items; ++i) {
67 ids.insert(IDC_CONTENT_CONTEXT_ARC_SUB_APP1 + i);
68 }
69 }
70
71 return ids;
72 }
73
74 // The same as GetCommandIdsToEnable except that the function returns a set of
75 // IDs to disable.
76 std::set<int> GetCommandIdsToDisable(size_t num_apps,
77 const std::set<int>& all_ids) {
78 std::set<int> ids;
79 std::set<int> enabled(GetCommandIdsToEnable(num_apps));
80 std::set_difference(all_ids.begin(), all_ids.end(), enabled.begin(),
81 enabled.end(), std::inserter(ids, ids.begin()));
82 return ids;
83 }
84
85 int GetIndexForCommandId(int command_id) {
86 int index = -1;
87 if (command_id >= IDC_CONTENT_CONTEXT_ARC_APP1 &&
88 command_id <= IDC_CONTENT_CONTEXT_ARC_APP_LAST) {
89 index = command_id - IDC_CONTENT_CONTEXT_ARC_APP1;
90 } else if (command_id >= IDC_CONTENT_CONTEXT_ARC_SUB_APP1 &&
91 command_id <= IDC_CONTENT_CONTEXT_ARC_SUB_APP_LAST) {
92 const int offset = kMaxArcAppsInMainMenu - 1;
93 index = command_id - IDC_CONTENT_CONTEXT_ARC_SUB_APP1 + offset;
94 }
95 return index;
96 }
97
98 class SubMenuDelegate : public ui::SimpleMenuModel::Delegate {
99 public:
100 explicit SubMenuDelegate(ArcAppMenuObserver* parent_menu)
101 : parent_menu_(parent_menu) {}
102 ~SubMenuDelegate() override {}
103
104 // ui::SimpleMenuModel::Delegate overrides:
105 bool IsCommandIdChecked(int command_id) const override { return false; }
106 bool IsCommandIdEnabled(int command_id) const override { return true; }
107 bool GetAcceleratorForCommandId(int command_id,
108 ui::Accelerator* accelerator) override {
109 return false;
110 }
111 void ExecuteCommand(int command_id, int event_flags) override {
112 parent_menu_->ExecuteCommand(command_id);
113 }
114
115 private:
116 ArcAppMenuObserver* parent_menu_;
117
118 DISALLOW_COPY_AND_ASSIGN(SubMenuDelegate);
119 };
120
121 } // namespace
122
123 ArcAppMenuObserver::ArcAppMenuObserver(RenderViewContextMenuProxy* proxy)
124 : proxy_(proxy),
125 all_command_ids_(GetAllCommandIds()),
126 sub_menu_(new SubMenuDelegate(this)),
127 loading_frame_(0),
128 weak_ptr_factory_(this) {}
129
130 ArcAppMenuObserver::~ArcAppMenuObserver() {}
131
132 arc::IntentHelperInstance* ArcAppMenuObserver::GetIntentHelper() {
133 arc::ArcBridgeService* bridge = arc::ArcBridgeService::Get();
134 if (!bridge) {
135 DLOG(WARNING) << "ARC bridge is not ready.";
136 return nullptr;
137 }
138 arc::IntentHelperInstance* intent_helper_instance =
139 bridge->intent_helper_instance();
140 if (!intent_helper_instance) {
141 DLOG(WARNING) << "ARC intent helper instance is not ready.";
142 return nullptr;
143 }
144 if (bridge->intent_helper_version() < kMinInstanceVersion) {
145 DLOG(WARNING) << "ARC intent helper instance is too old.";
146 return nullptr;
147 }
148 return intent_helper_instance;
149 }
150
151 void ArcAppMenuObserver::InitMenu(const content::ContextMenuParams& params) {
152 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
153
154 arc::IntentHelperInstance* intent_helper_instance = GetIntentHelper();
155 if (!intent_helper_instance) {
156 return;
157 }
158
159 if (params.link_url.is_empty()) {
160 return;
161 }
162 link_url_ = params.link_url;
163
164 loading_message_ =
165 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_LOADING_ARC_APP_NAMES);
166
167 // Add placeholder items.
168 for (int i = 0; i < kMaxArcAppsInMainMenu; ++i) {
169 proxy_->AddMenuItem(IDC_CONTENT_CONTEXT_ARC_APP1 + i,
170 loading_message_);
171 }
172 // Add a placeholder sub menu.
173 proxy_->AddSubMenu(IDC_CONTENT_CONTEXT_ARC_SUB_MENU, loading_message_,
174 &sub_menu_);
175 // Add placeholder sub items.
176 for (int i = 0; i < kMaxArcAppsInSubMenu; ++i) {
177 sub_menu_.AddItem(IDC_CONTENT_CONTEXT_ARC_SUB_APP1 + i,
178 loading_message_);
179 }
180
181 // Check if ARC apps can handle the |link_url_|. Sicne the information is
182 // held in a different (ARC) process, issue a mojo IPC request. Usually, the
183 // callback function, OnUrlHandlerList, is called within a few milliseconds
184 // even on a slowest Chromebook we support.
185 intent_helper_instance->RequestUrlHandlerList(
186 link_url_.spec(), base::Bind(&ArcAppMenuObserver::OnUrlHandlerList,
187 weak_ptr_factory_.GetWeakPtr()));
188
189 // Start the animation just in case. Since the IPC above almost always returns
190 // within a few milliseconds, the user will unlikely see the anination.
191 animation_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1),
192 this, &ArcAppMenuObserver::OnAnimationTimerExpired);
193 }
194
195 bool ArcAppMenuObserver::IsCommandIdSupported(int command_id) {
196 return all_command_ids_.count(command_id);
197 }
198
199 bool ArcAppMenuObserver::IsCommandIdChecked(int command_id) {
200 return false;
201 }
202
203 bool ArcAppMenuObserver::IsCommandIdEnabled(int command_id) {
204 // Items will be enabled later in OnUrlHandlerList() as needed.
205 return false;
206 }
207
208 void ArcAppMenuObserver::ExecuteCommand(int command_id) {
209 arc::IntentHelperInstance* intent_helper_instance = GetIntentHelper();
210 if (!intent_helper_instance) {
211 return;
212 }
213
214 const size_t index = GetIndexForCommandId(command_id);
215 if (index < handlers_.size()) {
216 intent_helper_instance->HandleUrl(link_url_.spec(),
217 handlers_[index]->package_name);
218 }
219 }
220
221 void ArcAppMenuObserver::OnMenuCancel() {}
222
223 std::set<int> ArcAppMenuObserver::GetCommandIdsToEnableForTesting(
224 size_t num_apps) {
225 return GetCommandIdsToEnable(num_apps);
226 }
227
228 std::set<int> ArcAppMenuObserver::GetCommandIdsToDisableForTesting(
229 size_t num_apps) {
230 return GetCommandIdsToDisable(num_apps, GetAllCommandIds());
231 }
232
233 int ArcAppMenuObserver::GetIndexForCommandIdForTesting(int command_id) {
234 return GetIndexForCommandId(command_id);
235 }
236
237 base::string16 ArcAppMenuObserver::GetLabelForCommandId(int command_id) const {
238 if (command_id == IDC_CONTENT_CONTEXT_ARC_SUB_MENU)
239 return l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_MORE_ARC_APPS);
240
241 const size_t index = GetIndexForCommandId(command_id);
242 if (index < handlers_.size()) {
243 return l10n_util::GetStringFUTF16(
244 IDS_CONTENT_CONTEXT_OPEN_WITH_ARC_APP,
245 base::UTF8ToUTF16(handlers_[index]->name.get()));
246 }
247 return base::EmptyString16();
248 }
249
250 void ArcAppMenuObserver::OnUrlHandlerList(
251 mojo::Array<arc::UrlHandlerInfoPtr> handlers) {
252 animation_timer_.Stop();
253 handlers_ = std::move(handlers);
254
255 const size_t num_apps = handlers_.size();
256 std::set<int> disabled(GetCommandIdsToDisable(num_apps, all_command_ids_));
257 for (const auto& command_id : disabled) {
258 proxy_->UpdateMenuItem(command_id,
259 false, // enabled
260 true, // hidden
261 base::EmptyString16());
262 }
263
264 std::set<int> enabled(GetCommandIdsToEnable(num_apps));
265 for (const auto& command_id : enabled) {
266 // TODO(yusukes): Show icon of the app.
267 proxy_->UpdateMenuItem(command_id,
268 true, // enabled
269 false, // hidden
270 GetLabelForCommandId(command_id));
271 }
272 }
273
274 void ArcAppMenuObserver::OnAnimationTimerExpired() {
275 // Append '.' characters to the end of "Checking".
276 loading_frame_ = (loading_frame_ + 1) & 3;
277 base::string16 loading_message =
278 loading_message_ + base::string16(loading_frame_, '.');
279
280 // Update the menu item with the text. We disable this item to prevent users
281 // from selecting it.
282 for (const auto& command_id : all_command_ids_) {
283 proxy_->UpdateMenuItem(command_id,
284 false, // enabled
285 false, // hidden
286 loading_message);
287 }
288 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698