// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/usb_peripheral/usb_peripheral_notification_controller.h"
#include <optional>
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/notifier_catalogs.h"
#include "ash/public/cpp/new_window_delegate.h"
#include "ash/public/cpp/notification_utils.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "third_party/cros_system_api/dbus/typecd/dbus-constants.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/public/cpp/notification.h"
#include "ui/message_center/public/cpp/notification_types.h"
#include "url/gurl.h"
namespace ash {
namespace {
const char kNotifierUsbPeripheral[] = "ash.usb_peripheral";
const char kUsbPeripheralInvalidDpCableNotificationId[] =
"cros_usb_peripheral_invalid_dp_cable_notification_id";
const char kUsbPeripheralInvalidUSB4ValidTBTCableNotificationId[] =
"cros_usb_peripheral_invalid_usb4_valid_tbt_cable_notification_id";
const char kUsbPeripheralInvalidUSB4CableNotificationId[] =
"cros_usb_peripheral_invalid_usb4_cable_notification_id";
const char kUsbPeripheralInvalidTBTCableNotificationId[] =
"cros_usb_peripheral_invalid_tbt_cable_notification_id";
const char kUsbPeripheralSpeedLimitingCableNotificationId[] =
"cros_usb_peripheral_speed_limiting_cable_notification_id";
const char kNotificationDisplayLandingPageUrl[] =
"https://support.google.com/chromebook?p=cable_notification";
const char kNotificationDeviceLandingPageUrl[] =
"https://support.google.com/chromebook?p=cable_notification_2";
bool GetCableSpeedNotificationShownPref() {
PrefService* prefs =
Shell::Get()->session_controller()->GetActivePrefService();
return prefs->GetBoolean(prefs::kUsbPeripheralCableSpeedNotificationShown);
}
void SetCableSpeedNotificationShownPref(bool pref) {
PrefService* prefs =
Shell::Get()->session_controller()->GetActivePrefService();
prefs->SetBoolean(prefs::kUsbPeripheralCableSpeedNotificationShown, pref);
}
bool ShouldDisplayNotification() {
return Shell::Get()->session_controller()->GetSessionState() ==
session_manager::SessionState::ACTIVE &&
!Shell::Get()->session_controller()->IsUserSessionBlocked();
}
void OnCableNotificationClicked(const std::string& notification_id,
const std::string& landing_page,
std::optional<int> button_index) {
if (notification_id == kUsbPeripheralSpeedLimitingCableNotificationId)
SetCableSpeedNotificationShownPref(true);
if (button_index) {
NewWindowDelegate::GetInstance()->OpenUrl(
GURL(landing_page), NewWindowDelegate::OpenUrlFrom::kUserInteraction,
NewWindowDelegate::Disposition::kNewForegroundTab);
}
message_center::MessageCenter::Get()->RemoveNotification(notification_id,
/*from_user=*/true);
}
} // namespace
UsbPeripheralNotificationController::UsbPeripheralNotificationController(
message_center::MessageCenter* message_center)
: message_center_(message_center) {
DCHECK(message_center_);
}
UsbPeripheralNotificationController::~UsbPeripheralNotificationController() {
if (ash::PeripheralNotificationManager::IsInitialized())
ash::PeripheralNotificationManager::Get()->RemoveObserver(this);
}
// static
void UsbPeripheralNotificationController::RegisterProfilePrefs(
PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(
prefs::kUsbPeripheralCableSpeedNotificationShown, false);
}
void UsbPeripheralNotificationController::
OnPeripheralNotificationManagerInitialized() {
DCHECK(ash::PeripheralNotificationManager::IsInitialized());
ash::PeripheralNotificationManager::Get()->AddObserver(this);
}
// Notify the user that the current cable may not support dp alt mode.
void UsbPeripheralNotificationController::OnInvalidDpCableWarning() {
if (!ShouldDisplayNotification())
return;
message_center::RichNotificationData optional;
optional.buttons.push_back(message_center::ButtonInfo(
l10n_util::GetStringUTF16(IDS_ASH_USB_NOTIFICATION_V2_LEARN_MORE)));
std::unique_ptr<message_center::Notification> notification =
CreateSystemNotificationPtr(
message_center::NOTIFICATION_TYPE_SIMPLE,
kUsbPeripheralInvalidDpCableNotificationId,
l10n_util::GetStringUTF16(
IDS_ASH_USB_NOTIFICATION_V2_CABLE_WARNING_DISPLAY_TITLE),
l10n_util::GetStringUTF16(
IDS_ASH_USB_NOTIFICATION_V2_CABLE_WARNING_DISPLAY_BODY),
/*display_source=*/std::u16string(), GURL(),
message_center::NotifierId(
message_center::NotifierType::SYSTEM_COMPONENT,
kNotifierUsbPeripheral,
NotificationCatalogName::kUSBPeripheralInvalidDpCable),
optional,
base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
base::BindRepeating(&OnCableNotificationClicked,
kUsbPeripheralInvalidDpCableNotificationId,
kNotificationDisplayLandingPageUrl)),
kSettingsIcon,
message_center::SystemNotificationWarningLevel::WARNING);
message_center_->AddNotification(std::move(notification));
}
// Notify the user that the USB4 device will use TBT due to the cable.
void UsbPeripheralNotificationController::OnInvalidUSB4ValidTBTCableWarning() {
if (!ShouldDisplayNotification())
return;
message_center::RichNotificationData optional;
optional.buttons.push_back(message_center::ButtonInfo(
l10n_util::GetStringUTF16(IDS_ASH_USB_NOTIFICATION_V2_LEARN_MORE)));
std::unique_ptr<message_center::Notification> notification =
CreateSystemNotificationPtr(
message_center::NOTIFICATION_TYPE_SIMPLE,
kUsbPeripheralInvalidUSB4ValidTBTCableNotificationId,
l10n_util::GetStringUTF16(
IDS_ASH_USB_NOTIFICATION_V2_CABLE_WARNING_PERFORMANCE_TITLE),
l10n_util::GetStringUTF16(
IDS_ASH_USB_NOTIFICATION_V2_CABLE_WARNING_NO_USB4_SUPPORT_BODY),
/*display_source=*/std::u16string(), GURL(),
message_center::NotifierId(
message_center::NotifierType::SYSTEM_COMPONENT,
kNotifierUsbPeripheral,
NotificationCatalogName::kUSBPeripheralInvalidUSB4ValidTBTCable),
optional,
base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
base::BindRepeating(
&OnCableNotificationClicked,
kUsbPeripheralInvalidUSB4ValidTBTCableNotificationId,
kNotificationDeviceLandingPageUrl)),
kSettingsIcon,
message_center::SystemNotificationWarningLevel::WARNING);
message_center_->AddNotification(std::move(notification));
}
// Notify the user that the USB4 device will use DisplayPort,
// USB 3.2 or USB 2.0 due to the cable.
void UsbPeripheralNotificationController::OnInvalidUSB4CableWarning() {
if (!ShouldDisplayNotification())
return;
message_center::RichNotificationData optional;
optional.buttons.push_back(message_center::ButtonInfo(
l10n_util::GetStringUTF16(IDS_ASH_USB_NOTIFICATION_V2_LEARN_MORE)));
std::unique_ptr<message_center::Notification> notification =
CreateSystemNotificationPtr(
message_center::NOTIFICATION_TYPE_SIMPLE,
kUsbPeripheralInvalidUSB4CableNotificationId,
l10n_util::GetStringUTF16(
IDS_ASH_USB_NOTIFICATION_V2_CABLE_WARNING_PERFORMANCE_TITLE),
l10n_util::GetStringUTF16(
IDS_ASH_USB_NOTIFICATION_V2_CABLE_WARNING_NO_USB4_SUPPORT_BODY),
/*display_source=*/std::u16string(), GURL(),
message_center::NotifierId(
message_center::NotifierType::SYSTEM_COMPONENT,
kNotifierUsbPeripheral,
NotificationCatalogName::kUSBPeripheralInvalidUSB4Cable),
optional,
base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
base::BindRepeating(&OnCableNotificationClicked,
kUsbPeripheralInvalidUSB4CableNotificationId,
kNotificationDeviceLandingPageUrl)),
kSettingsIcon,
message_center::SystemNotificationWarningLevel::WARNING);
message_center_->AddNotification(std::move(notification));
}
// Notify the user that the TBT device will use DisplayPort,
// USB 3.2 or USB 2.0 due to the cable.
void UsbPeripheralNotificationController::OnInvalidTBTCableWarning() {
if (!ShouldDisplayNotification())
return;
message_center::RichNotificationData optional;
optional.buttons.push_back(message_center::ButtonInfo(
l10n_util::GetStringUTF16(IDS_ASH_USB_NOTIFICATION_V2_LEARN_MORE)));
std::unique_ptr<message_center::Notification> notification =
CreateSystemNotificationPtr(
message_center::NOTIFICATION_TYPE_SIMPLE,
kUsbPeripheralInvalidTBTCableNotificationId,
l10n_util::GetStringUTF16(
IDS_ASH_USB_NOTIFICATION_V2_CABLE_WARNING_PERFORMANCE_TITLE),
l10n_util::GetStringUTF16(
IDS_ASH_USB_NOTIFICATION_V2_CABLE_WARNING_NO_TBT_SUPPORT_BODY),
/*display_source=*/std::u16string(), GURL(),
message_center::NotifierId(
message_center::NotifierType::SYSTEM_COMPONENT,
kNotifierUsbPeripheral,
NotificationCatalogName::kUSBPeripheralInvalidTBTCable),
optional,
base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
base::BindRepeating(&OnCableNotificationClicked,
kUsbPeripheralInvalidTBTCableNotificationId,
kNotificationDeviceLandingPageUrl)),
kSettingsIcon,
message_center::SystemNotificationWarningLevel::WARNING);
message_center_->AddNotification(std::move(notification));
}
// Notify the user that the cable limits USB device performance.
void UsbPeripheralNotificationController::OnSpeedLimitingCableWarning() {
if (!ShouldDisplayNotification() || GetCableSpeedNotificationShownPref())
return;
message_center::RichNotificationData optional;
optional.buttons.push_back(message_center::ButtonInfo(
l10n_util::GetStringUTF16(IDS_ASH_USB_NOTIFICATION_V2_LEARN_MORE)));
std::unique_ptr<message_center::Notification> notification =
CreateSystemNotificationPtr(
message_center::NOTIFICATION_TYPE_SIMPLE,
kUsbPeripheralSpeedLimitingCableNotificationId,
l10n_util::GetStringUTF16(
IDS_ASH_USB_NOTIFICATION_V2_CABLE_WARNING_PERFORMANCE_TITLE),
l10n_util::GetStringUTF16(
IDS_ASH_USB_NOTIFICATION_V2_CABLE_WARNING_SPEED_LIMITED_BODY),
/*display_source=*/std::u16string(), GURL(),
message_center::NotifierId(
message_center::NotifierType::SYSTEM_COMPONENT,
kNotifierUsbPeripheral,
NotificationCatalogName::kUSBPeripheralSpeedLimitingCable),
optional,
base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
base::BindRepeating(
&OnCableNotificationClicked,
kUsbPeripheralSpeedLimitingCableNotificationId,
kNotificationDeviceLandingPageUrl)),
kSettingsIcon,
message_center::SystemNotificationWarningLevel::WARNING);
message_center_->AddNotification(std::move(notification));
}
} // namespace ash