| 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 |