OLD | NEW |
---|---|
(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/chromeos/arc/open_with_menu_controller_delegate.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 arc { | |
21 | |
22 namespace { | |
23 | |
24 const size_t kMaxArcAppsInSubMenu = IDC_CONTENT_CONTEXT_OPEN_WITH_ARC_APP_LAST - | |
25 IDC_CONTENT_CONTEXT_OPEN_WITH_ARC_APP1 + 1; | |
26 | |
27 const int kInvalidCommandId = -1; | |
28 const int kMinInstanceVersion = 2; // see intent_helper.mojom | |
29 | |
30 // Converts |handlers| into |out_map| which is a map from a command ID to a | |
31 // UrlHandlerInfoPtr. Also assigns a command id for the parent of the sub | |
32 // menu to |out_sub_menu_parent_command_id|. When the sub menu is not needed, | |
33 // the function assigns |kInvalidCommandId| to the variable. | |
34 void BuildHandlersMap(int menu_id_start, | |
hidehiko
2016/03/15 07:34:29
nit: now we have std::move. How about return a pai
Yusuke Sato
2016/03/15 19:11:56
Done.
| |
35 size_t num_menu_items, | |
36 mojo::Array<UrlHandlerInfoPtr> handlers, | |
37 std::unordered_map<int, UrlHandlerInfoPtr>* out_map, | |
38 int* out_sub_menu_parent_command_id) { | |
39 // We assume |num_menu_items| is always >=3. | |
40 DCHECK_GE(num_menu_items, 3U); | |
41 *out_sub_menu_parent_command_id = kInvalidCommandId; | |
42 | |
43 const size_t num_apps = handlers.size(); | |
44 size_t handlers_index = 0; | |
45 // We use the last item in the main menu (menu_id_start + num_menu_items - 1) | |
46 // as a parent of a sub menu, and others as regular menu items. | |
47 if (num_apps < num_menu_items) { | |
48 // All apps can be shown with the regular main menu items. | |
49 for (size_t i = 0; i < num_apps; ++i) { | |
50 (*out_map)[menu_id_start + i] = std::move(handlers[handlers_index++]); | |
51 } | |
52 } else { | |
53 // Otherwise, use the sub menu too. In this case, disable the last item of | |
54 // the regular main menu (hence '-2'). | |
55 for (size_t i = 0; i < num_menu_items - 2; ++i) { | |
56 (*out_map)[menu_id_start + i] = std::move(handlers[handlers_index++]); | |
57 } | |
58 *out_sub_menu_parent_command_id = menu_id_start + num_menu_items - 1; | |
59 const size_t sub_items = | |
60 std::min(num_apps - (num_menu_items - 2), kMaxArcAppsInSubMenu); | |
61 for (size_t i = 0; i < sub_items; ++i) { | |
62 (*out_map)[IDC_CONTENT_CONTEXT_OPEN_WITH_ARC_APP1 + i] = | |
63 std::move(handlers[handlers_index++]); | |
64 } | |
65 } | |
66 } | |
67 | |
68 class SubMenuDelegate : public ui::SimpleMenuModel::Delegate { | |
69 public: | |
70 explicit SubMenuDelegate(OpenWithMenuControllerDelegate* parent_menu) | |
71 : parent_menu_(parent_menu) {} | |
72 ~SubMenuDelegate() override {} | |
73 | |
74 // ui::SimpleMenuModel::Delegate overrides: | |
75 bool IsCommandIdChecked(int command_id) const override { return false; } | |
76 bool IsCommandIdEnabled(int command_id) const override { return true; } | |
77 bool GetAcceleratorForCommandId(int command_id, | |
78 ui::Accelerator* accelerator) override { | |
79 return false; | |
80 } | |
81 void ExecuteCommand(int command_id, int event_flags) override { | |
82 parent_menu_->ExecuteCommand(command_id); | |
83 } | |
84 | |
85 private: | |
86 OpenWithMenuControllerDelegate* const parent_menu_; | |
87 | |
88 DISALLOW_COPY_AND_ASSIGN(SubMenuDelegate); | |
89 }; | |
90 | |
91 } // namespace | |
92 | |
93 OpenWithMenuControllerDelegate::OpenWithMenuControllerDelegate( | |
94 ArcBridgeService* bridge_service) | |
95 : bridge_service_(bridge_service), | |
96 sub_menu_(new SubMenuDelegate(this)), | |
97 loading_frame_(0), | |
98 weak_ptr_factory_(this) { | |
99 DCHECK(bridge_service_); | |
100 } | |
101 | |
102 OpenWithMenuControllerDelegate::~OpenWithMenuControllerDelegate() {} | |
103 | |
104 IntentHelperInstance* OpenWithMenuControllerDelegate::GetIntentHelper() { | |
105 if (!bridge_service_) { | |
106 DLOG(WARNING) << "ARC bridge is not ready."; | |
107 return nullptr; | |
108 } | |
109 IntentHelperInstance* intent_helper_instance = | |
110 bridge_service_->intent_helper_instance(); | |
111 if (!intent_helper_instance) { | |
112 DLOG(WARNING) << "ARC intent helper instance is not ready."; | |
113 return nullptr; | |
114 } | |
115 if (bridge_service_->intent_helper_version() < kMinInstanceVersion) { | |
116 DLOG(WARNING) << "ARC intent helper instance is too old."; | |
117 return nullptr; | |
118 } | |
119 return intent_helper_instance; | |
120 } | |
121 | |
122 int OpenWithMenuControllerDelegate::Init(RenderViewContextMenuProxy* proxy, | |
123 int menu_id_start, | |
124 size_t num_menu_items, | |
125 const GURL& url) { | |
126 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
127 const int kNumMenuItemsToEnableNow = 0; | |
128 | |
129 IntentHelperInstance* intent_helper_instance = GetIntentHelper(); | |
130 if (!intent_helper_instance || !url.is_valid() || num_menu_items < 3) | |
131 return kNumMenuItemsToEnableNow; | |
132 | |
133 proxy_ = proxy; | |
134 all_command_ids_.clear(); | |
135 main_menu_id_start_ = menu_id_start; | |
136 num_main_menu_items_ = num_menu_items; | |
137 link_url_ = url; | |
138 loading_message_ = | |
139 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_LOADING_ARC_APP_NAMES); | |
140 | |
141 // Add placeholder items. | |
142 for (size_t i = 0; i < num_menu_items; ++i) { | |
143 const int id = menu_id_start + i; | |
144 if (i == (num_menu_items - 1)) | |
145 proxy_->AddSubMenu(id, loading_message_, &sub_menu_); | |
146 else | |
147 proxy_->AddMenuItem(id, loading_message_); | |
148 all_command_ids_.insert(id); | |
149 } | |
150 | |
151 // Add placeholder sub items. | |
152 for (size_t i = 0; i < kMaxArcAppsInSubMenu; ++i) { | |
153 const int id = IDC_CONTENT_CONTEXT_OPEN_WITH_ARC_APP1 + i; | |
154 sub_menu_.AddItem(id, loading_message_); | |
155 all_command_ids_.insert(id); | |
156 } | |
157 | |
158 // Check if ARC apps can handle the |link_url_|. Since the information is | |
159 // held in a different (ARC) process, issue a mojo IPC request. Usually, the | |
160 // callback function, OnUrlHandlerList, is called within a few milliseconds | |
161 // even on a slowest Chromebook we support. | |
162 intent_helper_instance->RequestUrlHandlerList( | |
163 link_url_.spec(), | |
164 base::Bind(&OpenWithMenuControllerDelegate::OnUrlHandlerList, | |
165 weak_ptr_factory_.GetWeakPtr())); | |
166 | |
167 // Start the animation just in case. Since the IPC above almost always returns | |
168 // within a few milliseconds, the user will unlikely see the anination. | |
169 animation_timer_.Start( | |
170 FROM_HERE, base::TimeDelta::FromSeconds(1), this, | |
171 &OpenWithMenuControllerDelegate::OnAnimationTimerExpired); | |
172 | |
173 return kNumMenuItemsToEnableNow; | |
174 } | |
175 | |
176 void OpenWithMenuControllerDelegate::ExecuteCommand(int id) { | |
177 IntentHelperInstance* intent_helper_instance = GetIntentHelper(); | |
178 if (!intent_helper_instance) | |
179 return; | |
180 | |
181 const auto iter = handlers_.find(id); | |
182 if (iter != handlers_.end()) { | |
183 intent_helper_instance->HandleUrl(link_url_.spec(), | |
184 iter->second->package_name); | |
185 } | |
186 } | |
187 | |
188 void OpenWithMenuControllerDelegate::BuildHandlersMapForTesting( | |
189 int menu_id_start, | |
190 size_t num_menu_items, | |
191 mojo::Array<UrlHandlerInfoPtr> handlers, | |
192 std::unordered_map<int, UrlHandlerInfoPtr>* out_map, | |
193 int* out_sub_menu_parent_command_id) { | |
194 BuildHandlersMap(menu_id_start, num_menu_items, std::move(handlers), out_map, | |
195 out_sub_menu_parent_command_id); | |
196 } | |
197 | |
198 base::string16 OpenWithMenuControllerDelegate::GetLabelForCommandId( | |
199 int command_id) const { | |
200 if (static_cast<size_t>(command_id) == | |
201 (main_menu_id_start_ + num_main_menu_items_ - 1)) | |
202 return l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_MORE_ARC_APPS); | |
203 | |
204 const auto iter = handlers_.find(command_id); | |
205 if (iter != handlers_.end()) { | |
206 return l10n_util::GetStringFUTF16( | |
207 IDS_CONTENT_CONTEXT_OPEN_WITH_ARC_APP, | |
208 base::UTF8ToUTF16(iter->second->name.get())); | |
209 } | |
210 return base::EmptyString16(); | |
211 } | |
212 | |
213 void OpenWithMenuControllerDelegate::OnUrlHandlerList( | |
214 mojo::Array<UrlHandlerInfoPtr> handlers) { | |
215 animation_timer_.Stop(); | |
216 | |
217 int sub_menu_parent = kInvalidCommandId; | |
218 BuildHandlersMap(main_menu_id_start_, num_main_menu_items_, | |
219 std::move(handlers), &handlers_, &sub_menu_parent); | |
220 | |
221 for (const auto& command_id : all_command_ids_) { | |
222 if (command_id == sub_menu_parent || handlers_.count(command_id)) { | |
223 // TODO(yusukes): Show icon of the app. | |
224 proxy_->UpdateMenuItem(command_id, | |
225 true, // enabled | |
226 false, // hidden | |
227 GetLabelForCommandId(command_id)); | |
228 } else { | |
229 proxy_->UpdateMenuItem(command_id, | |
230 false, // enabled | |
231 true, // hidden | |
232 base::EmptyString16()); | |
233 } | |
234 } | |
235 } | |
236 | |
237 void OpenWithMenuControllerDelegate::OnAnimationTimerExpired() { | |
238 // Append '.' characters to the end of "Checking". | |
239 loading_frame_ = (loading_frame_ + 1) & 3; | |
240 base::string16 loading_message = | |
241 loading_message_ + base::string16(loading_frame_, '.'); | |
242 | |
243 // Update the menu item with the text. We disable this item to prevent users | |
244 // from selecting it. | |
245 for (const auto& command_id : all_command_ids_) { | |
246 proxy_->UpdateMenuItem(command_id, | |
247 false, // enabled | |
248 false, // hidden | |
249 loading_message); | |
250 } | |
251 } | |
252 | |
253 } // namespace arc | |
OLD | NEW |