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.
5 #include "ui/message_center/notification_list.h"
8 #include "base/logging.h"
10 #include "base/values.h"
12 namespace message_center
{
14 const size_t NotificationList::kMaxVisibleMessageCenterNotifications
= 100;
15 const size_t NotificationList::kMaxVisiblePopupNotifications
= 2;
17 NotificationList::Notification::Notification() : is_read(false),
18 shown_as_popup(false) {
21 NotificationList::Notification::~Notification() {
24 NotificationList::NotificationList(Delegate
* delegate
)
25 : delegate_(delegate
),
26 message_center_visible_(false),
31 NotificationList::~NotificationList() {
34 void NotificationList::SetMessageCenterVisible(bool visible
) {
35 if (message_center_visible_
== visible
)
37 message_center_visible_
= visible
;
39 // When the list is hidden, clear the unread count, and mark all
40 // notifications as read and shown.
42 for (NotificationMap::iterator mapiter
= notifications_
.begin();
43 mapiter
!= notifications_
.end(); ++mapiter
) {
44 for (Notifications::iterator iter
= mapiter
->second
.begin();
45 iter
!= mapiter
->second
.end(); ++iter
) {
47 iter
->shown_as_popup
= true;
53 void NotificationList::AddNotification(
54 ui::notifications::NotificationType type
,
55 const std::string
& id
,
56 const string16
& title
,
57 const string16
& message
,
58 const string16
& display_source
,
59 const std::string
& extension_id
,
60 const DictionaryValue
* optional_fields
) {
61 Notification notification
;
62 notification
.type
= type
;
64 notification
.title
= title
;
65 notification
.message
= message
;
66 notification
.display_source
= display_source
;
67 notification
.extension_id
= extension_id
;
69 // Initialize primitive fields before unpacking optional fields.
70 // timestamp initializes to default NULL time.
71 notification
.priority
= ui::notifications::DEFAULT_PRIORITY
;
72 notification
.unread_count
= 0;
74 UnpackOptionalFields(optional_fields
, ¬ification
);
76 PushNotification(notification
);
79 void NotificationList::UnpackOptionalFields(
80 const DictionaryValue
* optional_fields
, Notification
* notification
) {
84 if (optional_fields
->HasKey(ui::notifications::kPriorityKey
))
85 optional_fields
->GetInteger(ui::notifications::kPriorityKey
,
86 ¬ification
->priority
);
87 if (optional_fields
->HasKey(ui::notifications::kTimestampKey
)) {
88 std::string time_string
;
89 optional_fields
->GetString(ui::notifications::kTimestampKey
, &time_string
);
90 base::Time::FromString(time_string
.c_str(), ¬ification
->timestamp
);
92 if (optional_fields
->HasKey(ui::notifications::kUnreadCountKey
))
93 optional_fields
->GetInteger(ui::notifications::kUnreadCountKey
,
94 ¬ification
->unread_count
);
95 if (optional_fields
->HasKey(ui::notifications::kButtonOneTitleKey
))
96 optional_fields
->GetString(ui::notifications::kButtonOneTitleKey
,
97 ¬ification
->button_one_title
);
98 if (optional_fields
->HasKey(ui::notifications::kButtonTwoTitleKey
))
99 optional_fields
->GetString(ui::notifications::kButtonTwoTitleKey
,
100 ¬ification
->button_two_title
);
101 if (optional_fields
->HasKey(ui::notifications::kExpandedMessageKey
))
102 optional_fields
->GetString(ui::notifications::kExpandedMessageKey
,
103 ¬ification
->expanded_message
);
104 if (optional_fields
->HasKey(ui::notifications::kItemsKey
)) {
105 const ListValue
* items
;
106 CHECK(optional_fields
->GetList(ui::notifications::kItemsKey
, &items
));
107 for (size_t i
= 0; i
< items
->GetSize(); ++i
) {
110 const base::DictionaryValue
* item
;
111 items
->GetDictionary(i
, &item
);
112 item
->GetString(ui::notifications::kItemTitleKey
, &title
);
113 item
->GetString(ui::notifications::kItemMessageKey
, &message
);
114 notification
->items
.push_back(NotificationItem(title
, message
));
119 void NotificationList::UpdateNotificationMessage(
120 const std::string
& old_id
,
121 const std::string
& new_id
,
122 const string16
& title
,
123 const string16
& message
,
124 const base::DictionaryValue
* optional_fields
) {
125 Notifications::iterator iter
;
126 if (!GetNotification(old_id
, &iter
))
128 // Copy and update notification, then move it to the front of the list.
129 Notification
notification(*iter
);
130 notification
.id
= new_id
;
131 notification
.title
= title
;
132 notification
.message
= message
;
133 UnpackOptionalFields(optional_fields
, ¬ification
);
134 EraseNotification(iter
);
135 PushNotification(notification
);
138 bool NotificationList::RemoveNotification(const std::string
& id
) {
139 Notifications::iterator iter
;
140 if (!GetNotification(id
, &iter
))
142 EraseNotification(iter
);
146 void NotificationList::RemoveAllNotifications() {
147 notifications_
.clear();
151 void NotificationList::SendRemoveNotificationsBySource(
152 const std::string
& id
) {
153 Notifications::iterator source_iter
;
154 if (!GetNotification(id
, &source_iter
))
156 string16 display_source
= source_iter
->display_source
;
157 for (NotificationMap::iterator mapiter
= notifications_
.begin();
158 mapiter
!= notifications_
.end(); ++mapiter
) {
159 for (Notifications::iterator loopiter
= mapiter
->second
.begin();
160 loopiter
!= mapiter
->second
.end(); ) {
161 Notifications::iterator curiter
= loopiter
++;
162 if (curiter
->display_source
== display_source
)
163 delegate_
->SendRemoveNotification(curiter
->id
);
168 void NotificationList::SendRemoveNotificationsByExtension(
169 const std::string
& id
) {
170 Notifications::iterator source_iter
;
171 if (!GetNotification(id
, &source_iter
))
173 std::string extension_id
= source_iter
->extension_id
;
174 for (NotificationMap::iterator mapiter
= notifications_
.begin();
175 mapiter
!= notifications_
.end(); ++mapiter
) {
176 for (Notifications::iterator loopiter
= mapiter
->second
.begin();
177 loopiter
!= mapiter
->second
.end(); ) {
178 Notifications::iterator curiter
= loopiter
++;
179 if (curiter
->extension_id
== extension_id
)
180 delegate_
->SendRemoveNotification(curiter
->id
);
185 bool NotificationList::SetNotificationPrimaryIcon(const std::string
& id
,
186 const gfx::ImageSkia
& image
) {
187 Notifications::iterator iter
;
188 if (!GetNotification(id
, &iter
))
190 iter
->primary_icon
= image
;
194 bool NotificationList::SetNotificationSecondaryIcon(
195 const std::string
& id
,
196 const gfx::ImageSkia
& image
) {
197 Notifications::iterator iter
;
198 if (!GetNotification(id
, &iter
))
200 iter
->secondary_icon
= image
;
204 bool NotificationList::SetNotificationImage(const std::string
& id
,
205 const gfx::ImageSkia
& image
) {
206 Notifications::iterator iter
;
207 if (!GetNotification(id
, &iter
))
213 bool NotificationList::HasNotification(const std::string
& id
) {
214 Notifications::iterator dummy
;
215 return GetNotification(id
, &dummy
);
218 bool NotificationList::HasPopupNotifications() {
219 for (int i
= ui::notifications::DEFAULT_PRIORITY
;
220 i
<= ui::notifications::MAX_PRIORITY
; ++i
) {
221 Notifications notifications
= notifications_
[i
];
222 if (!notifications
.empty() && !notifications
.front().shown_as_popup
)
228 void NotificationList::GetPopupNotifications(
229 NotificationList::Notifications
* notifications
) {
230 typedef std::pair
<Notifications::iterator
, Notifications::iterator
>
232 // In the popup, latest should come earlier.
233 std::list
<NotificationRange
> iters
;
234 for (int i
= ui::notifications::DEFAULT_PRIORITY
;
235 i
<= ui::notifications::MAX_PRIORITY
; ++i
) {
236 Notifications::iterator first
, last
;
237 GetPopupIterators(i
, first
, last
);
239 iters
.push_back(make_pair(first
, last
));
241 notifications
->clear();
242 while (!iters
.empty()) {
243 std::list
<NotificationRange
>::iterator max_iter
= iters
.begin();
244 std::list
<NotificationRange
>::iterator iter
= max_iter
;
246 for (; iter
!= iters
.end(); ++iter
) {
247 if (max_iter
->first
->timestamp
< iter
->first
->timestamp
)
250 notifications
->push_back(*(max_iter
->first
));
252 if (max_iter
->first
== max_iter
->second
)
253 iters
.erase(max_iter
);
257 void NotificationList::MarkPopupsAsShown(int priority
) {
258 Notifications::iterator first
, last
;
259 GetPopupIterators(priority
, first
, last
);
260 for (Notifications::iterator iter
= first
; iter
!= last
; ++iter
)
261 iter
->shown_as_popup
= true;
264 void NotificationList::SetQuietMode(bool quiet_mode
) {
265 SetQuietModeInternal(quiet_mode
);
266 quiet_mode_timer_
.reset();
269 void NotificationList::EnterQuietModeWithExpire(
270 const base::TimeDelta
& expires_in
) {
271 if (quiet_mode_timer_
.get()) {
272 // Note that the capital Reset() is the method to restart the timer, not
273 // scoped_ptr::reset().
274 quiet_mode_timer_
->Reset();
276 SetQuietModeInternal(true);
277 quiet_mode_timer_
.reset(new base::OneShotTimer
<NotificationList
>);
278 quiet_mode_timer_
->Start(FROM_HERE
, expires_in
, base::Bind(
279 &NotificationList::SetQuietMode
, base::Unretained(this), false));
283 void NotificationList::GetNotifications(
284 NotificationList::Notifications
* notifications
) const {
285 DCHECK(notifications
);
286 // Higher priority should come earlier.
287 for (NotificationMap::const_reverse_iterator mapiter
=
288 notifications_
.rbegin();
289 mapiter
!= notifications_
.rend(); ++mapiter
) {
290 for (Notifications::const_iterator iter
= mapiter
->second
.begin();
291 iter
!= mapiter
->second
.end(); ++iter
) {
292 notifications
->push_back(*iter
);
297 size_t NotificationList::NotificationCount() const {
299 for (NotificationMap::const_iterator mapiter
= notifications_
.begin();
300 mapiter
!= notifications_
.end(); ++mapiter
) {
301 result
+= mapiter
->second
.size();
306 void NotificationList::SetQuietModeInternal(bool quiet_mode
) {
307 quiet_mode_
= quiet_mode
;
309 for (NotificationMap::iterator mapiter
= notifications_
.begin();
310 mapiter
!= notifications_
.end(); ++mapiter
) {
311 for (Notifications::iterator iter
= mapiter
->second
.begin();
312 iter
!= mapiter
->second
.end(); ++iter
) {
313 iter
->is_read
= true;
314 iter
->shown_as_popup
= true;
319 delegate_
->OnQuietModeChanged(quiet_mode
);
322 bool NotificationList::GetNotification(
323 const std::string
& id
, Notifications::iterator
* iter
) {
324 for (NotificationMap::iterator mapiter
= notifications_
.begin();
325 mapiter
!= notifications_
.end(); ++mapiter
) {
326 for (Notifications::iterator curiter
= mapiter
->second
.begin();
327 curiter
!= mapiter
->second
.end(); ++curiter
) {
328 if (curiter
->id
== id
) {
337 void NotificationList::EraseNotification(Notifications::iterator iter
) {
338 if (!message_center_visible_
&& !iter
->is_read
&&
339 iter
->priority
> ui::notifications::MIN_PRIORITY
) {
342 notifications_
[iter
->priority
].erase(iter
);
345 void NotificationList::PushNotification(Notification
& notification
) {
346 // Ensure that notification.id is unique by erasing any existing
347 // notification with the same id (shouldn't normally happen).
348 Notifications::iterator iter
;
349 if (GetNotification(notification
.id
, &iter
))
350 EraseNotification(iter
);
351 // Add the notification to the front (top) of the list and mark it
352 // unread and unshown.
353 if (!message_center_visible_
) {
355 // TODO(mukai): needs to distinguish if a notification is dismissed by
356 // the quiet mode or user operation.
357 notification
.is_read
= true;
358 notification
.shown_as_popup
= true;
360 if (notification
.priority
> ui::notifications::MIN_PRIORITY
)
362 notification
.is_read
= false;
363 notification
.shown_as_popup
= false;
366 notifications_
[notification
.priority
].push_front(notification
);
369 void NotificationList::GetPopupIterators(int priority
,
370 Notifications::iterator
& first
,
371 Notifications::iterator
& last
) {
372 Notifications
& notifications
= notifications_
[priority
];
373 // No popups for LOW/MIN priority.
374 if (priority
< ui::notifications::DEFAULT_PRIORITY
) {
375 first
= notifications
.end();
376 last
= notifications
.end();
380 size_t popup_count
= 0;
381 first
= notifications
.begin();
383 while (last
!= notifications
.end()) {
384 if (last
->shown_as_popup
)
388 // No limits for HIGH/MAX priority.
389 if (priority
== ui::notifications::DEFAULT_PRIORITY
&&
390 popup_count
>= kMaxVisiblePopupNotifications
) {
396 } // namespace message_center