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

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 from hidehiko@ 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 size_t kMaxArcAppsInMainMenu =
23 IDC_CONTENT_CONTEXT_ARC_APP_LAST - IDC_CONTENT_CONTEXT_ARC_APP1 + 1;
24 const size_t 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 (size_t 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 (size_t 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(num_apps - (kMaxArcAppsInMainMenu - 1),
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 (size_t 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 (size_t 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_|. Since 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 // |index| will be UINT_MAX when command_id is invalid/unknown.
215 const size_t index = GetIndexForCommandId(command_id);
216 if (index < handlers_.size()) {
217 intent_helper_instance->HandleUrl(link_url_.spec(),
218 handlers_[index]->package_name);
219 }
220 }
221
222 void ArcAppMenuObserver::OnMenuCancel() {}
223
224 std::set<int> ArcAppMenuObserver::GetCommandIdsToEnableForTesting(
225 size_t num_apps) {
226 return GetCommandIdsToEnable(num_apps);
227 }
228
229 std::set<int> ArcAppMenuObserver::GetCommandIdsToDisableForTesting(
230 size_t num_apps) {
231 return GetCommandIdsToDisable(num_apps, GetAllCommandIds());
232 }
233
234 int ArcAppMenuObserver::GetIndexForCommandIdForTesting(int command_id) {
235 return GetIndexForCommandId(command_id);
236 }
237
238 base::string16 ArcAppMenuObserver::GetLabelForCommandId(int command_id) const {
239 if (command_id == IDC_CONTENT_CONTEXT_ARC_SUB_MENU)
240 return l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_MORE_ARC_APPS);
241
242 // |index| will be UINT_MAX when command_id is invalid/unknown.
243 const size_t index = GetIndexForCommandId(command_id);
244 if (index < handlers_.size()) {
245 return l10n_util::GetStringFUTF16(
246 IDS_CONTENT_CONTEXT_OPEN_WITH_ARC_APP,
247 base::UTF8ToUTF16(handlers_[index]->name.get()));
248 }
249 return base::EmptyString16();
250 }
251
252 void ArcAppMenuObserver::OnUrlHandlerList(
253 mojo::Array<arc::UrlHandlerInfoPtr> handlers) {
254 animation_timer_.Stop();
255 handlers_ = std::move(handlers);
256
257 const size_t num_apps = handlers_.size();
258 std::set<int> disabled(GetCommandIdsToDisable(num_apps, all_command_ids_));
259 for (const auto& command_id : disabled) {
260 proxy_->UpdateMenuItem(command_id,
261 false, // enabled
262 true, // hidden
263 base::EmptyString16());
264 }
265
266 std::set<int> enabled(GetCommandIdsToEnable(num_apps));
267 for (const auto& command_id : enabled) {
268 // TODO(yusukes): Show icon of the app.
269 proxy_->UpdateMenuItem(command_id,
270 true, // enabled
271 false, // hidden
272 GetLabelForCommandId(command_id));
273 }
274 }
275
276 void ArcAppMenuObserver::OnAnimationTimerExpired() {
277 // Append '.' characters to the end of "Checking".
278 loading_frame_ = (loading_frame_ + 1) & 3;
279 base::string16 loading_message =
280 loading_message_ + base::string16(loading_frame_, '.');
281
282 // Update the menu item with the text. We disable this item to prevent users
283 // from selecting it.
284 for (const auto& command_id : all_command_ids_) {
285 proxy_->UpdateMenuItem(command_id,
286 false, // enabled
287 false, // hidden
288 loading_message);
289 }
290 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698