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