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

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

Powered by Google App Engine
This is Rietveld 408576698