• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ShouldShowNotificationAsPopup(const Notification & notification,const NotificationBlockers & blockers)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 
operator ()(Notification * n1,Notification * n2)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 
operator ()(Notification * n1,Notification * n2)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 
NotificationList()55 NotificationList::NotificationList()
56     : message_center_visible_(false),
57       quiet_mode_(false) {
58 }
59 
~NotificationList()60 NotificationList::~NotificationList() {
61   STLDeleteContainerPointers(notifications_.begin(), notifications_.end());
62 }
63 
SetMessageCenterVisible(bool visible,std::set<std::string> * updated_ids)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 
AddNotification(scoped_ptr<Notification> notification)88 void NotificationList::AddNotification(scoped_ptr<Notification> notification) {
89   PushNotification(notification.Pass());
90 }
91 
UpdateNotificationMessage(const std::string & old_id,scoped_ptr<Notification> new_notification)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 
RemoveNotification(const std::string & id)120 void NotificationList::RemoveNotification(const std::string& id) {
121   EraseNotification(GetNotification(id));
122 }
123 
GetNotificationsByNotifierId(const NotifierId & notifier_id)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 
SetNotificationIcon(const std::string & notification_id,const gfx::Image & image)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 
SetNotificationImage(const std::string & notification_id,const gfx::Image & image)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 
SetNotificationButtonIcon(const std::string & notification_id,int button_index,const gfx::Image & image)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 
HasNotificationOfType(const std::string & id,const NotificationType type)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 
HasPopupNotifications(const NotificationBlockers & blockers)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 
GetPopupNotifications(const NotificationBlockers & blockers,std::list<std::string> * blocked_ids)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 
MarkSinglePopupAsShown(const std::string & id,bool mark_notification_as_read)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 
MarkSinglePopupAsDisplayed(const std::string & id)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 
GetNotificationDelegate(const std::string & id)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 
SetQuietMode(bool quiet_mode)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 
GetNotificationById(const std::string & id)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 
GetVisibleNotifications(const NotificationBlockers & blockers) const277 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 
NotificationCount(const NotificationBlockers & blockers) const296 size_t NotificationList::NotificationCount(
297     const NotificationBlockers& blockers) const {
298   return GetVisibleNotifications(blockers).size();
299 }
300 
UnreadCount(const NotificationBlockers & blockers) const301 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 
GetNotification(const std::string & id)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 
EraseNotification(Notifications::iterator iter)323 void NotificationList::EraseNotification(Notifications::iterator iter) {
324   delete *iter;
325   notifications_.erase(iter);
326 }
327 
PushNotification(scoped_ptr<Notification> notification)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