OLD | NEW |
| (Empty) |
1 // Copyright 2013 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/extensions/file_manager/desktop_notifications.
h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/message_loop/message_loop.h" | |
9 #include "base/stl_util.h" | |
10 #include "base/strings/utf_string_conversions.h" | |
11 #include "chrome/browser/chromeos/extensions/file_manager/url_util.h" | |
12 #include "chrome/browser/notifications/desktop_notification_service.h" | |
13 #include "chrome/browser/notifications/notification_delegate.h" | |
14 #include "grit/generated_resources.h" | |
15 #include "grit/theme_resources.h" | |
16 #include "ui/base/l10n/l10n_util.h" | |
17 #include "ui/base/resource/resource_bundle.h" | |
18 | |
19 namespace file_manager { | |
20 namespace { | |
21 | |
22 struct NotificationTypeInfo { | |
23 DesktopNotifications::NotificationType type; | |
24 const char* notification_id_prefix; | |
25 int icon_id; | |
26 int title_id; | |
27 int message_id; | |
28 }; | |
29 | |
30 // Information about notification types. | |
31 // The order of notification types in the array must match the order of types in | |
32 // NotificationType enum (i.e. the following MUST be satisfied: | |
33 // kNotificationTypes[type].type == type). | |
34 const NotificationTypeInfo kNotificationTypes[] = { | |
35 { | |
36 DesktopNotifications::DEVICE, // type | |
37 "Device_", // notification_id_prefix | |
38 IDR_FILES_APP_ICON, // icon_id | |
39 IDS_REMOVABLE_DEVICE_DETECTION_TITLE, // title_id | |
40 IDS_REMOVABLE_DEVICE_SCANNING_MESSAGE // message_id | |
41 }, | |
42 { | |
43 DesktopNotifications::DEVICE_FAIL, // type | |
44 "DeviceFail_", // notification_id_prefix | |
45 IDR_FILES_APP_ICON, // icon_id | |
46 IDS_REMOVABLE_DEVICE_DETECTION_TITLE, // title_id | |
47 IDS_DEVICE_UNSUPPORTED_DEFAULT_MESSAGE // message_id | |
48 }, | |
49 { | |
50 DesktopNotifications::DEVICE_EXTERNAL_STORAGE_DISABLED, // type | |
51 "DeviceFail_", // nottification_id_prefix; same as for DEVICE_FAIL. | |
52 IDR_FILES_APP_ICON, // icon_id | |
53 IDS_REMOVABLE_DEVICE_DETECTION_TITLE, // title_id | |
54 IDS_EXTERNAL_STORAGE_DISABLED_MESSAGE // message_id | |
55 }, | |
56 { | |
57 DesktopNotifications::FORMAT_START, // type | |
58 "FormatStart_", // notification_id_prefix | |
59 IDR_FILES_APP_ICON, // icon_id | |
60 IDS_FORMATTING_OF_DEVICE_PENDING_TITLE, // title_id | |
61 IDS_FORMATTING_OF_DEVICE_PENDING_MESSAGE // message_id | |
62 }, | |
63 { | |
64 DesktopNotifications::FORMAT_START_FAIL, // type | |
65 "FormatComplete_", // notification_id_prefix | |
66 IDR_FILES_APP_ICON, // icon_id | |
67 IDS_FORMATTING_OF_DEVICE_FAILED_TITLE, // title_id | |
68 IDS_FORMATTING_STARTED_FAILURE_MESSAGE // message_id | |
69 }, | |
70 { | |
71 DesktopNotifications::FORMAT_SUCCESS, // type | |
72 "FormatComplete_", // notification_id_prefix | |
73 IDR_FILES_APP_ICON, // icon_id | |
74 IDS_FORMATTING_OF_DEVICE_FINISHED_TITLE, // title_id | |
75 IDS_FORMATTING_FINISHED_SUCCESS_MESSAGE // message_id | |
76 }, | |
77 { | |
78 DesktopNotifications::FORMAT_FAIL, // type | |
79 "FormatComplete_", // notifications_id_prefix | |
80 IDR_FILES_APP_ICON, // icon_id | |
81 IDS_FORMATTING_OF_DEVICE_FAILED_TITLE, // title_id | |
82 IDS_FORMATTING_FINISHED_FAILURE_MESSAGE // message_id | |
83 }, | |
84 }; | |
85 | |
86 int GetIconId(DesktopNotifications::NotificationType type) { | |
87 DCHECK_GE(type, 0); | |
88 DCHECK_LT(static_cast<size_t>(type), arraysize(kNotificationTypes)); | |
89 DCHECK(kNotificationTypes[type].type == type); | |
90 | |
91 return kNotificationTypes[type].icon_id; | |
92 } | |
93 | |
94 string16 GetTitle(DesktopNotifications::NotificationType type) { | |
95 DCHECK_GE(type, 0); | |
96 DCHECK_LT(static_cast<size_t>(type), arraysize(kNotificationTypes)); | |
97 DCHECK(kNotificationTypes[type].type == type); | |
98 | |
99 int id = kNotificationTypes[type].title_id; | |
100 if (id < 0) | |
101 return string16(); | |
102 return l10n_util::GetStringUTF16(id); | |
103 } | |
104 | |
105 string16 GetMessage(DesktopNotifications::NotificationType type) { | |
106 DCHECK_GE(type, 0); | |
107 DCHECK_LT(static_cast<size_t>(type), arraysize(kNotificationTypes)); | |
108 DCHECK(kNotificationTypes[type].type == type); | |
109 | |
110 int id = kNotificationTypes[type].message_id; | |
111 if (id < 0) | |
112 return string16(); | |
113 return l10n_util::GetStringUTF16(id); | |
114 } | |
115 | |
116 std::string GetNotificationId(DesktopNotifications::NotificationType type, | |
117 const std::string& path) { | |
118 DCHECK_GE(type, 0); | |
119 DCHECK_LT(static_cast<size_t>(type), arraysize(kNotificationTypes)); | |
120 DCHECK(kNotificationTypes[type].type == type); | |
121 | |
122 std::string id_prefix(kNotificationTypes[type].notification_id_prefix); | |
123 return id_prefix.append(path); | |
124 } | |
125 | |
126 } // namespace | |
127 | |
128 // Manages file browser notifications. Generates a desktop notification on | |
129 // construction and removes it from the host when closed. Owned by the host. | |
130 class DesktopNotifications::NotificationMessage { | |
131 public: | |
132 class Delegate : public NotificationDelegate { | |
133 public: | |
134 Delegate(const base::WeakPtr<DesktopNotifications>& host, | |
135 const std::string& id) | |
136 : host_(host), | |
137 id_(id) {} | |
138 virtual void Display() OVERRIDE {} | |
139 virtual void Error() OVERRIDE {} | |
140 virtual void Close(bool by_user) OVERRIDE { | |
141 if (host_) | |
142 host_->RemoveNotificationById(id_); | |
143 } | |
144 virtual void Click() OVERRIDE { | |
145 // TODO(tbarzic): Show more info page once we have one. | |
146 } | |
147 virtual std::string id() const OVERRIDE { return id_; } | |
148 virtual content::RenderViewHost* GetRenderViewHost() const OVERRIDE { | |
149 return NULL; | |
150 } | |
151 | |
152 private: | |
153 virtual ~Delegate() {} | |
154 | |
155 base::WeakPtr<DesktopNotifications> host_; | |
156 std::string id_; | |
157 | |
158 DISALLOW_COPY_AND_ASSIGN(Delegate); | |
159 }; | |
160 | |
161 NotificationMessage(DesktopNotifications* host, | |
162 Profile* profile, | |
163 NotificationType type, | |
164 const std::string& notification_id, | |
165 const string16& message) | |
166 : message_(message) { | |
167 const gfx::Image& icon = | |
168 ResourceBundle::GetSharedInstance().GetNativeImageNamed( | |
169 GetIconId(type)); | |
170 // TODO(mukai): refactor here to invoke NotificationUIManager directly. | |
171 const string16 replace_id = UTF8ToUTF16(notification_id); | |
172 DesktopNotificationService::AddIconNotification( | |
173 util::GetFileManagerBaseUrl(), GetTitle(type), | |
174 message, icon, replace_id, | |
175 new Delegate(host->AsWeakPtr(), notification_id), profile); | |
176 } | |
177 | |
178 ~NotificationMessage() {} | |
179 | |
180 // Used in test. | |
181 string16 message() { return message_; } | |
182 | |
183 private: | |
184 string16 message_; | |
185 | |
186 DISALLOW_COPY_AND_ASSIGN(NotificationMessage); | |
187 }; | |
188 | |
189 struct DesktopNotifications::MountRequestsInfo { | |
190 bool mount_success_exists; | |
191 bool fail_message_finalized; | |
192 bool fail_notification_shown; | |
193 bool non_parent_device_failed; | |
194 bool device_notification_hidden; | |
195 | |
196 MountRequestsInfo() : mount_success_exists(false), | |
197 fail_message_finalized(false), | |
198 fail_notification_shown(false), | |
199 non_parent_device_failed(false), | |
200 device_notification_hidden(false) { | |
201 } | |
202 }; | |
203 | |
204 DesktopNotifications::DesktopNotifications(Profile* profile) | |
205 : profile_(profile) { | |
206 } | |
207 | |
208 DesktopNotifications::~DesktopNotifications() { | |
209 STLDeleteContainerPairSecondPointers(notification_map_.begin(), | |
210 notification_map_.end()); | |
211 } | |
212 | |
213 void DesktopNotifications::RegisterDevice(const std::string& path) { | |
214 mount_requests_.insert(MountRequestsMap::value_type(path, | |
215 MountRequestsInfo())); | |
216 } | |
217 | |
218 void DesktopNotifications::UnregisterDevice(const std::string& path) { | |
219 mount_requests_.erase(path); | |
220 } | |
221 | |
222 void DesktopNotifications::ManageNotificationsOnMountCompleted( | |
223 const std::string& system_path, const std::string& label, bool is_parent, | |
224 bool success, bool is_unsupported) { | |
225 MountRequestsMap::iterator it = mount_requests_.find(system_path); | |
226 if (it == mount_requests_.end()) | |
227 return; | |
228 | |
229 // We have to hide device scanning notification if we haven't done it already. | |
230 if (!it->second.device_notification_hidden) { | |
231 HideNotification(DEVICE, system_path); | |
232 it->second.device_notification_hidden = true; | |
233 } | |
234 | |
235 // Check if there is fail notification for parent device. If so, disregard it. | |
236 // (parent device contains partition table, which is unmountable). | |
237 if (!is_parent && it->second.fail_notification_shown && | |
238 !it->second.non_parent_device_failed) { | |
239 HideNotification(DEVICE_FAIL, system_path); | |
240 it->second.fail_notification_shown = false; | |
241 } | |
242 | |
243 // If notification can't change any more, no need to continue. | |
244 if (it->second.fail_message_finalized) | |
245 return; | |
246 | |
247 // Do we have a multi-partition device for which at least one mount failed. | |
248 bool fail_on_multipartition_device = | |
249 success ? it->second.non_parent_device_failed | |
250 : it->second.mount_success_exists || | |
251 it->second.non_parent_device_failed; | |
252 | |
253 base::string16 message; | |
254 if (fail_on_multipartition_device) { | |
255 it->second.fail_message_finalized = true; | |
256 message = label.empty() ? | |
257 l10n_util::GetStringUTF16( | |
258 IDS_MULTIPART_DEVICE_UNSUPPORTED_DEFAULT_MESSAGE) : | |
259 l10n_util::GetStringFUTF16( | |
260 IDS_MULTIPART_DEVICE_UNSUPPORTED_MESSAGE, UTF8ToUTF16(label)); | |
261 } else if (!success) { | |
262 // First device failed. | |
263 if (!is_unsupported) { | |
264 message = label.empty() ? | |
265 l10n_util::GetStringUTF16(IDS_DEVICE_UNKNOWN_DEFAULT_MESSAGE) : | |
266 l10n_util::GetStringFUTF16(IDS_DEVICE_UNKNOWN_MESSAGE, | |
267 UTF8ToUTF16(label)); | |
268 } else { | |
269 message = label.empty() ? | |
270 l10n_util::GetStringUTF16(IDS_DEVICE_UNSUPPORTED_DEFAULT_MESSAGE) : | |
271 l10n_util::GetStringFUTF16(IDS_DEVICE_UNSUPPORTED_MESSAGE, | |
272 UTF8ToUTF16(label)); | |
273 } | |
274 } | |
275 | |
276 if (success) { | |
277 it->second.mount_success_exists = true; | |
278 } else { | |
279 it->second.non_parent_device_failed |= !is_parent; | |
280 } | |
281 | |
282 if (message.empty()) | |
283 return; | |
284 | |
285 if (it->second.fail_notification_shown) { | |
286 HideNotification(DEVICE_FAIL, system_path); | |
287 } else { | |
288 it->second.fail_notification_shown = true; | |
289 } | |
290 | |
291 ShowNotificationWithMessage(DEVICE_FAIL, system_path, message); | |
292 } | |
293 | |
294 void DesktopNotifications::ShowNotification(NotificationType type, | |
295 const std::string& path) { | |
296 ShowNotificationWithMessage(type, path, GetMessage(type)); | |
297 } | |
298 | |
299 void DesktopNotifications::ShowNotificationWithMessage( | |
300 NotificationType type, | |
301 const std::string& path, | |
302 const string16& message) { | |
303 std::string notification_id = GetNotificationId(type, path); | |
304 hidden_notifications_.erase(notification_id); | |
305 ShowNotificationById(type, notification_id, message); | |
306 } | |
307 | |
308 void DesktopNotifications::ShowNotificationDelayed( | |
309 NotificationType type, | |
310 const std::string& path, | |
311 base::TimeDelta delay) { | |
312 std::string notification_id = GetNotificationId(type, path); | |
313 hidden_notifications_.erase(notification_id); | |
314 base::MessageLoop::current()->PostDelayedTask( | |
315 FROM_HERE, | |
316 base::Bind(&DesktopNotifications::ShowNotificationById, AsWeakPtr(), | |
317 type, notification_id, GetMessage(type)), | |
318 delay); | |
319 } | |
320 | |
321 void DesktopNotifications::HideNotification(NotificationType type, | |
322 const std::string& path) { | |
323 std::string notification_id = GetNotificationId(type, path); | |
324 HideNotificationById(notification_id); | |
325 } | |
326 | |
327 void DesktopNotifications::HideNotificationDelayed( | |
328 NotificationType type, const std::string& path, base::TimeDelta delay) { | |
329 base::MessageLoop::current()->PostDelayedTask( | |
330 FROM_HERE, | |
331 base::Bind(&DesktopNotifications::HideNotification, AsWeakPtr(), | |
332 type, path), | |
333 delay); | |
334 } | |
335 | |
336 void DesktopNotifications::ShowNotificationById( | |
337 NotificationType type, | |
338 const std::string& notification_id, | |
339 const string16& message) { | |
340 if (hidden_notifications_.find(notification_id) != | |
341 hidden_notifications_.end()) { | |
342 // Notification was hidden after a delayed show was requested. | |
343 hidden_notifications_.erase(notification_id); | |
344 return; | |
345 } | |
346 if (notification_map_.find(notification_id) != notification_map_.end()) { | |
347 // Remove any existing notification with |notification_id|. | |
348 // Will trigger Delegate::Close which will call RemoveNotificationById. | |
349 DesktopNotificationService::RemoveNotification(notification_id); | |
350 DCHECK(notification_map_.find(notification_id) == notification_map_.end()); | |
351 } | |
352 // Create a new notification with |notification_id|. | |
353 NotificationMessage* new_message = | |
354 new NotificationMessage(this, profile_, type, notification_id, message); | |
355 notification_map_[notification_id] = new_message; | |
356 } | |
357 | |
358 void DesktopNotifications::HideNotificationById( | |
359 const std::string& notification_id) { | |
360 NotificationMap::iterator it = notification_map_.find(notification_id); | |
361 if (it != notification_map_.end()) { | |
362 // Will trigger Delegate::Close which will call RemoveNotificationById. | |
363 DesktopNotificationService::RemoveNotification(notification_id); | |
364 } else { | |
365 // Mark as hidden so it does not get shown from a delayed task. | |
366 hidden_notifications_.insert(notification_id); | |
367 } | |
368 } | |
369 | |
370 void DesktopNotifications::RemoveNotificationById( | |
371 const std::string& notification_id) { | |
372 NotificationMap::iterator it = notification_map_.find(notification_id); | |
373 if (it != notification_map_.end()) { | |
374 NotificationMessage* notification = it->second; | |
375 notification_map_.erase(it); | |
376 delete notification; | |
377 } | |
378 } | |
379 | |
380 string16 DesktopNotifications::GetNotificationMessageForTest( | |
381 const std::string& id) const { | |
382 NotificationMap::const_iterator it = notification_map_.find(id); | |
383 if (it == notification_map_.end()) | |
384 return string16(); | |
385 return it->second->message(); | |
386 } | |
387 | |
388 } // namespace file_manager | |
OLD | NEW |