blob: f2c8e3a062cfc84788138ad6081a197a53433954 [file] [log] [blame]
// Copyright 2017 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 "chrome/browser/notifications/chrome_ash_message_center_client.h"
#include "ash/public/interfaces/constants.mojom.h"
#include "base/i18n/string_compare.h"
#include "base/stl_util.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/arc/arc_session_manager.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/notifications/arc_application_notifier_controller.h"
#include "chrome/browser/notifications/extension_notifier_controller.h"
#include "chrome/browser/notifications/web_page_notifier_controller.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
#include "chrome/browser/ui/settings_window_manager_chromeos.h"
#include "chrome/common/webui_url_constants.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/notification_service.h"
#include "content/public/common/service_manager_connection.h"
#include "services/service_manager/public/cpp/connector.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/public/cpp/notifier_id.h"
using ash::mojom::NotifierUiDataPtr;
using message_center::MessageCenter;
using message_center::NotifierId;
namespace {
// The singleton instance, which is tracked to allow access from tests.
ChromeAshMessageCenterClient* g_chrome_ash_message_center_client = nullptr;
// All notifier actions are performed on the notifiers for the currently active
// profile, so this just returns the active profile.
Profile* GetProfileForNotifiers() {
const auto* user = user_manager::UserManager::Get()->GetActiveUser();
return chromeos::ProfileHelper::Get()->GetProfileByUser(user);
}
class NotifierComparator {
public:
explicit NotifierComparator(icu::Collator* collator) : collator_(collator) {}
bool operator()(const NotifierUiDataPtr& n1, const NotifierUiDataPtr& n2) {
if (n1->notifier_id.type != n2->notifier_id.type)
return n1->notifier_id.type < n2->notifier_id.type;
if (collator_) {
return base::i18n::CompareString16WithCollator(*collator_, n1->name,
n2->name) == UCOL_LESS;
}
return n1->name < n2->name;
}
private:
icu::Collator* collator_;
};
// This delegate forwards NotificationDelegate methods to their equivalent in
// NotificationPlatformBridgeDelegate.
class ForwardingNotificationDelegate
: public message_center::NotificationDelegate {
public:
ForwardingNotificationDelegate(const std::string& notification_id,
NotificationPlatformBridgeDelegate* delegate)
: notification_id_(notification_id), delegate_(delegate) {}
// message_center::NotificationDelegate:
void Close(bool by_user) override {
delegate_->HandleNotificationClosed(notification_id_, by_user);
}
void Click(const base::Optional<int>& button_index,
const base::Optional<base::string16>& reply) override {
if (button_index) {
delegate_->HandleNotificationButtonClicked(notification_id_,
*button_index, reply);
} else {
delegate_->HandleNotificationClicked(notification_id_);
}
}
void SettingsClick() override {
delegate_->HandleNotificationSettingsButtonClicked(notification_id_);
}
void DisableNotification() override {
delegate_->DisableNotification(notification_id_);
}
private:
~ForwardingNotificationDelegate() override = default;
// The ID of the notification.
const std::string notification_id_;
NotificationPlatformBridgeDelegate* delegate_;
DISALLOW_COPY_AND_ASSIGN(ForwardingNotificationDelegate);
};
} // namespace
ChromeAshMessageCenterClient::ChromeAshMessageCenterClient(
NotificationPlatformBridgeDelegate* delegate)
: delegate_(delegate), binding_(this) {
DCHECK(!g_chrome_ash_message_center_client);
g_chrome_ash_message_center_client = this;
// May be null in unit tests.
auto* connection = content::ServiceManagerConnection::GetForProcess();
if (connection) {
connection->GetConnector()->BindInterface(ash::mojom::kServiceName,
&controller_);
// Register this object as the client interface implementation.
ash::mojom::AshMessageCenterClientAssociatedPtrInfo ptr_info;
binding_.Bind(mojo::MakeRequest(&ptr_info));
controller_->SetClient(std::move(ptr_info));
}
sources_.insert(
std::make_pair(message_center::NotifierType::APPLICATION,
std::make_unique<ExtensionNotifierController>(this)));
sources_.insert(
std::make_pair(message_center::NotifierType::WEB_PAGE,
std::make_unique<WebPageNotifierController>(this)));
sources_.insert(std::make_pair(
message_center::NotifierType::ARC_APPLICATION,
std::make_unique<arc::ArcApplicationNotifierController>(this)));
}
ChromeAshMessageCenterClient::~ChromeAshMessageCenterClient() {
DCHECK_EQ(this, g_chrome_ash_message_center_client);
g_chrome_ash_message_center_client = nullptr;
if (deferred_notifier_list_callback_) {
std::move(deferred_notifier_list_callback_)
.Run(std::vector<ash::mojom::NotifierUiDataPtr>());
}
}
void ChromeAshMessageCenterClient::Display(
const message_center::Notification& notification) {
auto message_center_notification =
std::make_unique<message_center::Notification>(
base::WrapRefCounted(
new ForwardingNotificationDelegate(notification.id(), delegate_)),
notification);
MessageCenter::Get()->AddNotification(std::move(message_center_notification));
}
void ChromeAshMessageCenterClient::Close(const std::string& notification_id) {
// During shutdown, Ash is destroyed before |this|, taking the MessageCenter
// with it.
if (MessageCenter::Get()) {
MessageCenter::Get()->RemoveNotification(notification_id,
false /* by_user */);
}
}
void ChromeAshMessageCenterClient::SetNotifierEnabled(
const NotifierId& notifier_id,
bool enabled) {
Profile* profile = GetProfileForNotifiers();
CHECK(profile);
sources_[notifier_id.type]->SetNotifierEnabled(profile, notifier_id, enabled);
}
void ChromeAshMessageCenterClient::GetNotifierList(
GetNotifierListCallback callback) {
if (deferred_notifier_list_callback_) {
std::move(deferred_notifier_list_callback_)
.Run(std::vector<ash::mojom::NotifierUiDataPtr>());
registrar_.RemoveAll();
}
Profile* profile = GetProfileForNotifiers();
if (profile) {
RespondWithNotifierList(profile, std::move(callback));
} else {
LOG(ERROR) << "GetNotifierList called before profile fully loaded, see "
"https://crbug.com/968825";
deferred_notifier_list_callback_ = std::move(callback);
registrar_.Add(this, chrome::NOTIFICATION_PROFILE_ADDED,
content::NotificationService::AllSources());
}
}
void ChromeAshMessageCenterClient::RespondWithNotifierList(
Profile* profile,
GetNotifierListCallback callback) const {
CHECK(profile);
std::vector<ash::mojom::NotifierUiDataPtr> notifiers;
for (auto& source : sources_) {
auto source_notifiers = source.second->GetNotifierList(profile);
for (auto& notifier : source_notifiers) {
notifiers.push_back(std::move(notifier));
}
}
UErrorCode error = U_ZERO_ERROR;
std::unique_ptr<icu::Collator> collator(icu::Collator::createInstance(error));
NotifierComparator comparator(U_SUCCESS(error) ? collator.get() : nullptr);
std::sort(notifiers.begin(), notifiers.end(), comparator);
std::move(callback).Run(std::move(notifiers));
}
void ChromeAshMessageCenterClient::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
switch (type) {
case chrome::NOTIFICATION_PROFILE_ADDED: {
Profile* profile = GetProfileForNotifiers();
if (profile) {
CHECK(deferred_notifier_list_callback_);
RespondWithNotifierList(profile,
std::move(deferred_notifier_list_callback_));
registrar_.RemoveAll();
}
break;
}
default:
NOTREACHED();
}
}
void ChromeAshMessageCenterClient::GetArcAppIdByPackageName(
const std::string& package_name,
GetArcAppIdByPackageNameCallback callback) {
std::move(callback).Run(
ArcAppListPrefs::Get(arc::ArcSessionManager::Get()->profile())
->GetAppIdByPackageName(package_name));
}
void ChromeAshMessageCenterClient::OnIconImageUpdated(
const NotifierId& notifier_id,
const gfx::ImageSkia& image) {
// |controller_| may be null in unit tests.
if (!image.isNull() && controller_)
controller_->UpdateNotifierIcon(notifier_id, image);
}
void ChromeAshMessageCenterClient::OnNotifierEnabledChanged(
const NotifierId& notifier_id,
bool enabled) {
// May be null in unit tests.
if (controller_)
controller_->NotifierEnabledChanged(notifier_id, enabled);
}
// static
void ChromeAshMessageCenterClient::FlushForTesting() {
g_chrome_ash_message_center_client->binding_.FlushForTesting();
}