1 // Copyright (c) 2012 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 "ui/message_center/notification_list.h" 6 7 #include "base/bind.h" 8 #include "base/logging.h" 9 #include "base/stl_util.h" 10 #include "base/time/time.h" 11 #include "base/values.h" 12 #include "ui/message_center/message_center_style.h" 13 #include "ui/message_center/notification.h" 14 #include "ui/message_center/notification_types.h" 15 16 namespace message_center { 17 18 bool ComparePriorityTimestampSerial::operator()(Notification* n1, 19 Notification* n2) { 20 if (n1->priority() > n2->priority()) // Higher pri go first. 21 return true; 22 if (n1->priority() < n2->priority()) 23 return false; 24 return CompareTimestampSerial()(n1, n2); 25 } 26 27 bool CompareTimestampSerial::operator()(Notification* n1, Notification* n2) { 28 if (n1->timestamp() > n2->timestamp()) // Newer come first. 29 return true; 30 if (n1->timestamp() < n2->timestamp()) 31 return false; 32 if (n1->serial_number() > n2->serial_number()) // Newer come first. 33 return true; 34 if (n1->serial_number() < n2->serial_number()) 35 return false; 36 return false; 37 } 38 39 NotificationList::NotificationList() 40 : message_center_visible_(false), 41 unread_count_(0), 42 quiet_mode_(false) { 43 } 44 45 NotificationList::~NotificationList() { 46 STLDeleteContainerPointers(notifications_.begin(), notifications_.end()); 47 } 48 49 void NotificationList::SetMessageCenterVisible( 50 bool visible, 51 std::set<std::string>* updated_ids) { 52 if (message_center_visible_ == visible) 53 return; 54 55 message_center_visible_ = visible; 56 57 if (!visible) 58 return; 59 60 unread_count_ = 0; 61 62 for (Notifications::iterator iter = notifications_.begin(); 63 iter != notifications_.end(); ++iter) { 64 Notification* notification = *iter; 65 if (notification->priority() < SYSTEM_PRIORITY) 66 notification->set_shown_as_popup(true); 67 notification->set_is_read(true); 68 if (updated_ids && 69 !(notification->shown_as_popup() && notification->is_read())) { 70 updated_ids->insert(notification->id()); 71 } 72 } 73 } 74 75 void NotificationList::AddNotification(scoped_ptr<Notification> notification) { 76 PushNotification(notification.Pass()); 77 } 78 79 void NotificationList::UpdateNotificationMessage( 80 const std::string& old_id, 81 scoped_ptr<Notification> new_notification) { 82 Notifications::iterator iter = GetNotification(old_id); 83 if (iter == notifications_.end()) 84 return; 85 86 new_notification->CopyState(*iter); 87 88 // Handles priority promotion. If the notification is already dismissed but 89 // the updated notification has higher priority, it should re-appear as a 90 // toast. 91 if ((*iter)->priority() < new_notification->priority()) { 92 new_notification->set_is_read(false); 93 new_notification->set_shown_as_popup(false); 94 } 95 96 // Do not use EraseNotification and PushNotification, since we don't want to 97 // change unread counts nor to update is_read/shown_as_popup states. 98 Notification* old = *iter; 99 notifications_.erase(iter); 100 delete old; 101 notifications_.insert(new_notification.release()); 102 } 103 104 void NotificationList::RemoveNotification(const std::string& id) { 105 EraseNotification(GetNotification(id)); 106 } 107 108 void NotificationList::RemoveAllNotifications() { 109 for (Notifications::iterator loopiter = notifications_.begin(); 110 loopiter != notifications_.end(); ) { 111 Notifications::iterator curiter = loopiter++; 112 EraseNotification(curiter); 113 } 114 unread_count_ = 0; 115 } 116 117 NotificationList::Notifications NotificationList::GetNotificationsBySource( 118 const std::string& id) { 119 Notifications notifications; 120 Notifications::iterator source_iter = GetNotification(id); 121 if (source_iter == notifications_.end()) 122 return notifications; 123 124 string16 display_source = (*source_iter)->display_source(); 125 for (Notifications::iterator iter = notifications_.begin(); 126 iter != notifications_.end(); ++iter) { 127 if ((*iter)->display_source() == display_source) 128 notifications.insert(*iter); 129 } 130 return notifications; 131 } 132 133 NotificationList::Notifications NotificationList::GetNotificationsByExtension( 134 const std::string& id) { 135 Notifications notifications; 136 Notifications::iterator source_iter = GetNotification(id); 137 if (source_iter == notifications_.end()) 138 return notifications; 139 140 std::string extension_id = (*source_iter)->extension_id(); 141 for (Notifications::iterator iter = notifications_.begin(); 142 iter != notifications_.end(); ++iter) { 143 if ((*iter)->extension_id() == extension_id) 144 notifications.insert(*iter); 145 } 146 return notifications; 147 } 148 149 bool NotificationList::SetNotificationIcon(const std::string& notification_id, 150 const gfx::Image& image) { 151 Notifications::iterator iter = GetNotification(notification_id); 152 if (iter == notifications_.end()) 153 return false; 154 (*iter)->set_icon(image); 155 return true; 156 } 157 158 bool NotificationList::SetNotificationImage(const std::string& notification_id, 159 const gfx::Image& image) { 160 Notifications::iterator iter = GetNotification(notification_id); 161 if (iter == notifications_.end()) 162 return false; 163 (*iter)->set_image(image); 164 return true; 165 } 166 167 bool NotificationList::SetNotificationButtonIcon( 168 const std::string& notification_id, int button_index, 169 const gfx::Image& image) { 170 Notifications::iterator iter = GetNotification(notification_id); 171 if (iter == notifications_.end()) 172 return false; 173 (*iter)->SetButtonIcon(button_index, image); 174 return true; 175 } 176 177 bool NotificationList::HasNotification(const std::string& id) { 178 return GetNotification(id) != notifications_.end(); 179 } 180 181 bool NotificationList::HasPopupNotifications() { 182 for (Notifications::iterator iter = notifications_.begin(); 183 iter != notifications_.end(); ++iter) { 184 if ((*iter)->priority() < DEFAULT_PRIORITY) 185 break; 186 if (!(*iter)->shown_as_popup()) 187 return true; 188 } 189 return false; 190 } 191 192 NotificationList::PopupNotifications NotificationList::GetPopupNotifications() { 193 PopupNotifications result; 194 size_t default_priority_popup_count = 0; 195 196 // Collect notifications that should be shown as popups. Start from oldest. 197 for (Notifications::const_reverse_iterator iter = notifications_.rbegin(); 198 iter != notifications_.rend(); iter++) { 199 if ((*iter)->shown_as_popup()) 200 continue; 201 202 // No popups for LOW/MIN priority. 203 if ((*iter)->priority() < DEFAULT_PRIORITY) 204 continue; 205 206 // Checking limits. No limits for HIGH/MAX priority. DEFAULT priority 207 // will return at most kMaxVisiblePopupNotifications entries. If the 208 // popup entries are more, older entries are used. see crbug.com/165768 209 if ((*iter)->priority() == DEFAULT_PRIORITY && 210 default_priority_popup_count++ >= kMaxVisiblePopupNotifications) { 211 continue; 212 } 213 214 result.insert(*iter); 215 } 216 return result; 217 } 218 219 Notification* NotificationList::GetPopup(const std::string& id) { 220 PopupNotifications popups = GetPopupNotifications(); 221 for (PopupNotifications::iterator iter = popups.begin(); iter != popups.end(); 222 ++iter) { 223 if ((*iter)->id() == id) 224 return *iter; 225 } 226 227 return NULL; 228 } 229 230 void NotificationList::MarkPopupsAsShown(int priority) { 231 PopupNotifications popups = GetPopupNotifications(); 232 for (PopupNotifications::iterator iter = popups.begin(); 233 iter != popups.end(); ++iter) { 234 if ((*iter)->priority() == priority) 235 (*iter)->set_shown_as_popup(true); 236 } 237 } 238 239 void NotificationList::MarkSinglePopupAsShown( 240 const std::string& id, bool mark_notification_as_read) { 241 Notifications::iterator iter = GetNotification(id); 242 DCHECK(iter != notifications_.end()); 243 244 if ((*iter)->shown_as_popup()) 245 return; 246 247 // System notification is marked as shown only when marked as read. 248 if ((*iter)->priority() != SYSTEM_PRIORITY || mark_notification_as_read) 249 (*iter)->set_shown_as_popup(true); 250 251 // The popup notification is already marked as read when it's displayed. 252 // Set the is_read() back to false if necessary. 253 if (!mark_notification_as_read) { 254 (*iter)->set_is_read(false); 255 ++unread_count_; 256 } 257 } 258 259 void NotificationList::MarkSinglePopupAsDisplayed(const std::string& id) { 260 Notifications::iterator iter = GetNotification(id); 261 if (iter == notifications_.end()) 262 return; 263 264 if ((*iter)->shown_as_popup()) 265 return; 266 267 if (!(*iter)->is_read()) { 268 (*iter)->set_is_read(true); 269 --unread_count_; 270 } 271 } 272 273 void NotificationList::MarkNotificationAsExpanded(const std::string& id) { 274 Notifications::iterator iter = GetNotification(id); 275 if (iter != notifications_.end()) 276 (*iter)->set_is_expanded(true); 277 } 278 279 NotificationDelegate* NotificationList::GetNotificationDelegate( 280 const std::string& id) { 281 Notifications::iterator iter = GetNotification(id); 282 if (iter == notifications_.end()) 283 return NULL; 284 return (*iter)->delegate(); 285 } 286 287 void NotificationList::SetQuietMode(bool quiet_mode) { 288 SetQuietModeInternal(quiet_mode); 289 quiet_mode_timer_.reset(); 290 } 291 292 void NotificationList::EnterQuietModeWithExpire( 293 const base::TimeDelta& expires_in) { 294 if (quiet_mode_timer_.get()) { 295 // Note that the capital Reset() is the method to restart the timer, not 296 // scoped_ptr::reset(). 297 quiet_mode_timer_->Reset(); 298 } else { 299 SetQuietModeInternal(true); 300 quiet_mode_timer_.reset(new base::OneShotTimer<NotificationList>); 301 quiet_mode_timer_->Start(FROM_HERE, expires_in, base::Bind( 302 &NotificationList::SetQuietMode, base::Unretained(this), false)); 303 } 304 } 305 306 const NotificationList::Notifications& NotificationList::GetNotifications() { 307 return notifications_; 308 } 309 310 size_t NotificationList::NotificationCount() const { 311 return notifications_.size(); 312 } 313 314 void NotificationList::SetQuietModeInternal(bool quiet_mode) { 315 quiet_mode_ = quiet_mode; 316 if (quiet_mode_) { 317 for (Notifications::iterator iter = notifications_.begin(); 318 iter != notifications_.end(); 319 ++iter) { 320 (*iter)->set_shown_as_popup(true); 321 } 322 } 323 } 324 325 NotificationList::Notifications::iterator 326 NotificationList::GetNotification(const std::string& id) { 327 for (Notifications::iterator iter = notifications_.begin(); 328 iter != notifications_.end(); ++iter) { 329 if ((*iter)->id() == id) 330 return iter; 331 } 332 return notifications_.end(); 333 } 334 335 void NotificationList::EraseNotification(Notifications::iterator iter) { 336 if (!(*iter)->is_read() && (*iter)->priority() > MIN_PRIORITY) 337 --unread_count_; 338 delete *iter; 339 notifications_.erase(iter); 340 } 341 342 void NotificationList::PushNotification(scoped_ptr<Notification> notification) { 343 // Ensure that notification.id is unique by erasing any existing 344 // notification with the same id (shouldn't normally happen). 345 Notifications::iterator iter = GetNotification(notification->id()); 346 bool state_inherited = false; 347 if (iter != notifications_.end()) { 348 notification->CopyState(*iter); 349 state_inherited = true; 350 EraseNotification(iter); 351 // if |iter| is unread, EraseNotification decrements |unread_count_| but 352 // actually the count is unchanged since |notification| will be added. 353 if (!notification->is_read()) 354 ++unread_count_; 355 } 356 // Add the notification to the the list and mark it unread and unshown. 357 if (!state_inherited) { 358 // TODO(mukai): needs to distinguish if a notification is dismissed by 359 // the quiet mode or user operation. 360 notification->set_is_read(false); 361 notification->set_shown_as_popup(message_center_visible_ || quiet_mode_); 362 if (notification->priority() > MIN_PRIORITY) 363 ++unread_count_; 364 } 365 // Take ownership. The notification can only be removed from the list 366 // in EraseNotification(), which will delete it. 367 notifications_.insert(notification.release()); 368 } 369 370 } // namespace message_center 371