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