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