- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / file_manager / desktop_notifications.cc
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/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/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