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.
5 #include "chrome/browser/chromeos/file_manager/desktop_notifications.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"
19 namespace file_manager {
22 struct NotificationTypeInfo {
23 DesktopNotifications::NotificationType type;
24 const char* notification_id_prefix;
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[] = {
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
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
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
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
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
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
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
86 int GetIconId(DesktopNotifications::NotificationType type) {
88 DCHECK_LT(static_cast<size_t>(type), arraysize(kNotificationTypes));
89 DCHECK(kNotificationTypes[type].type == type);
91 return kNotificationTypes[type].icon_id;
94 string16 GetTitle(DesktopNotifications::NotificationType type) {
96 DCHECK_LT(static_cast<size_t>(type), arraysize(kNotificationTypes));
97 DCHECK(kNotificationTypes[type].type == type);
99 int id = kNotificationTypes[type].title_id;
102 return l10n_util::GetStringUTF16(id);
105 string16 GetMessage(DesktopNotifications::NotificationType type) {
107 DCHECK_LT(static_cast<size_t>(type), arraysize(kNotificationTypes));
108 DCHECK(kNotificationTypes[type].type == type);
110 int id = kNotificationTypes[type].message_id;
113 return l10n_util::GetStringUTF16(id);
116 std::string GetNotificationId(DesktopNotifications::NotificationType type,
117 const std::string& path) {
119 DCHECK_LT(static_cast<size_t>(type), arraysize(kNotificationTypes));
120 DCHECK(kNotificationTypes[type].type == type);
122 std::string id_prefix(kNotificationTypes[type].notification_id_prefix);
123 return id_prefix.append(path);
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 {
132 class Delegate : public NotificationDelegate {
134 Delegate(const base::WeakPtr<DesktopNotifications>& host,
135 const std::string& id)
138 virtual void Display() OVERRIDE {}
139 virtual void Error() OVERRIDE {}
140 virtual void Close(bool by_user) OVERRIDE {
142 host_->RemoveNotificationById(id_);
144 virtual void Click() OVERRIDE {
145 // TODO(tbarzic): Show more info page once we have one.
147 virtual std::string id() const OVERRIDE { return id_; }
148 virtual content::RenderViewHost* GetRenderViewHost() const OVERRIDE {
153 virtual ~Delegate() {}
155 base::WeakPtr<DesktopNotifications> host_;
158 DISALLOW_COPY_AND_ASSIGN(Delegate);
161 NotificationMessage(DesktopNotifications* host,
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(
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);
178 ~NotificationMessage() {}
181 string16 message() { return message_; }
186 DISALLOW_COPY_AND_ASSIGN(NotificationMessage);
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;
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) {
204 DesktopNotifications::DesktopNotifications(Profile* profile)
205 : profile_(profile) {
208 DesktopNotifications::~DesktopNotifications() {
209 STLDeleteContainerPairSecondPointers(notification_map_.begin(),
210 notification_map_.end());
213 void DesktopNotifications::RegisterDevice(const std::string& path) {
214 mount_requests_.insert(MountRequestsMap::value_type(path,
215 MountRequestsInfo()));
218 void DesktopNotifications::UnregisterDevice(const std::string& path) {
219 mount_requests_.erase(path);
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())
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;
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;
243 // If notification can't change any more, no need to continue.
244 if (it->second.fail_message_finalized)
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;
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,
269 message = label.empty() ?
270 l10n_util::GetStringUTF16(IDS_DEVICE_UNSUPPORTED_DEFAULT_MESSAGE) :
271 l10n_util::GetStringFUTF16(IDS_DEVICE_UNSUPPORTED_MESSAGE,
277 it->second.mount_success_exists = true;
279 it->second.non_parent_device_failed |= !is_parent;
285 if (it->second.fail_notification_shown) {
286 HideNotification(DEVICE_FAIL, system_path);
288 it->second.fail_notification_shown = true;
291 ShowNotificationWithMessage(DEVICE_FAIL, system_path, message);
294 void DesktopNotifications::ShowNotification(NotificationType type,
295 const std::string& path) {
296 ShowNotificationWithMessage(type, path, GetMessage(type));
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);
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(
316 base::Bind(&DesktopNotifications::ShowNotificationById, AsWeakPtr(),
317 type, notification_id, GetMessage(type)),
321 void DesktopNotifications::HideNotification(NotificationType type,
322 const std::string& path) {
323 std::string notification_id = GetNotificationId(type, path);
324 HideNotificationById(notification_id);
327 void DesktopNotifications::HideNotificationDelayed(
328 NotificationType type, const std::string& path, base::TimeDelta delay) {
329 base::MessageLoop::current()->PostDelayedTask(
331 base::Bind(&DesktopNotifications::HideNotification, AsWeakPtr(),
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);
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());
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;
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);
365 // Mark as hidden so it does not get shown from a delayed task.
366 hidden_notifications_.insert(notification_id);
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);
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())
385 return it->second->message();
388 } // namespace file_manager