| // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/message_center/message_center_impl.h" |
| |
| #include <algorithm> |
| #include <deque> |
| #include <utility> |
| |
| #include "base/command_line.h" |
| #include "base/macros.h" |
| #include "base/memory/scoped_vector.h" |
| #include "base/observer_list.h" |
| #include "base/stl_util.h" |
| #include "build/build_config.h" |
| #include "ui/message_center/message_center_style.h" |
| #include "ui/message_center/message_center_switches.h" |
| #include "ui/message_center/message_center_types.h" |
| #include "ui/message_center/notification.h" |
| #include "ui/message_center/notification_blocker.h" |
| #include "ui/message_center/notification_list.h" |
| #include "ui/message_center/notification_types.h" |
| #include "ui/message_center/popup_timers_controller.h" |
| |
| namespace message_center { |
| namespace internal { |
| |
| // ChangeQueue keeps track of all the changes that we need to make to the |
| // notification list once the visibility is set to VISIBILITY_TRANSIENT. |
| class ChangeQueue { |
| public: |
| enum ChangeType { |
| CHANGE_TYPE_ADD = 0, |
| CHANGE_TYPE_UPDATE, |
| CHANGE_TYPE_DELETE |
| }; |
| |
| // Change represents an operation made on a notification. Since it contains |
| // the final state of the notification, we only keep the last change for a |
| // particular notification that is in the notification list around. There are |
| // two ids; |id_| is the newest notification id that has been assigned by an |
| // update, and |notification_list_id_| is the id of the notification it should |
| // be updating as it exists in the notification list. |
| class Change { |
| public: |
| Change(ChangeType type, |
| const std::string& id, |
| std::unique_ptr<Notification> notification); |
| ~Change(); |
| |
| // Used to transfer ownership of the contained notification. |
| std::unique_ptr<Notification> PassNotification(); |
| |
| Notification* notification() const { return notification_.get(); } |
| // Returns the post-update ID. It means: |
| // - ADD event: ID of the notification to be added. |
| // - UPDATE event: ID of the notification after the change. If the change |
| // doesn't update its ID, this value is same as |notification_list_id|. |
| // - DELETE event: ID of the notification to be deleted. |
| const std::string& id() const { return id_; } |
| ChangeType type() const { return type_; } |
| bool by_user() const { return by_user_; } |
| void set_by_user(bool by_user) { by_user_ = by_user; } |
| // Returns the ID which is used in the notification list. In other word, it |
| // means the ID before the change. |
| const std::string& notification_list_id() const { |
| return notification_list_id_; |
| } |
| void set_type(const ChangeType new_type) { |
| type_ = new_type; |
| } |
| void ReplaceNotification(std::unique_ptr<Notification> new_notification); |
| |
| private: |
| ChangeType type_; |
| std::string id_; |
| std::string notification_list_id_; |
| bool by_user_; |
| std::unique_ptr<Notification> notification_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Change); |
| }; |
| |
| ChangeQueue(); |
| ~ChangeQueue(); |
| |
| // Called when the message center has appropriate visibility. Modifies |
| // |message_center| but does not retain it. This also causes the queue to |
| // empty itself. |
| void ApplyChanges(MessageCenterImpl* message_center); |
| |
| // Applies only the changes of the given ID. |
| void ApplyChangesForId(MessageCenterImpl* message_center, |
| const std::string& id); |
| |
| // Causes a TYPE_ADD change to be added to the queue. |
| void AddNotification(std::unique_ptr<Notification> notification); |
| |
| // Causes a TYPE_UPDATE change to be added to the queue. |
| void UpdateNotification(const std::string& old_id, |
| std::unique_ptr<Notification> notification); |
| |
| // Causes a TYPE_DELETE change to be added to the queue. |
| void EraseNotification(const std::string& id, bool by_user); |
| |
| // Returns whether the queue matches an id. The id given will be matched |
| // against the ID of all changes post-update, not the id of the notification |
| // as it stands in the notification list. |
| bool Has(const std::string& id) const; |
| |
| // Returns a Change that can be modified by the caller. ChangeQueue retains |
| // ownership of the Change; pointers should not be retained. |
| Notification* GetLatestNotification(const std::string& id) const; |
| |
| private: |
| void ApplyChangeInternal(MessageCenterImpl* message_center, |
| std::unique_ptr<Change> change); |
| |
| ScopedVector<Change> changes_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ChangeQueue); |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ChangeFinder |
| |
| struct ChangeFinder { |
| explicit ChangeFinder(const std::string& id) : id(id) {} |
| bool operator()(ChangeQueue::Change* change) { return change->id() == id; } |
| |
| std::string id; |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ChangeQueue::Change |
| |
| ChangeQueue::Change::Change(ChangeType type, |
| const std::string& id, |
| std::unique_ptr<Notification> notification) |
| : type_(type), |
| notification_list_id_(id), |
| by_user_(false), |
| notification_(std::move(notification)) { |
| DCHECK(!id.empty()); |
| DCHECK(type != CHANGE_TYPE_DELETE || !notification_); |
| |
| id_ = notification_ ? notification_->id() : notification_list_id_; |
| } |
| |
| ChangeQueue::Change::~Change() {} |
| |
| std::unique_ptr<Notification> ChangeQueue::Change::PassNotification() { |
| return std::move(notification_); |
| } |
| |
| void ChangeQueue::Change::ReplaceNotification( |
| std::unique_ptr<Notification> new_notification) { |
| id_ = new_notification ? new_notification->id() : notification_list_id_; |
| notification_.swap(new_notification); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ChangeQueue |
| |
| ChangeQueue::ChangeQueue() {} |
| |
| ChangeQueue::~ChangeQueue() {} |
| |
| void ChangeQueue::ApplyChanges(MessageCenterImpl* message_center) { |
| // This method is re-entrant. |
| while (!changes_.empty()) { |
| ScopedVector<Change>::iterator iter = changes_.begin(); |
| std::unique_ptr<Change> change(*iter); |
| // TODO(dewittj): Replace changes_ with a deque. |
| changes_.weak_erase(iter); |
| ApplyChangeInternal(message_center, std::move(change)); |
| } |
| } |
| |
| void ChangeQueue::ApplyChangesForId(MessageCenterImpl* message_center, |
| const std::string& id) { |
| std::deque<Change*> changes_for_id; |
| std::string interesting_id = id; |
| |
| // Traverses the queue in reverse so shat we can track changes which change |
| // the notification's ID. |
| ScopedVector<Change>::iterator iter = changes_.end(); |
| while (iter != changes_.begin()) { |
| --iter; |
| if (interesting_id != (*iter)->id()) |
| continue; |
| std::unique_ptr<Change> change(*iter); |
| |
| interesting_id = change->notification_list_id(); |
| |
| iter = changes_.weak_erase(iter); |
| changes_for_id.push_back(change.release()); |
| } |
| |
| while (!changes_for_id.empty()) { |
| ApplyChangeInternal(message_center, |
| std::unique_ptr<Change>(changes_for_id.back())); |
| changes_for_id.pop_back(); |
| } |
| } |
| |
| void ChangeQueue::AddNotification(std::unique_ptr<Notification> notification) { |
| std::string id = notification->id(); |
| changes_.push_back(new Change(CHANGE_TYPE_ADD, id, std::move(notification))); |
| } |
| |
| void ChangeQueue::UpdateNotification( |
| const std::string& old_id, |
| std::unique_ptr<Notification> notification) { |
| ScopedVector<Change>::reverse_iterator iter = |
| std::find_if(changes_.rbegin(), changes_.rend(), ChangeFinder(old_id)); |
| if (iter == changes_.rend()) { |
| changes_.push_back( |
| new Change(CHANGE_TYPE_UPDATE, old_id, std::move(notification))); |
| return; |
| } |
| |
| Change* change = *iter; |
| switch (change->type()) { |
| case CHANGE_TYPE_ADD: { |
| std::string id = notification->id(); |
| // Needs to add the change at the last, because if this change updates |
| // its ID, some previous changes may affect new ID. |
| // (eg. Add A, Update B->C, and This update A->B). |
| changes_.erase(--(iter.base())); |
| changes_.push_back( |
| new Change(CHANGE_TYPE_ADD, id, std::move(notification))); |
| break; |
| } |
| case CHANGE_TYPE_UPDATE: |
| if (notification->id() == old_id) { |
| // Safe to place the change at the previous place. |
| change->ReplaceNotification(std::move(notification)); |
| } else if (change->id() == change->notification_list_id()) { |
| std::string id = notification->id(); |
| // Safe to place the change at the last. |
| changes_.erase(--(iter.base())); |
| changes_.push_back( |
| new Change(CHANGE_TYPE_ADD, id, std::move(notification))); |
| } else { |
| // Complex case: gives up to optimize. |
| changes_.push_back( |
| new Change(CHANGE_TYPE_UPDATE, old_id, std::move(notification))); |
| } |
| break; |
| case CHANGE_TYPE_DELETE: |
| // DELETE -> UPDATE. Something is wrong. Treats the UPDATE as ADD. |
| changes_.push_back( |
| new Change(CHANGE_TYPE_ADD, old_id, std::move(notification))); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| void ChangeQueue::EraseNotification(const std::string& id, bool by_user) { |
| ScopedVector<Change>::reverse_iterator iter = |
| std::find_if(changes_.rbegin(), changes_.rend(), ChangeFinder(id)); |
| if (iter == changes_.rend()) { |
| std::unique_ptr<Change> change(new Change(CHANGE_TYPE_DELETE, id, nullptr)); |
| change->set_by_user(by_user); |
| changes_.push_back(std::move(change)); |
| return; |
| } |
| |
| Change* change = *iter; |
| switch (change->type()) { |
| case CHANGE_TYPE_ADD: |
| // ADD -> DELETE. Just removes both. |
| changes_.erase(--(iter.base())); |
| break; |
| case CHANGE_TYPE_UPDATE: |
| // UPDATE -> DELETE. Changes the previous UPDATE to DELETE. |
| change->set_type(CHANGE_TYPE_DELETE); |
| change->set_by_user(by_user); |
| change->ReplaceNotification(nullptr); |
| break; |
| case CHANGE_TYPE_DELETE: |
| // DELETE -> DELETE. Something is wrong. Combines them with overriding |
| // the |by_user| flag. |
| change->set_by_user(!change->by_user() && by_user); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| bool ChangeQueue::Has(const std::string& id) const { |
| ScopedVector<Change>::const_iterator iter = |
| std::find_if(changes_.begin(), changes_.end(), ChangeFinder(id)); |
| return iter != changes_.end(); |
| } |
| |
| Notification* ChangeQueue::GetLatestNotification(const std::string& id) const { |
| ScopedVector<Change>::const_iterator iter = |
| std::find_if(changes_.begin(), changes_.end(), ChangeFinder(id)); |
| if (iter == changes_.end()) |
| return NULL; |
| |
| return (*iter)->notification(); |
| } |
| |
| void ChangeQueue::ApplyChangeInternal(MessageCenterImpl* message_center, |
| std::unique_ptr<Change> change) { |
| switch (change->type()) { |
| case CHANGE_TYPE_ADD: |
| message_center->AddNotificationImmediately(change->PassNotification()); |
| break; |
| case CHANGE_TYPE_UPDATE: |
| message_center->UpdateNotificationImmediately( |
| change->notification_list_id(), change->PassNotification()); |
| break; |
| case CHANGE_TYPE_DELETE: |
| message_center->RemoveNotificationImmediately( |
| change->notification_list_id(), change->by_user()); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| } // namespace internal |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // MessageCenterImpl::NotificationCache |
| |
| MessageCenterImpl::NotificationCache::NotificationCache() |
| : unread_count(0) {} |
| |
| MessageCenterImpl::NotificationCache::~NotificationCache() {} |
| |
| void MessageCenterImpl::NotificationCache::Rebuild( |
| const NotificationList::Notifications& notifications) { |
| visible_notifications = notifications; |
| RecountUnread(); |
| } |
| |
| void MessageCenterImpl::NotificationCache::RecountUnread() { |
| unread_count = 0; |
| for (auto* notification : visible_notifications) { |
| if (!notification->IsRead()) |
| ++unread_count; |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // MessageCenterImpl |
| |
| MessageCenterImpl::MessageCenterImpl() |
| : MessageCenter(), |
| popup_timers_controller_(new PopupTimersController(this)), |
| settings_provider_(NULL) { |
| notification_list_.reset(new NotificationList(this)); |
| |
| bool enable_message_center_changes_while_open = true; // enable by default |
| std::string arg = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
| switches::kMessageCenterChangesWhileOpen); |
| if (!arg.empty()) { |
| if (arg == "enabled") |
| enable_message_center_changes_while_open = true; |
| else if (arg == "disabled") |
| enable_message_center_changes_while_open = false; |
| } |
| |
| if (!enable_message_center_changes_while_open) |
| notification_queue_.reset(new internal::ChangeQueue()); |
| } |
| |
| MessageCenterImpl::~MessageCenterImpl() { |
| SetNotifierSettingsProvider(NULL); |
| } |
| |
| void MessageCenterImpl::AddObserver(MessageCenterObserver* observer) { |
| observer_list_.AddObserver(observer); |
| } |
| |
| void MessageCenterImpl::RemoveObserver(MessageCenterObserver* observer) { |
| observer_list_.RemoveObserver(observer); |
| } |
| |
| void MessageCenterImpl::AddNotificationBlocker(NotificationBlocker* blocker) { |
| if (base::ContainsValue(blockers_, blocker)) |
| return; |
| |
| blocker->AddObserver(this); |
| blockers_.push_back(blocker); |
| } |
| |
| void MessageCenterImpl::RemoveNotificationBlocker( |
| NotificationBlocker* blocker) { |
| std::vector<NotificationBlocker*>::iterator iter = |
| std::find(blockers_.begin(), blockers_.end(), blocker); |
| if (iter == blockers_.end()) |
| return; |
| blocker->RemoveObserver(this); |
| blockers_.erase(iter); |
| } |
| |
| void MessageCenterImpl::OnBlockingStateChanged(NotificationBlocker* blocker) { |
| std::list<std::string> blocked_ids; |
| NotificationList::PopupNotifications popups = |
| notification_list_->GetPopupNotifications(blockers_, &blocked_ids); |
| |
| for (const auto& id : blocked_ids) { |
| // Do not call MessageCenterImpl::MarkSinglePopupAsShown() directly here |
| // just for performance reason. MessageCenterImpl::MarkSinglePopupAsShown() |
| // calls NotificationList::MarkSinglePopupAsShown() and then updates the |
| // unread count, but the whole cache will be recreated below. |
| notification_list_->MarkSinglePopupAsShown(id, true); |
| } |
| notification_cache_.Rebuild( |
| notification_list_->GetVisibleNotifications(blockers_)); |
| |
| for (const auto& id : blocked_ids) { |
| FOR_EACH_OBSERVER(MessageCenterObserver, |
| observer_list_, |
| OnNotificationUpdated(id)); |
| } |
| FOR_EACH_OBSERVER(MessageCenterObserver, |
| observer_list_, |
| OnBlockingStateChanged(blocker)); |
| } |
| |
| void MessageCenterImpl::UpdateIconImage( |
| const NotifierId& notifier_id, const gfx::Image& icon) {} |
| |
| void MessageCenterImpl::NotifierGroupChanged() {} |
| |
| void MessageCenterImpl::NotifierEnabledChanged( |
| const NotifierId& notifier_id, bool enabled) { |
| if (!enabled) { |
| RemoveNotificationsForNotifierId(notifier_id); |
| } |
| } |
| |
| void MessageCenterImpl::SetVisibility(Visibility visibility) { |
| visible_ = (visibility == VISIBILITY_MESSAGE_CENTER); |
| |
| if (visible_ && !locked_) { |
| std::set<std::string> updated_ids; |
| notification_list_->SetNotificationsShown(blockers_, &updated_ids); |
| notification_cache_.RecountUnread(); |
| |
| for (const auto& id : updated_ids) { |
| FOR_EACH_OBSERVER( |
| MessageCenterObserver, observer_list_, OnNotificationUpdated(id)); |
| } |
| } |
| |
| if (notification_queue_ && |
| visibility == VISIBILITY_TRANSIENT) { |
| notification_queue_->ApplyChanges(this); |
| } |
| |
| FOR_EACH_OBSERVER(MessageCenterObserver, |
| observer_list_, |
| OnCenterVisibilityChanged(visibility)); |
| } |
| |
| bool MessageCenterImpl::IsMessageCenterVisible() const { |
| return visible_; |
| } |
| |
| size_t MessageCenterImpl::NotificationCount() const { |
| return notification_cache_.visible_notifications.size(); |
| } |
| |
| size_t MessageCenterImpl::UnreadNotificationCount() const { |
| return notification_cache_.unread_count; |
| } |
| |
| bool MessageCenterImpl::HasPopupNotifications() const { |
| return !IsMessageCenterVisible() && |
| notification_list_->HasPopupNotifications(blockers_); |
| } |
| |
| bool MessageCenterImpl::IsQuietMode() const { |
| return notification_list_->quiet_mode(); |
| } |
| |
| bool MessageCenterImpl::IsLockedState() const { |
| return locked_; |
| } |
| |
| bool MessageCenterImpl::HasClickedListener(const std::string& id) { |
| scoped_refptr<NotificationDelegate> delegate = |
| notification_list_->GetNotificationDelegate(id); |
| return delegate.get() && delegate->HasClickedListener(); |
| } |
| |
| message_center::Notification* MessageCenterImpl::FindVisibleNotificationById( |
| const std::string& id) { |
| return notification_list_->GetNotificationById(id); |
| } |
| |
| const NotificationList::Notifications& |
| MessageCenterImpl::GetVisibleNotifications() { |
| return notification_cache_.visible_notifications; |
| } |
| |
| NotificationList::PopupNotifications |
| MessageCenterImpl::GetPopupNotifications() { |
| return notification_list_->GetPopupNotifications(blockers_, NULL); |
| } |
| |
| void MessageCenterImpl::ForceNotificationFlush(const std::string& id) { |
| if (notification_queue_) |
| notification_queue_->ApplyChangesForId(this, id); |
| } |
| |
| //------------------------------------------------------------------------------ |
| // Client code interface. |
| void MessageCenterImpl::AddNotification( |
| std::unique_ptr<Notification> notification) { |
| DCHECK(notification); |
| const std::string id = notification->id(); |
| for (size_t i = 0; i < blockers_.size(); ++i) |
| blockers_[i]->CheckState(); |
| |
| if (notification_queue_ && visible_) { |
| notification_queue_->AddNotification(std::move(notification)); |
| return; |
| } |
| |
| AddNotificationImmediately(std::move(notification)); |
| } |
| |
| void MessageCenterImpl::AddNotificationImmediately( |
| std::unique_ptr<Notification> notification) { |
| const std::string id = notification->id(); |
| |
| // Sometimes the notification can be added with the same id and the |
| // |notification_list| will replace the notification instead of adding new. |
| // This is essentially an update rather than addition. |
| bool already_exists = (notification_list_->GetNotificationById(id) != NULL); |
| notification_list_->AddNotification(std::move(notification)); |
| notification_cache_.Rebuild( |
| notification_list_->GetVisibleNotifications(blockers_)); |
| |
| if (already_exists) { |
| FOR_EACH_OBSERVER( |
| MessageCenterObserver, observer_list_, OnNotificationUpdated(id)); |
| } else { |
| FOR_EACH_OBSERVER( |
| MessageCenterObserver, observer_list_, OnNotificationAdded(id)); |
| } |
| } |
| |
| void MessageCenterImpl::UpdateNotification( |
| const std::string& old_id, |
| std::unique_ptr<Notification> new_notification) { |
| for (size_t i = 0; i < blockers_.size(); ++i) |
| blockers_[i]->CheckState(); |
| |
| if (notification_queue_ && visible_) { |
| // We will allow notifications that are progress types (and stay progress |
| // types) to be updated even if the message center is open. There are 3 |
| // requirements here: |
| // * Notification of type PROGRESS exists with same ID in the center |
| // * There are no queued updates for this notification (they imply a change |
| // that violates the PROGRESS invariant |
| // * The new notification is type PROGRESS. |
| // TODO(dewittj): Ensure this works when the ID is changed by the caller. |
| // This shouldn't be an issue in practice since only W3C notifications |
| // change the ID on update, and they don't have progress type notifications. |
| bool update_keeps_progress_type = |
| new_notification->type() == NOTIFICATION_TYPE_PROGRESS && |
| !notification_queue_->Has(old_id) && |
| notification_list_->HasNotificationOfType(old_id, |
| NOTIFICATION_TYPE_PROGRESS); |
| if (!update_keeps_progress_type) { |
| // Updates are allowed only for progress notifications. |
| notification_queue_->UpdateNotification(old_id, |
| std::move(new_notification)); |
| return; |
| } |
| } |
| |
| UpdateNotificationImmediately(old_id, std::move(new_notification)); |
| } |
| |
| void MessageCenterImpl::UpdateNotificationImmediately( |
| const std::string& old_id, |
| std::unique_ptr<Notification> new_notification) { |
| std::string new_id = new_notification->id(); |
| notification_list_->UpdateNotificationMessage(old_id, |
| std::move(new_notification)); |
| notification_cache_.Rebuild( |
| notification_list_->GetVisibleNotifications(blockers_)); |
| if (old_id == new_id) { |
| FOR_EACH_OBSERVER( |
| MessageCenterObserver, observer_list_, OnNotificationUpdated(new_id)); |
| } else { |
| FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_, |
| OnNotificationRemoved(old_id, false)); |
| FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_, |
| OnNotificationAdded(new_id)); |
| } |
| } |
| |
| void MessageCenterImpl::RemoveNotification(const std::string& id, |
| bool by_user) { |
| if (notification_queue_ && !by_user && visible_) { |
| notification_queue_->EraseNotification(id, by_user); |
| return; |
| } |
| |
| RemoveNotificationImmediately(id, by_user); |
| } |
| |
| void MessageCenterImpl::RemoveNotificationImmediately( |
| const std::string& id, bool by_user) { |
| Notification* notification = FindVisibleNotificationById(id); |
| if (notification == NULL) |
| return; |
| |
| if (by_user && notification->pinned()) |
| return; |
| |
| // In many cases |id| is a reference to an existing notification instance |
| // but the instance can be destructed in this method. Hence copies the id |
| // explicitly here. |
| std::string copied_id(id); |
| |
| scoped_refptr<NotificationDelegate> delegate = |
| notification_list_->GetNotificationDelegate(copied_id); |
| if (delegate.get()) |
| delegate->Close(by_user); |
| |
| notification_list_->RemoveNotification(copied_id); |
| notification_cache_.Rebuild( |
| notification_list_->GetVisibleNotifications(blockers_)); |
| FOR_EACH_OBSERVER(MessageCenterObserver, |
| observer_list_, |
| OnNotificationRemoved(copied_id, by_user)); |
| } |
| |
| void MessageCenterImpl::RemoveNotificationsForNotifierId( |
| const NotifierId& notifier_id) { |
| NotificationList::Notifications notifications = |
| notification_list_->GetNotificationsByNotifierId(notifier_id); |
| for (auto* notification : notifications) |
| RemoveNotification(notification->id(), false); |
| if (!notifications.empty()) { |
| notification_cache_.Rebuild( |
| notification_list_->GetVisibleNotifications(blockers_)); |
| } |
| } |
| |
| void MessageCenterImpl::RemoveAllNotifications(bool by_user, RemoveType type) { |
| bool remove_pinned = (type == RemoveType::ALL); |
| |
| const NotificationBlockers& blockers = |
| (type == RemoveType::ALL ? NotificationBlockers() /* empty blockers */ |
| : blockers_ /* use default blockers */); |
| |
| const NotificationList::Notifications notifications = |
| notification_list_->GetVisibleNotifications(blockers); |
| std::set<std::string> ids; |
| for (auto* notification : notifications) { |
| if (!remove_pinned && notification->pinned()) |
| continue; |
| |
| ids.insert(notification->id()); |
| scoped_refptr<NotificationDelegate> delegate = notification->delegate(); |
| if (delegate.get()) |
| delegate->Close(by_user); |
| notification_list_->RemoveNotification(notification->id()); |
| } |
| |
| if (!ids.empty()) { |
| notification_cache_.Rebuild( |
| notification_list_->GetVisibleNotifications(blockers_)); |
| } |
| for (const auto& id : ids) { |
| FOR_EACH_OBSERVER(MessageCenterObserver, |
| observer_list_, |
| OnNotificationRemoved(id, by_user)); |
| } |
| } |
| |
| void MessageCenterImpl::SetNotificationIcon(const std::string& notification_id, |
| const gfx::Image& image) { |
| bool updated = false; |
| Notification* queue_notification = |
| notification_queue_ |
| ? notification_queue_->GetLatestNotification(notification_id) |
| : NULL; |
| |
| if (queue_notification) { |
| queue_notification->set_icon(image); |
| updated = true; |
| } else { |
| updated = notification_list_->SetNotificationIcon(notification_id, image); |
| } |
| |
| if (updated) { |
| FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_, |
| OnNotificationUpdated(notification_id)); |
| } |
| } |
| |
| void MessageCenterImpl::SetNotificationImage(const std::string& notification_id, |
| const gfx::Image& image) { |
| bool updated = false; |
| Notification* queue_notification = |
| notification_queue_ |
| ? notification_queue_->GetLatestNotification(notification_id) |
| : NULL; |
| |
| if (queue_notification) { |
| queue_notification->set_image(image); |
| updated = true; |
| } else { |
| updated = notification_list_->SetNotificationImage(notification_id, image); |
| } |
| |
| if (updated) { |
| FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_, |
| OnNotificationUpdated(notification_id)); |
| } |
| } |
| |
| void MessageCenterImpl::SetNotificationButtonIcon( |
| const std::string& notification_id, int button_index, |
| const gfx::Image& image) { |
| bool updated = false; |
| Notification* queue_notification = |
| notification_queue_ |
| ? notification_queue_->GetLatestNotification(notification_id) |
| : NULL; |
| |
| if (queue_notification) { |
| queue_notification->SetButtonIcon(button_index, image); |
| updated = true; |
| } else { |
| updated = notification_list_->SetNotificationButtonIcon( |
| notification_id, button_index, image); |
| } |
| |
| if (updated) { |
| FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_, |
| OnNotificationUpdated(notification_id)); |
| } |
| } |
| |
| void MessageCenterImpl::DisableNotificationsByNotifier( |
| const NotifierId& notifier_id) { |
| if (settings_provider_) { |
| // TODO(mukai): SetNotifierEnabled can just accept notifier_id? |
| Notifier notifier(notifier_id, base::string16(), true); |
| settings_provider_->SetNotifierEnabled(notifier, false); |
| // The settings provider will call back to remove the notifications |
| // belonging to the notifier id. |
| } else { |
| RemoveNotificationsForNotifierId(notifier_id); |
| } |
| } |
| |
| void MessageCenterImpl::ClickOnNotification(const std::string& id) { |
| if (FindVisibleNotificationById(id) == NULL) |
| return; |
| #if defined(OS_CHROMEOS) |
| if (HasPopupNotifications()) |
| MarkSinglePopupAsShown(id, true); |
| #endif |
| scoped_refptr<NotificationDelegate> delegate = |
| notification_list_->GetNotificationDelegate(id); |
| if (delegate.get()) |
| delegate->Click(); |
| FOR_EACH_OBSERVER( |
| MessageCenterObserver, observer_list_, OnNotificationClicked(id)); |
| } |
| |
| void MessageCenterImpl::ClickOnNotificationButton(const std::string& id, |
| int button_index) { |
| if (FindVisibleNotificationById(id) == NULL) |
| return; |
| #if defined(OS_CHROMEOS) |
| if (HasPopupNotifications()) |
| MarkSinglePopupAsShown(id, true); |
| #endif |
| scoped_refptr<NotificationDelegate> delegate = |
| notification_list_->GetNotificationDelegate(id); |
| if (delegate.get()) |
| delegate->ButtonClick(button_index); |
| FOR_EACH_OBSERVER( |
| MessageCenterObserver, observer_list_, OnNotificationButtonClicked( |
| id, button_index)); |
| } |
| |
| void MessageCenterImpl::ClickOnSettingsButton(const std::string& id) { |
| scoped_refptr<NotificationDelegate> delegate = |
| notification_list_->GetNotificationDelegate(id); |
| if (delegate.get()) |
| delegate->SettingsClick(); |
| FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_, |
| OnNotificationSettingsClicked()); |
| } |
| |
| void MessageCenterImpl::MarkSinglePopupAsShown(const std::string& id, |
| bool mark_notification_as_read) { |
| if (FindVisibleNotificationById(id) == NULL) |
| return; |
| #if !defined(OS_CHROMEOS) |
| return this->RemoveNotification(id, false); |
| #else |
| notification_list_->MarkSinglePopupAsShown(id, mark_notification_as_read); |
| notification_cache_.RecountUnread(); |
| FOR_EACH_OBSERVER( |
| MessageCenterObserver, observer_list_, OnNotificationUpdated(id)); |
| #endif // defined(OS_CHROMEOS) |
| } |
| |
| void MessageCenterImpl::DisplayedNotification( |
| const std::string& id, |
| const DisplaySource source) { |
| if (FindVisibleNotificationById(id) == NULL) |
| return; |
| |
| if (HasPopupNotifications()) |
| notification_list_->MarkSinglePopupAsDisplayed(id); |
| notification_cache_.RecountUnread(); |
| scoped_refptr<NotificationDelegate> delegate = |
| notification_list_->GetNotificationDelegate(id); |
| if (delegate.get()) |
| delegate->Display(); |
| FOR_EACH_OBSERVER( |
| MessageCenterObserver, |
| observer_list_, |
| OnNotificationDisplayed(id, source)); |
| } |
| |
| void MessageCenterImpl::SetNotifierSettingsProvider( |
| NotifierSettingsProvider* provider) { |
| if (settings_provider_) { |
| settings_provider_->RemoveObserver(this); |
| settings_provider_ = NULL; |
| } |
| settings_provider_ = provider; |
| if (settings_provider_) |
| settings_provider_->AddObserver(this); |
| } |
| |
| NotifierSettingsProvider* MessageCenterImpl::GetNotifierSettingsProvider() { |
| return settings_provider_; |
| } |
| |
| void MessageCenterImpl::SetQuietMode(bool in_quiet_mode) { |
| if (in_quiet_mode != notification_list_->quiet_mode()) { |
| notification_list_->SetQuietMode(in_quiet_mode); |
| FOR_EACH_OBSERVER(MessageCenterObserver, |
| observer_list_, |
| OnQuietModeChanged(in_quiet_mode)); |
| } |
| quiet_mode_timer_.reset(); |
| } |
| |
| void MessageCenterImpl::SetLockedState(bool locked) { |
| if (locked != locked_) { |
| locked_ = locked; |
| FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_, |
| OnLockedStateChanged(locked)); |
| } |
| } |
| |
| void MessageCenterImpl::EnterQuietModeWithExpire( |
| const base::TimeDelta& expires_in) { |
| if (quiet_mode_timer_) { |
| // Note that the capital Reset() is the method to restart the timer, not |
| // scoped_ptr::reset(). |
| quiet_mode_timer_->Reset(); |
| } else { |
| notification_list_->SetQuietMode(true); |
| FOR_EACH_OBSERVER( |
| MessageCenterObserver, observer_list_, OnQuietModeChanged(true)); |
| |
| quiet_mode_timer_.reset(new base::OneShotTimer); |
| quiet_mode_timer_->Start( |
| FROM_HERE, |
| expires_in, |
| base::Bind( |
| &MessageCenterImpl::SetQuietMode, base::Unretained(this), false)); |
| } |
| } |
| |
| void MessageCenterImpl::RestartPopupTimers() { |
| if (popup_timers_controller_) |
| popup_timers_controller_->StartAll(); |
| } |
| |
| void MessageCenterImpl::PausePopupTimers() { |
| if (popup_timers_controller_) |
| popup_timers_controller_->PauseAll(); |
| } |
| |
| void MessageCenterImpl::DisableTimersForTest() { |
| popup_timers_controller_.reset(); |
| } |
| |
| void MessageCenterImpl::EnableChangeQueueForTest(bool enable) { |
| if (enable) |
| notification_queue_.reset(new internal::ChangeQueue()); |
| else |
| notification_queue_.reset(); |
| } |
| |
| } // namespace message_center |