1 // Copyright (c) 2013 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/message_center_impl.h"
9 #include "base/memory/scoped_vector.h"
10 #include "base/observer_list.h"
11 #include "ui/message_center/message_center_style.h"
12 #include "ui/message_center/message_center_types.h"
13 #include "ui/message_center/notification.h"
14 #include "ui/message_center/notification_blocker.h"
15 #include "ui/message_center/notification_list.h"
16 #include "ui/message_center/notification_types.h"
20 base::TimeDelta GetTimeoutForPriority(int priority) {
21 if (priority > message_center::DEFAULT_PRIORITY) {
22 return base::TimeDelta::FromSeconds(
23 message_center::kAutocloseHighPriorityDelaySeconds);
25 return base::TimeDelta::FromSeconds(
26 message_center::kAutocloseDefaultDelaySeconds);
31 namespace message_center {
34 // ChangeQueue keeps track of all the changes that we need to make to the
35 // notification list once the visibility is set to VISIBILITY_TRANSIENT.
44 // Change represents an operation made on a notification. Since it contains
45 // the final state of the notification, we only keep the last change for a
46 // particular notification that is in the notification list around. There are
47 // two ids; |id_| is the newest notification id that has been assigned by an
48 // update, and |notification_list_id_| is the id of the notification it should
49 // be updating as it exists in the notification list.
52 Change(ChangeType type,
53 const std::string& id,
54 scoped_ptr<Notification> notification);
57 // Used to transfer ownership of the contained notification.
58 scoped_ptr<Notification> PassNotification();
60 Notification* notification() const { return notification_.get(); }
61 const std::string& id() const { return id_; }
62 ChangeType type() const { return type_; }
63 bool by_user() const { return by_user_; }
64 void set_by_user(bool by_user) { by_user_ = by_user; }
65 const std::string& notification_list_id() const {
66 return notification_list_id_;
68 void set_notification_list_id(const std::string& id) {
69 notification_list_id_ = id;
73 const ChangeType type_;
74 const std::string id_;
75 std::string notification_list_id_;
77 scoped_ptr<Notification> notification_;
79 DISALLOW_COPY_AND_ASSIGN(Change);
85 // Called when the message center has appropriate visibility. Modifies
86 // |message_center| but does not retain it. This also causes the queue to
88 void ApplyChanges(MessageCenter* message_center);
90 // Causes a TYPE_ADD change to be added to the queue.
91 void AddNotification(scoped_ptr<Notification> notification);
93 // Causes a TYPE_UPDATE change to be added to the queue.
94 void UpdateNotification(const std::string& old_id,
95 scoped_ptr<Notification> notification);
97 // Causes a TYPE_DELETE change to be added to the queue.
98 void EraseNotification(const std::string& id, bool by_user);
100 // Returns whether the queue matches an id. The id given will be matched
101 // against the ID of all changes post-update, not the id of the notification
102 // as it stands in the notification list.
103 bool Has(const std::string& id) const;
105 // Returns a Change that can be modified by the caller. ChangeQueue retains
106 // ownership of the Change; pointers should not be retained.
107 Notification* GetLatestNotification(const std::string& id) const;
110 void Replace(const std::string& id, scoped_ptr<Change> change);
112 ScopedVector<Change> changes_;
115 ////////////////////////////////////////////////////////////////////////////////
118 struct ChangeFinder {
119 explicit ChangeFinder(const std::string& id) : id(id) {}
120 bool operator()(ChangeQueue::Change* change) { return change->id() == id; }
125 ////////////////////////////////////////////////////////////////////////////////
126 // ChangeQueue::Change
128 ChangeQueue::Change::Change(ChangeType type,
129 const std::string& id,
130 scoped_ptr<Notification> notification)
133 notification_list_id_(id),
135 notification_(notification.Pass()) {
136 DCHECK(!id.empty() &&
137 (type != CHANGE_TYPE_DELETE || notification_.get() == NULL));
140 ChangeQueue::Change::~Change() {}
142 scoped_ptr<Notification> ChangeQueue::Change::PassNotification() {
143 return notification_.Pass();
146 ////////////////////////////////////////////////////////////////////////////////
149 ChangeQueue::ChangeQueue() {}
151 ChangeQueue::~ChangeQueue() {}
153 void ChangeQueue::ApplyChanges(MessageCenter* message_center) {
154 ScopedVector<Change>::const_iterator iter = changes_.begin();
155 for (; iter != changes_.end(); ++iter) {
156 Change* change = *iter;
157 // |message_center| is taking ownership of each element here.
158 switch (change->type()) {
159 case CHANGE_TYPE_ADD:
160 message_center->AddNotification(change->PassNotification());
162 case CHANGE_TYPE_UPDATE:
163 message_center->UpdateNotification(change->notification_list_id(),
164 change->PassNotification());
166 case CHANGE_TYPE_DELETE:
167 message_center->RemoveNotification(change->notification_list_id(),
178 void ChangeQueue::AddNotification(scoped_ptr<Notification> notification) {
179 std::string id = notification->id();
180 scoped_ptr<Change> change(
181 new Change(CHANGE_TYPE_ADD, id, notification.Pass()));
182 Replace(id, change.Pass());
185 void ChangeQueue::UpdateNotification(const std::string& old_id,
186 scoped_ptr<Notification> notification) {
187 std::string new_id = notification->id();
188 scoped_ptr<Change> change(
189 new Change(CHANGE_TYPE_UPDATE, new_id, notification.Pass()));
190 Replace(old_id, change.Pass());
193 void ChangeQueue::EraseNotification(const std::string& id, bool by_user) {
194 scoped_ptr<Change> change(
195 new Change(CHANGE_TYPE_DELETE, id, scoped_ptr<Notification>()));
196 change->set_by_user(by_user);
197 Replace(id, change.Pass());
200 bool ChangeQueue::Has(const std::string& id) const {
201 ScopedVector<Change>::const_iterator iter =
202 std::find_if(changes_.begin(), changes_.end(), ChangeFinder(id));
203 return iter != changes_.end();
206 Notification* ChangeQueue::GetLatestNotification(const std::string& id) const {
207 ScopedVector<Change>::const_iterator iter =
208 std::find_if(changes_.begin(), changes_.end(), ChangeFinder(id));
209 if (iter == changes_.end())
212 return (*iter)->notification();
215 void ChangeQueue::Replace(const std::string& changed_id,
216 scoped_ptr<Change> new_change) {
217 ScopedVector<Change>::iterator iter =
218 std::find_if(changes_.begin(), changes_.end(), ChangeFinder(changed_id));
219 if (iter != changes_.end()) {
220 Change* old_change = *iter;
221 new_change->set_notification_list_id(old_change->notification_list_id());
222 changes_.erase(iter);
224 new_change->set_notification_list_id(changed_id);
227 changes_.push_back(new_change.release());
230 ////////////////////////////////////////////////////////////////////////////////
233 PopupTimer::PopupTimer(const std::string& id,
234 base::TimeDelta timeout,
235 base::WeakPtr<PopupTimersController> controller)
238 timer_controller_(controller),
239 timer_(new base::OneShotTimer<PopupTimersController>) {}
241 PopupTimer::~PopupTimer() {
245 if (timer_->IsRunning())
249 void PopupTimer::Start() {
250 if (timer_->IsRunning())
252 base::TimeDelta timeout_to_close =
253 timeout_ <= passed_ ? base::TimeDelta() : timeout_ - passed_;
254 start_time_ = base::Time::Now();
259 &PopupTimersController::TimerFinished, timer_controller_, id_));
262 void PopupTimer::Pause() {
263 if (!timer_.get() || !timer_->IsRunning())
267 passed_ += base::Time::Now() - start_time_;
270 void PopupTimer::Reset() {
273 passed_ = base::TimeDelta();
276 ////////////////////////////////////////////////////////////////////////////////
277 // PopupTimersController
279 PopupTimersController::PopupTimersController(MessageCenter* message_center)
280 : message_center_(message_center), popup_deleter_(&popup_timers_) {
281 message_center_->AddObserver(this);
284 PopupTimersController::~PopupTimersController() {
285 message_center_->RemoveObserver(this);
288 void PopupTimersController::StartTimer(const std::string& id,
289 const base::TimeDelta& timeout) {
290 PopupTimerCollection::iterator iter = popup_timers_.find(id);
291 if (iter != popup_timers_.end()) {
292 DCHECK(iter->second);
293 iter->second->Start();
297 PopupTimer* timer = new PopupTimer(id, timeout, AsWeakPtr());
300 popup_timers_[id] = timer;
303 void PopupTimersController::StartAll() {
304 std::map<std::string, PopupTimer*>::iterator iter;
305 for (iter = popup_timers_.begin(); iter != popup_timers_.end(); iter++) {
306 iter->second->Start();
310 void PopupTimersController::ResetTimer(const std::string& id,
311 const base::TimeDelta& timeout) {
313 StartTimer(id, timeout);
316 void PopupTimersController::PauseTimer(const std::string& id) {
317 PopupTimerCollection::iterator iter = popup_timers_.find(id);
318 if (iter == popup_timers_.end())
320 iter->second->Pause();
323 void PopupTimersController::PauseAll() {
324 std::map<std::string, PopupTimer*>::iterator iter;
325 for (iter = popup_timers_.begin(); iter != popup_timers_.end(); iter++) {
326 iter->second->Pause();
330 void PopupTimersController::CancelTimer(const std::string& id) {
331 PopupTimerCollection::iterator iter = popup_timers_.find(id);
332 if (iter == popup_timers_.end())
335 PopupTimer* timer = iter->second;
338 popup_timers_.erase(iter);
341 void PopupTimersController::CancelAll() {
342 STLDeleteValues(&popup_timers_);
343 popup_timers_.clear();
346 void PopupTimersController::TimerFinished(const std::string& id) {
347 PopupTimerCollection::iterator iter = popup_timers_.find(id);
348 if (iter == popup_timers_.end())
352 message_center_->MarkSinglePopupAsShown(id, false);
355 void PopupTimersController::OnNotificationDisplayed(const std::string& id) {
356 OnNotificationUpdated(id);
359 void PopupTimersController::OnNotificationUpdated(const std::string& id) {
360 NotificationList::PopupNotifications popup_notifications =
361 message_center_->GetPopupNotifications();
363 if (!popup_notifications.size()) {
368 NotificationList::PopupNotifications::const_iterator iter =
369 popup_notifications.begin();
370 for (; iter != popup_notifications.end(); iter++) {
371 if ((*iter)->id() == id)
375 if (iter == popup_notifications.end() || (*iter)->never_timeout()) {
380 // Start the timer if not yet.
381 if (popup_timers_.find(id) == popup_timers_.end())
382 StartTimer(id, GetTimeoutForPriority((*iter)->priority()));
385 void PopupTimersController::OnNotificationRemoved(const std::string& id,
390 } // namespace internal
392 ////////////////////////////////////////////////////////////////////////////////
395 MessageCenterImpl::MessageCenterImpl()
397 popup_timers_controller_(new internal::PopupTimersController(this)),
398 settings_provider_(NULL) {
399 notification_list_.reset(new NotificationList());
400 notification_queue_.reset(new internal::ChangeQueue());
403 MessageCenterImpl::~MessageCenterImpl() {}
405 void MessageCenterImpl::AddObserver(MessageCenterObserver* observer) {
406 observer_list_.AddObserver(observer);
409 void MessageCenterImpl::RemoveObserver(MessageCenterObserver* observer) {
410 observer_list_.RemoveObserver(observer);
413 void MessageCenterImpl::AddNotificationBlocker(NotificationBlocker* blocker) {
414 if (std::find(blockers_.begin(), blockers_.end(), blocker) !=
418 blocker->AddObserver(this);
419 blockers_.push_back(blocker);
422 void MessageCenterImpl::RemoveNotificationBlocker(
423 NotificationBlocker* blocker) {
424 std::vector<NotificationBlocker*>::iterator iter =
425 std::find(blockers_.begin(), blockers_.end(), blocker);
426 if (iter == blockers_.end())
428 blocker->RemoveObserver(this);
429 blockers_.erase(iter);
432 void MessageCenterImpl::OnBlockingStateChanged() {
433 std::list<std::string> blocked_ids;
434 NotificationList::PopupNotifications popups =
435 notification_list_->GetPopupNotifications(blockers_, &blocked_ids);
437 for (std::list<std::string>::const_iterator iter = blocked_ids.begin();
438 iter != blocked_ids.end(); ++iter) {
439 MarkSinglePopupAsShown((*iter), true);
443 void MessageCenterImpl::SetVisibility(Visibility visibility) {
444 std::set<std::string> updated_ids;
445 notification_list_->SetMessageCenterVisible(
446 (visibility == VISIBILITY_MESSAGE_CENTER), &updated_ids);
448 for (std::set<std::string>::const_iterator iter = updated_ids.begin();
449 iter != updated_ids.end();
452 MessageCenterObserver, observer_list_, OnNotificationUpdated(*iter));
455 if (visibility == VISIBILITY_TRANSIENT)
456 notification_queue_->ApplyChanges(this);
458 FOR_EACH_OBSERVER(MessageCenterObserver,
460 OnCenterVisibilityChanged(visibility));
463 bool MessageCenterImpl::IsMessageCenterVisible() {
464 return notification_list_->is_message_center_visible();
467 size_t MessageCenterImpl::NotificationCount() const {
468 return notification_list_->NotificationCount();
471 size_t MessageCenterImpl::UnreadNotificationCount() const {
472 return notification_list_->unread_count();
475 bool MessageCenterImpl::HasPopupNotifications() const {
476 return notification_list_->HasPopupNotifications(blockers_);
479 bool MessageCenterImpl::HasNotification(const std::string& id) {
480 return notification_list_->HasNotification(id);
483 bool MessageCenterImpl::IsQuietMode() const {
484 return notification_list_->quiet_mode();
487 bool MessageCenterImpl::HasClickedListener(const std::string& id) {
488 NotificationDelegate* delegate =
489 notification_list_->GetNotificationDelegate(id);
490 return delegate && delegate->HasClickedListener();
493 const NotificationList::Notifications&
494 MessageCenterImpl::GetVisibleNotifications() {
495 return notification_list_->GetNotifications();
498 NotificationList::PopupNotifications
499 MessageCenterImpl::GetPopupNotifications() {
500 return notification_list_->GetPopupNotifications(blockers_, NULL);
503 //------------------------------------------------------------------------------
504 // Client code interface.
505 void MessageCenterImpl::AddNotification(scoped_ptr<Notification> notification) {
506 DCHECK(notification.get());
508 for (size_t i = 0; i < blockers_.size(); ++i)
509 blockers_[i]->CheckState();
511 if (notification_list_->is_message_center_visible()) {
512 notification_queue_->AddNotification(notification.Pass());
516 // Sometimes the notification can be added with the same id and the
517 // |notification_list| will replace the notification instead of adding new.
518 // This is essentially an update rather than addition.
519 const std::string& id = notification->id();
520 bool already_exists = notification_list_->HasNotification(id);
521 notification_list_->AddNotification(notification.Pass());
523 if (already_exists) {
525 MessageCenterObserver, observer_list_, OnNotificationUpdated(id));
528 MessageCenterObserver, observer_list_, OnNotificationAdded(id));
532 void MessageCenterImpl::UpdateNotification(
533 const std::string& old_id,
534 scoped_ptr<Notification> new_notification) {
535 for (size_t i = 0; i < blockers_.size(); ++i)
536 blockers_[i]->CheckState();
538 if (notification_list_->is_message_center_visible()) {
539 // We will allow notifications that are progress types (and stay progress
540 // types) to be updated even if the message center is open. There are 3
541 // requirements here:
542 // * Notification of type PROGRESS exists with same ID in the center
543 // * There are no queued updates for this notification (they imply a change
544 // that violates the PROGRESS invariant
545 // * The new notification is type PROGRESS.
546 // TODO(dewittj): Ensure this works when the ID is changed by the caller.
547 // This shouldn't be an issue in practice since only W3C notifications
548 // change the ID on update, and they don't have progress type notifications.
549 bool update_keeps_progress_type =
550 new_notification->type() == NOTIFICATION_TYPE_PROGRESS &&
551 !notification_queue_->Has(old_id) &&
552 notification_list_->HasNotificationOfType(old_id,
553 NOTIFICATION_TYPE_PROGRESS);
554 if (!update_keeps_progress_type) {
555 // Updates are allowed only for progress notifications.
556 notification_queue_->UpdateNotification(old_id, new_notification.Pass());
561 std::string new_id = new_notification->id();
562 notification_list_->UpdateNotificationMessage(old_id,
563 new_notification.Pass());
564 if (old_id == new_id) {
566 MessageCenterObserver, observer_list_, OnNotificationUpdated(new_id));
568 FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_,
569 OnNotificationRemoved(old_id, false));
570 FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_,
571 OnNotificationAdded(new_id));
575 void MessageCenterImpl::RemoveNotification(const std::string& id,
577 if (!by_user && notification_list_->is_message_center_visible()) {
578 notification_queue_->EraseNotification(id, by_user);
582 if (!HasNotification(id))
585 NotificationDelegate* delegate =
586 notification_list_->GetNotificationDelegate(id);
588 delegate->Close(by_user);
590 // In many cases |id| is a reference to an existing notification instance
591 // but the instance can be destructed in RemoveNotification(). Hence
592 // copies the id explicitly here.
593 std::string copied_id(id);
594 notification_list_->RemoveNotification(copied_id);
595 FOR_EACH_OBSERVER(MessageCenterObserver,
597 OnNotificationRemoved(copied_id, by_user));
600 void MessageCenterImpl::RemoveAllNotifications(bool by_user) {
601 const NotificationList::Notifications& notifications =
602 notification_list_->GetNotifications();
603 std::set<std::string> ids;
604 for (NotificationList::Notifications::const_iterator iter =
605 notifications.begin(); iter != notifications.end(); ++iter) {
606 ids.insert((*iter)->id());
607 NotificationDelegate* delegate = (*iter)->delegate();
609 delegate->Close(by_user);
611 notification_list_->RemoveAllNotifications();
613 for (std::set<std::string>::const_iterator iter = ids.begin();
614 iter != ids.end(); ++iter) {
615 FOR_EACH_OBSERVER(MessageCenterObserver,
617 OnNotificationRemoved(*iter, by_user));
621 void MessageCenterImpl::SetNotificationIcon(const std::string& notification_id,
622 const gfx::Image& image) {
623 bool updated = false;
624 Notification* queue_notification = notification_queue_->GetLatestNotification(
627 if (queue_notification) {
628 queue_notification->set_icon(image);
631 updated = notification_list_->SetNotificationIcon(notification_id, image);
635 FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_,
636 OnNotificationUpdated(notification_id));
640 void MessageCenterImpl::SetNotificationImage(const std::string& notification_id,
641 const gfx::Image& image) {
642 bool updated = false;
643 Notification* queue_notification = notification_queue_->GetLatestNotification(
646 if (queue_notification) {
647 queue_notification->set_image(image);
650 updated = notification_list_->SetNotificationImage(notification_id, image);
654 FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_,
655 OnNotificationUpdated(notification_id));
659 void MessageCenterImpl::SetNotificationButtonIcon(
660 const std::string& notification_id, int button_index,
661 const gfx::Image& image) {
662 bool updated = false;
663 Notification* queue_notification = notification_queue_->GetLatestNotification(
666 if (queue_notification) {
667 queue_notification->SetButtonIcon(button_index, image);
670 updated = notification_list_->SetNotificationButtonIcon(
671 notification_id, button_index, image);
675 FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_,
676 OnNotificationUpdated(notification_id));
680 void MessageCenterImpl::DisableNotificationsByNotifier(
681 const NotifierId& notifier_id) {
682 if (settings_provider_) {
683 // TODO(mukai): SetNotifierEnabled can just accept notifier_id?
684 Notifier notifier(notifier_id, base::string16(), true);
685 settings_provider_->SetNotifierEnabled(notifier, false);
688 NotificationList::Notifications notifications =
689 notification_list_->GetNotificationsByNotifierId(notifier_id);
690 for (NotificationList::Notifications::const_iterator iter =
691 notifications.begin(); iter != notifications.end();) {
692 std::string id = (*iter)->id();
694 RemoveNotification(id, false);
698 void MessageCenterImpl::ExpandNotification(const std::string& id) {
699 if (!HasNotification(id))
701 notification_list_->MarkNotificationAsExpanded(id);
702 FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_,
703 OnNotificationUpdated(id));
706 void MessageCenterImpl::ClickOnNotification(const std::string& id) {
707 if (!HasNotification(id))
709 if (HasPopupNotifications())
710 MarkSinglePopupAsShown(id, true);
711 NotificationDelegate* delegate =
712 notification_list_->GetNotificationDelegate(id);
716 MessageCenterObserver, observer_list_, OnNotificationClicked(id));
719 void MessageCenterImpl::ClickOnNotificationButton(const std::string& id,
721 if (!HasNotification(id))
723 if (HasPopupNotifications())
724 MarkSinglePopupAsShown(id, true);
725 NotificationDelegate* delegate =
726 notification_list_->GetNotificationDelegate(id);
728 delegate->ButtonClick(button_index);
730 MessageCenterObserver, observer_list_, OnNotificationButtonClicked(
734 void MessageCenterImpl::MarkSinglePopupAsShown(const std::string& id,
735 bool mark_notification_as_read) {
736 if (!HasNotification(id))
738 notification_list_->MarkSinglePopupAsShown(id, mark_notification_as_read);
740 MessageCenterObserver, observer_list_, OnNotificationUpdated(id));
743 void MessageCenterImpl::DisplayedNotification(const std::string& id) {
744 if (!HasNotification(id))
747 if (HasPopupNotifications())
748 notification_list_->MarkSinglePopupAsDisplayed(id);
749 NotificationDelegate* delegate =
750 notification_list_->GetNotificationDelegate(id);
754 MessageCenterObserver, observer_list_, OnNotificationDisplayed(id));
757 void MessageCenterImpl::SetNotifierSettingsProvider(
758 NotifierSettingsProvider* provider) {
759 settings_provider_ = provider;
762 NotifierSettingsProvider* MessageCenterImpl::GetNotifierSettingsProvider() {
763 return settings_provider_;
766 void MessageCenterImpl::SetQuietMode(bool in_quiet_mode) {
767 if (in_quiet_mode != notification_list_->quiet_mode()) {
768 notification_list_->SetQuietMode(in_quiet_mode);
769 FOR_EACH_OBSERVER(MessageCenterObserver,
771 OnQuietModeChanged(in_quiet_mode));
773 quiet_mode_timer_.reset();
776 void MessageCenterImpl::EnterQuietModeWithExpire(
777 const base::TimeDelta& expires_in) {
778 if (quiet_mode_timer_.get()) {
779 // Note that the capital Reset() is the method to restart the timer, not
780 // scoped_ptr::reset().
781 quiet_mode_timer_->Reset();
783 notification_list_->SetQuietMode(true);
785 MessageCenterObserver, observer_list_, OnQuietModeChanged(true));
787 quiet_mode_timer_.reset(new base::OneShotTimer<MessageCenterImpl>);
788 quiet_mode_timer_->Start(
792 &MessageCenterImpl::SetQuietMode, base::Unretained(this), false));
796 void MessageCenterImpl::RestartPopupTimers() {
797 if (popup_timers_controller_.get())
798 popup_timers_controller_->StartAll();
801 void MessageCenterImpl::PausePopupTimers() {
802 if (popup_timers_controller_.get())
803 popup_timers_controller_->PauseAll();
806 void MessageCenterImpl::DisableTimersForTest() {
807 popup_timers_controller_.reset();
810 } // namespace message_center