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

Side by Side Diff: chrome/browser/chromeos/arc/open_with_menu_controller_delegate.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 hidehiko's 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/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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698