611d4e89a432fda8d5ab333b57d7454cea3ced6d
[platform/framework/web/crosswalk.git] / src / ui / message_center / notification_list.cc
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::HasNotification(const std::string& id) {
164   return GetNotification(id) != notifications_.end();
165 }
166
167 bool NotificationList::HasNotificationOfType(const std::string& id,
168                                              const NotificationType type) {
169   Notifications::iterator iter = GetNotification(id);
170   if (iter == notifications_.end())
171     return false;
172
173   return (*iter)->type() == type;
174 }
175
176 bool NotificationList::HasPopupNotifications(
177     const NotificationBlockers& blockers) {
178   for (Notifications::iterator iter = notifications_.begin();
179        iter != notifications_.end(); ++iter) {
180     if ((*iter)->priority() < DEFAULT_PRIORITY)
181       break;
182     if (!ShouldShowNotificationAsPopup(**iter, blockers))
183       continue;
184     if (!(*iter)->shown_as_popup())
185       return true;
186   }
187   return false;
188 }
189
190 NotificationList::PopupNotifications NotificationList::GetPopupNotifications(
191     const NotificationBlockers& blockers,
192     std::list<std::string>* blocked_ids) {
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     if (!ShouldShowNotificationAsPopup(**iter, blockers)) {
207       if (blocked_ids)
208         blocked_ids->push_back((*iter)->id());
209       continue;
210     }
211
212     // Checking limits. No limits for HIGH/MAX priority. DEFAULT priority
213     // will return at most kMaxVisiblePopupNotifications entries. If the
214     // popup entries are more, older entries are used. see crbug.com/165768
215     if ((*iter)->priority() == DEFAULT_PRIORITY &&
216         default_priority_popup_count++ >= kMaxVisiblePopupNotifications) {
217       continue;
218     }
219
220     result.insert(*iter);
221   }
222   return result;
223 }
224
225 void NotificationList::MarkSinglePopupAsShown(
226     const std::string& id, bool mark_notification_as_read) {
227   Notifications::iterator iter = GetNotification(id);
228   DCHECK(iter != notifications_.end());
229
230   if ((*iter)->shown_as_popup())
231     return;
232
233   // System notification is marked as shown only when marked as read.
234   if ((*iter)->priority() != SYSTEM_PRIORITY || mark_notification_as_read)
235     (*iter)->set_shown_as_popup(true);
236
237   // The popup notification is already marked as read when it's displayed.
238   // Set the is_read() back to false if necessary.
239   if (!mark_notification_as_read)
240     (*iter)->set_is_read(false);
241 }
242
243 void NotificationList::MarkSinglePopupAsDisplayed(const std::string& id) {
244   Notifications::iterator iter = GetNotification(id);
245   if (iter == notifications_.end())
246     return;
247
248   if ((*iter)->shown_as_popup())
249     return;
250
251   if (!(*iter)->IsRead())
252     (*iter)->set_is_read(true);
253 }
254
255 NotificationDelegate* NotificationList::GetNotificationDelegate(
256     const std::string& id) {
257   Notifications::iterator iter = GetNotification(id);
258   if (iter == notifications_.end())
259     return NULL;
260   return (*iter)->delegate();
261 }
262
263 void NotificationList::SetQuietMode(bool quiet_mode) {
264   quiet_mode_ = quiet_mode;
265   if (quiet_mode_) {
266     for (Notifications::iterator iter = notifications_.begin();
267          iter != notifications_.end();
268          ++iter) {
269       (*iter)->set_shown_as_popup(true);
270     }
271   }
272 }
273
274 NotificationList::Notifications NotificationList::GetVisibleNotifications(
275     const NotificationBlockers& blockers) const {
276   Notifications result;
277   for (Notifications::const_iterator iter = notifications_.begin();
278        iter != notifications_.end(); ++iter) {
279     bool should_show = true;
280     for (size_t i = 0; i < blockers.size(); ++i) {
281       if (!blockers[i]->ShouldShowNotification((*iter)->notifier_id())) {
282         should_show = false;
283         break;
284       }
285     }
286     if (should_show)
287       result.insert(*iter);
288   }
289
290   return result;
291 }
292
293 size_t NotificationList::NotificationCount(
294     const NotificationBlockers& blockers) const {
295   return GetVisibleNotifications(blockers).size();
296 }
297
298 size_t NotificationList::UnreadCount(
299     const NotificationBlockers& blockers) const {
300   Notifications notifications = GetVisibleNotifications(blockers);
301   size_t unread_count = 0;
302   for (Notifications::const_iterator iter = notifications.begin();
303        iter != notifications.end(); ++iter) {
304     if (!(*iter)->IsRead())
305       ++unread_count;
306   }
307   return unread_count;
308 }
309
310 NotificationList::Notifications::iterator NotificationList::GetNotification(
311     const std::string& id) {
312   for (Notifications::iterator iter = notifications_.begin();
313        iter != notifications_.end(); ++iter) {
314     if ((*iter)->id() == id)
315       return iter;
316   }
317   return notifications_.end();
318 }
319
320 void NotificationList::EraseNotification(Notifications::iterator iter) {
321   delete *iter;
322   notifications_.erase(iter);
323 }
324
325 void NotificationList::PushNotification(scoped_ptr<Notification> notification) {
326   // Ensure that notification.id is unique by erasing any existing
327   // notification with the same id (shouldn't normally happen).
328   Notifications::iterator iter = GetNotification(notification->id());
329   bool state_inherited = false;
330   if (iter != notifications_.end()) {
331     notification->CopyState(*iter);
332     state_inherited = true;
333     EraseNotification(iter);
334   }
335   // Add the notification to the the list and mark it unread and unshown.
336   if (!state_inherited) {
337     // TODO(mukai): needs to distinguish if a notification is dismissed by
338     // the quiet mode or user operation.
339     notification->set_is_read(false);
340     notification->set_shown_as_popup(message_center_visible_
341                                      || quiet_mode_
342                                      || notification->shown_as_popup());
343   }
344   // Take ownership. The notification can only be removed from the list
345   // in EraseNotification(), which will delete it.
346   notifications_.insert(notification.release());
347 }
348
349 }  // namespace message_center