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