123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596 |
- // 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 <iterator>
- #include <memory>
- #include <utility>
- #include <vector>
- #include "base/auto_reset.h"
- #include "base/bind.h"
- #include "base/command_line.h"
- #include "base/containers/contains.h"
- #include "base/observer_list.h"
- #include "base/strings/string_util.h"
- #include "build/build_config.h"
- #include "build/chromeos_buildflags.h"
- #include "ui/message_center/lock_screen/lock_screen_controller.h"
- #include "ui/message_center/message_center_types.h"
- #include "ui/message_center/notification_blocker.h"
- #include "ui/message_center/notification_list.h"
- #include "ui/message_center/popup_timers_controller.h"
- #include "ui/message_center/public/cpp/message_center_constants.h"
- #include "ui/message_center/public/cpp/notification.h"
- #include "ui/message_center/public/cpp/notification_types.h"
- #include "ui/message_center/public/cpp/notifier_id.h"
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- #include "ash/constants/ash_features.h"
- #endif
- namespace message_center {
- ////////////////////////////////////////////////////////////////////////////////
- // MessageCenterImpl
- MessageCenterImpl::MessageCenterImpl(
- std::unique_ptr<LockScreenController> lock_screen_controller)
- : lock_screen_controller_(std::move(lock_screen_controller)),
- popup_timers_controller_(std::make_unique<PopupTimersController>(this)),
- stats_collector_(this) {
- notification_list_ = std::make_unique<NotificationList>(this);
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- notifications_grouping_enabled_ =
- ash::features::IsNotificationsRefreshEnabled();
- #endif
- }
- MessageCenterImpl::~MessageCenterImpl() = default;
- void MessageCenterImpl::AddObserver(MessageCenterObserver* observer) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- observer_list_.AddObserver(observer);
- }
- void MessageCenterImpl::RemoveObserver(MessageCenterObserver* observer) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- observer_list_.RemoveObserver(observer);
- }
- void MessageCenterImpl::AddNotificationBlocker(NotificationBlocker* blocker) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (base::Contains(blockers_, blocker))
- return;
- blocker->AddObserver(this);
- blockers_.push_back(blocker);
- }
- void MessageCenterImpl::RemoveNotificationBlocker(
- NotificationBlocker* blocker) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- auto iter = std::find(blockers_.begin(), blockers_.end(), blocker);
- if (iter == blockers_.end())
- return;
- blocker->RemoveObserver(this);
- blockers_.erase(iter);
- }
- void MessageCenterImpl::OnBlockingStateChanged(NotificationBlocker* blocker) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- std::list<std::string> blocked;
- NotificationList::PopupNotifications popups =
- notification_list_->GetPopupNotifications(blockers_, &blocked);
- visible_notifications_ =
- notification_list_->GetVisibleNotifications(blockers_);
- for (const std::string& notification_id : blocked) {
- for (MessageCenterObserver& observer : observer_list_)
- observer.OnNotificationUpdated(notification_id);
- }
- for (MessageCenterObserver& observer : observer_list_)
- observer.OnBlockingStateChanged(blocker);
- }
- void MessageCenterImpl::SetVisibility(Visibility visibility) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- visible_ = (visibility == VISIBILITY_MESSAGE_CENTER);
- if (visible_) {
- std::set<std::string> updated_ids;
- notification_list_->SetNotificationsShown(blockers_, &updated_ids);
- for (const auto& id : updated_ids) {
- for (MessageCenterObserver& observer : observer_list_)
- observer.OnNotificationUpdated(id);
- }
- for (Notification* notification : GetPopupNotifications())
- MarkSinglePopupAsShown(notification->id(), false);
- }
- for (MessageCenterObserver& observer : observer_list_)
- observer.OnCenterVisibilityChanged(visibility);
- }
- bool MessageCenterImpl::IsMessageCenterVisible() const {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- return visible_;
- }
- void MessageCenterImpl::SetHasMessageCenterView(bool has_message_center_view) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- has_message_center_view_ = has_message_center_view;
- }
- bool MessageCenterImpl::HasMessageCenterView() const {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- return has_message_center_view_;
- }
- size_t MessageCenterImpl::NotificationCount() const {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- return visible_notifications_.size();
- }
- bool MessageCenterImpl::HasPopupNotifications() const {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- return !IsMessageCenterVisible() &&
- notification_list_->HasPopupNotifications(blockers_);
- }
- bool MessageCenterImpl::IsQuietMode() const {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- return notification_list_->quiet_mode();
- }
- bool MessageCenterImpl::IsSpokenFeedbackEnabled() const {
- return spoken_feedback_enabled_;
- }
- Notification* MessageCenterImpl::FindNotificationById(const std::string& id) {
- return notification_list_->GetNotificationById(id);
- }
- Notification* MessageCenterImpl::FindParentNotification(
- Notification* notification) {
- // For a notification to have a parent notification, they must have identical
- // notifier_ids. To make sure that the notifications come from
- // the same website for the same user. Also make sure to only group
- // notifications from web pages with valid origin urls.
- if (notification->origin_url().is_empty() ||
- notification->notifier_id().type != NotifierType::WEB_PAGE) {
- return nullptr;
- }
- NotificationList::Notifications notifications =
- notification_list_->GetNotificationsByNotifierId(
- notification->notifier_id());
- // `notifications` keeps notifications ordered with the most recent one in the
- // front. If we have notifications for this notifier_id we return the last
- // notification..
- return notifications.size() ? *notifications.rbegin() : nullptr;
- }
- Notification* MessageCenterImpl::FindPopupNotificationById(
- const std::string& id) {
- auto id_match = [&id](Notification* notification) {
- return id == notification->id();
- };
- auto notifications = GetPopupNotifications();
- auto notification =
- std::find_if(notifications.begin(), notifications.end(), id_match);
- return notification == notifications.end() ? nullptr : *notification;
- }
- Notification* MessageCenterImpl::FindVisibleNotificationById(
- const std::string& id) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- const auto& notifications = GetVisibleNotifications();
- for (Notification* notification : notifications) {
- if (notification->id() == id)
- return notification;
- }
- return nullptr;
- }
- NotificationList::Notifications MessageCenterImpl::FindNotificationsByAppId(
- const std::string& app_id) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- return notification_list_->GetNotificationsByAppId(app_id);
- }
- NotificationList::Notifications MessageCenterImpl::GetNotifications() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- return notification_list_->GetNotifications();
- }
- const NotificationList::Notifications&
- MessageCenterImpl::GetVisibleNotifications() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- return visible_notifications_;
- }
- NotificationList::PopupNotifications
- MessageCenterImpl::GetPopupNotifications() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- return notification_list_->GetPopupNotifications(blockers_, nullptr);
- }
- NotificationList::PopupNotifications
- MessageCenterImpl::GetPopupNotificationsWithoutBlocker(
- const NotificationBlocker& blocker) const {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- return notification_list_->GetPopupNotificationsWithoutBlocker(blockers_,
- blocker);
- }
- //------------------------------------------------------------------------------
- // Client code interface.
- void MessageCenterImpl::AddNotification(
- std::unique_ptr<Notification> notification) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(notification);
- notification->set_allow_group(notifications_grouping_enabled_);
- const std::string id = notification->id();
- for (NotificationBlocker* blocker : blockers_)
- blocker->CheckState();
- auto* parent = FindParentNotification(notification.get());
- if (notification->allow_group() && parent && !notification->group_parent()) {
- parent->SetGroupParent();
- notification->SetGroupChild();
- }
- // 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) != nullptr;
- if (already_exists) {
- UpdateNotification(id, std::move(notification));
- return;
- }
- notification_list_->AddNotification(std::move(notification));
- visible_notifications_ =
- notification_list_->GetVisibleNotifications(blockers_);
- for (MessageCenterObserver& observer : observer_list_)
- observer.OnNotificationAdded(id);
- }
- void MessageCenterImpl::UpdateNotification(
- const std::string& old_id,
- std::unique_ptr<Notification> new_notification) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- for (NotificationBlocker* blocker : blockers_)
- blocker->CheckState();
- std::string new_id = new_notification->id();
- notification_list_->UpdateNotificationMessage(old_id,
- std::move(new_notification));
- visible_notifications_ =
- notification_list_->GetVisibleNotifications(blockers_);
- for (MessageCenterObserver& observer : observer_list_) {
- if (old_id == new_id) {
- observer.OnNotificationUpdated(new_id);
- } else {
- observer.OnNotificationRemoved(old_id, false);
- observer.OnNotificationAdded(new_id);
- }
- }
- }
- void MessageCenterImpl::RemoveNotification(const std::string& id,
- bool by_user) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- Notification* notification = notification_list_->GetNotificationById(id);
- if (!notification)
- return;
- if (by_user && notification->pinned()) {
- // When pinned, a popup will not be removed completely but moved into the
- // message center bubble.
- MarkSinglePopupAsShown(id, true);
- 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);
- // Remove notification before calling the Close method in case it calls
- // RemoveNotification reentrantly.
- notification_list_->RemoveNotification(copied_id);
- if (delegate.get())
- delegate->Close(by_user);
- visible_notifications_ =
- notification_list_->GetVisibleNotifications(blockers_);
- for (MessageCenterObserver& observer : observer_list_)
- observer.OnNotificationRemoved(copied_id, by_user);
- }
- void MessageCenterImpl::RemoveNotificationsForNotifierId(
- const NotifierId& notifier_id) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- NotificationList::Notifications notifications =
- notification_list_->GetNotificationsByNotifierId(notifier_id);
- for (Notification* notification : notifications)
- RemoveNotification(notification->id(), false);
- if (!notifications.empty()) {
- visible_notifications_ =
- notification_list_->GetVisibleNotifications(blockers_);
- }
- }
- void MessageCenterImpl::RemoveAllNotifications(bool by_user, RemoveType type) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- bool remove_pinned = (type == RemoveType::ALL);
- const NotificationBlockers& blockers =
- remove_pinned ? NotificationBlockers() /* empty blockers */
- : blockers_; /* use default blockers */
- const NotificationList::Notifications notifications =
- notification_list_->GetVisibleNotifications(blockers);
- std::set<std::string> ids;
- for (Notification* notification : notifications) {
- if (!remove_pinned && notification->pinned())
- continue;
- ids.insert(notification->id());
- scoped_refptr<NotificationDelegate> delegate = notification->delegate();
- // Remove notification before calling the Close method in case it calls
- // RemoveNotification reentrantly.
- notification_list_->RemoveNotification(notification->id());
- if (delegate.get())
- delegate->Close(by_user);
- }
- if (!ids.empty()) {
- visible_notifications_ =
- notification_list_->GetVisibleNotifications(blockers_);
- }
- for (const auto& id : ids) {
- for (MessageCenterObserver& observer : observer_list_)
- observer.OnNotificationRemoved(id, by_user);
- }
- }
- void MessageCenterImpl::SetNotificationIcon(const std::string& notification_id,
- const ui::ImageModel& image) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (notification_list_->SetNotificationIcon(notification_id, image)) {
- for (MessageCenterObserver& observer : observer_list_)
- observer.OnNotificationUpdated(notification_id);
- }
- }
- void MessageCenterImpl::SetNotificationImage(const std::string& notification_id,
- const gfx::Image& image) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (notification_list_->SetNotificationImage(notification_id, image)) {
- for (MessageCenterObserver& observer : observer_list_)
- observer.OnNotificationUpdated(notification_id);
- }
- }
- void MessageCenterImpl::ClickOnNotification(const std::string& id) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (!FindVisibleNotificationById(id))
- return;
- lock_screen_controller_->DismissLockScreenThenExecute(
- base::BindOnce(&MessageCenterImpl::ClickOnNotificationUnlocked,
- base::Unretained(this), id, absl::nullopt, absl::nullopt),
- base::OnceClosure());
- }
- void MessageCenterImpl::ClickOnNotificationButton(const std::string& id,
- int button_index) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (!FindVisibleNotificationById(id))
- return;
- lock_screen_controller_->DismissLockScreenThenExecute(
- base::BindOnce(&MessageCenterImpl::ClickOnNotificationUnlocked,
- base::Unretained(this), id, button_index, absl::nullopt),
- base::OnceClosure());
- }
- void MessageCenterImpl::ClickOnNotificationButtonWithReply(
- const std::string& id,
- int button_index,
- const std::u16string& reply) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (!FindVisibleNotificationById(id))
- return;
- lock_screen_controller_->DismissLockScreenThenExecute(
- base::BindOnce(&MessageCenterImpl::ClickOnNotificationUnlocked,
- base::Unretained(this), id, button_index, reply),
- base::OnceClosure());
- }
- void MessageCenterImpl::ClickOnNotificationUnlocked(
- const std::string& id,
- const absl::optional<int>& button_index,
- const absl::optional<std::u16string>& reply) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- // This method must be called under unlocked screen.
- DCHECK(!lock_screen_controller_->IsScreenLocked());
- // Ensure the notification is still visible.
- if (!FindVisibleNotificationById(id))
- return;
- if (HasMessageCenterView() && HasPopupNotifications())
- MarkSinglePopupAsShown(id, true);
- for (MessageCenterObserver& observer : observer_list_)
- observer.OnNotificationClicked(id, button_index, reply);
- scoped_refptr<NotificationDelegate> delegate =
- notification_list_->GetNotificationDelegate(id);
- if (delegate)
- delegate->Click(button_index, reply);
- }
- void MessageCenterImpl::ClickOnSettingsButton(const std::string& id) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- Notification* notification = notification_list_->GetNotificationById(id);
- bool handled_by_delegate =
- notification &&
- (notification->rich_notification_data().settings_button_handler ==
- SettingsButtonHandler::DELEGATE);
- if (handled_by_delegate)
- notification->delegate()->SettingsClick();
- for (MessageCenterObserver& observer : observer_list_)
- observer.OnNotificationSettingsClicked(handled_by_delegate);
- }
- void MessageCenterImpl::DisableNotification(const std::string& id) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- Notification* notification = notification_list_->GetNotificationById(id);
- if (notification && notification->delegate()) {
- notification->delegate()->DisableNotification();
- RemoveNotificationsForNotifierId(notification->notifier_id());
- }
- }
- void MessageCenterImpl::MarkSinglePopupAsShown(const std::string& id,
- bool mark_notification_as_read) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (!FindVisibleNotificationById(id))
- return;
- if (HasMessageCenterView()) {
- notification_list_->MarkSinglePopupAsShown(id, mark_notification_as_read);
- for (MessageCenterObserver& observer : observer_list_) {
- observer.OnNotificationUpdated(id);
- observer.OnNotificationPopupShown(id, mark_notification_as_read);
- }
- } else {
- RemoveNotification(id, false);
- }
- }
- void MessageCenterImpl::ResetPopupTimer(const std::string& id) {
- DCHECK(FindPopupNotificationById(id));
- popup_timers_controller_->CancelTimer(id);
- popup_timers_controller_->StartTimer(
- id, popup_timers_controller_->GetTimeoutForNotification(
- FindPopupNotificationById(id)));
- }
- void MessageCenterImpl::ResetSinglePopup(const std::string& id) {
- notification_list_->ResetSinglePopup(id);
- for (MessageCenterObserver& observer : observer_list_) {
- observer.OnNotificationUpdated(id);
- }
- }
- void MessageCenterImpl::DisplayedNotification(const std::string& id,
- const DisplaySource source) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- // This method may be called from the handlers, so we shouldn't manipulate
- // notifications in this method.
- if (!FindVisibleNotificationById(id))
- return;
- if (HasPopupNotifications())
- notification_list_->MarkSinglePopupAsDisplayed(id);
- scoped_refptr<NotificationDelegate> delegate =
- notification_list_->GetNotificationDelegate(id);
- for (MessageCenterObserver& observer : observer_list_)
- observer.OnNotificationDisplayed(id, source);
- }
- void MessageCenterImpl::SetQuietMode(bool in_quiet_mode) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (in_quiet_mode != notification_list_->quiet_mode()) {
- notification_list_->SetQuietMode(in_quiet_mode);
- for (MessageCenterObserver& observer : observer_list_)
- observer.OnQuietModeChanged(in_quiet_mode);
- }
- quiet_mode_timer_.Stop();
- }
- void MessageCenterImpl::SetSpokenFeedbackEnabled(bool enabled) {
- spoken_feedback_enabled_ = enabled;
- }
- void MessageCenterImpl::EnterQuietModeWithExpire(
- const base::TimeDelta& expires_in) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (!quiet_mode_timer_.IsRunning()) {
- notification_list_->SetQuietMode(true);
- for (MessageCenterObserver& observer : observer_list_)
- observer.OnQuietModeChanged(true);
- }
- // This will restart the timer if it is already running.
- quiet_mode_timer_.Start(FROM_HERE, expires_in,
- base::BindOnce(&MessageCenterImpl::SetQuietMode,
- base::Unretained(this), false));
- }
- void MessageCenterImpl::RestartPopupTimers() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (popup_timers_controller_)
- popup_timers_controller_->StartAll();
- }
- void MessageCenterImpl::PausePopupTimers() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (popup_timers_controller_)
- popup_timers_controller_->PauseAll();
- }
- const std::u16string& MessageCenterImpl::GetSystemNotificationAppName() const {
- return system_notification_app_name_;
- }
- void MessageCenterImpl::SetSystemNotificationAppName(
- const std::u16string& name) {
- system_notification_app_name_ = name;
- }
- void MessageCenterImpl::OnMessageViewHovered(
- const std::string& notification_id) {
- for (MessageCenterObserver& observer : observer_list_)
- observer.OnMessageViewHovered(notification_id);
- }
- void MessageCenterImpl::DisableTimersForTest() {
- popup_timers_controller_.reset();
- }
- } // namespace message_center
|