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