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