// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/notifications/update_required_notification.h"
#include "ash/constants/notifier_catalogs.h"
#include "ash/public/cpp/notification_utils.h"
#include "base/functional/bind.h"
#include "base/i18n/message_formatter.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/notifications/system_notification_helper.h"
#include "chrome/grit/generated_resources.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/base/l10n/l10n_util.h"
using NotificationType = policy::MinimumVersionPolicyHandler::NotificationType;
using MessageFormatter = base::i18n::MessageFormatter;
namespace ash {
namespace {
const char kUpdateRequiredNotificationId[] = "policy.update_required";
std::u16string GetTitle(NotificationType type,
int days_remaining,
const std::u16string& device_type) {
// |days_remaining| could be zero if we are very close to the deadline, like
// 10 minutes as we round of the time remaining into days. In this case, we
// need to show the last day notification title which does not mention the
// number of remaining days but is rather a generic string like 'Immediate
// update required'.
days_remaining = days_remaining > 1 ? days_remaining : 1;
switch (type) {
case NotificationType::kNoConnection:
case NotificationType::kMeteredConnection:
return (days_remaining % 7)
? MessageFormatter::FormatWithNumberedArgs(
l10n_util::GetStringUTF16(
IDS_UPDATE_REQUIRED_NETWORK_LIMITATION_TITLE_DAYS),
days_remaining, device_type)
: MessageFormatter::FormatWithNumberedArgs(
l10n_util::GetStringUTF16(
IDS_UPDATE_REQUIRED_NETWORK_LIMITATION_TITLE_WEEKS),
days_remaining / 7, device_type);
case NotificationType::kEolReached:
return (days_remaining % 7)
? MessageFormatter::FormatWithNumberedArgs(
l10n_util::GetStringUTF16(
IDS_UPDATE_REQUIRED_EOL_TITLE_DAYS),
days_remaining, device_type)
: MessageFormatter::FormatWithNumberedArgs(
l10n_util::GetStringUTF16(
IDS_UPDATE_REQUIRED_EOL_TITLE_WEEKS),
days_remaining / 7, device_type);
}
}
std::u16string GetMessage(NotificationType type,
const std::string& manager,
int days_remaining,
const std::u16string& device_type) {
// |days_remaining| could be zero if we are very close to the deadline, like
// 10 minutes as we round of the time remaining into days. In this case, we
// need to show the last day notification.
days_remaining = days_remaining > 1 ? days_remaining : 1;
switch (type) {
case NotificationType::kNoConnection:
return MessageFormatter::FormatWithNumberedArgs(
l10n_util::GetStringUTF16(IDS_UPDATE_REQUIRED_NO_NETWORK_MESSAGE),
days_remaining, base::UTF8ToUTF16(manager));
case NotificationType::kMeteredConnection:
return MessageFormatter::FormatWithNumberedArgs(
l10n_util::GetStringUTF16(
IDS_UPDATE_REQUIRED_METERED_NETWORK_MESSAGE),
days_remaining, base::UTF8ToUTF16(manager));
case NotificationType::kEolReached:
return MessageFormatter::FormatWithNumberedArgs(
l10n_util::GetStringUTF16(IDS_UPDATE_REQUIRED_EOL_MESSAGE),
days_remaining, base::UTF8ToUTF16(manager), device_type);
}
}
std::u16string GetButtonText(NotificationType type) {
switch (type) {
case NotificationType::kNoConnection:
return l10n_util::GetStringUTF16(
IDS_UPDATE_REQUIRED_SCREEN_OPEN_NETWORK_SETTINGS);
case NotificationType::kMeteredConnection:
return l10n_util::GetStringUTF16(
IDS_UPDATE_REQUIRED_SCREEN_ALLOW_METERED);
case NotificationType::kEolReached:
return l10n_util::GetStringUTF16(IDS_UPDATE_REQUIRED_EOL_SEE_DETAILS);
}
}
message_center::NotificationPriority GetNotificationPriority(
int days_remaining) {
return days_remaining > 1 ? message_center::HIGH_PRIORITY
: message_center::SYSTEM_PRIORITY;
}
message_center::SystemNotificationWarningLevel GetWarningLevel(
NotificationType type,
int days_remaining) {
if ((NotificationType::kEolReached == type && days_remaining <= 7) ||
days_remaining <= 1) {
return message_center::SystemNotificationWarningLevel::CRITICAL_WARNING;
}
return message_center::SystemNotificationWarningLevel::WARNING;
}
} // namespace
UpdateRequiredNotification::UpdateRequiredNotification() = default;
UpdateRequiredNotification::~UpdateRequiredNotification() = default;
void UpdateRequiredNotification::Show(NotificationType type,
base::TimeDelta warning_time,
const std::string& manager,
const std::u16string& device_type,
base::OnceClosure button_click_callback,
base::OnceClosure close_callback) {
const int days_remaining = warning_time.InDays();
notification_button_click_callback_ = std::move(button_click_callback);
notification_close_callback_ = std::move(close_callback);
std::u16string title = GetTitle(type, days_remaining, device_type);
std::u16string body = GetMessage(type, manager, days_remaining, device_type);
std::u16string button = GetButtonText(type);
if (title.empty() || body.empty() || button.empty()) {
NOTREACHED_IN_MIGRATION();
return;
}
DisplayNotification(title, body, button,
GetWarningLevel(type, days_remaining),
GetNotificationPriority(days_remaining));
}
void UpdateRequiredNotification::DisplayNotification(
const std::u16string& title,
const std::u16string& message,
const std::u16string& button_text,
message_center::SystemNotificationWarningLevel color_type,
message_center::NotificationPriority priority) {
message_center::RichNotificationData data;
data.buttons.push_back(message_center::ButtonInfo(button_text));
message_center::Notification notification = ash::CreateSystemNotification(
message_center::NOTIFICATION_TYPE_SIMPLE, kUpdateRequiredNotificationId,
title, message, std::u16string() /*display_source*/, GURL(),
message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
kUpdateRequiredNotificationId,
NotificationCatalogName::kUpdateRequired),
data,
base::MakeRefCounted<message_center::ThunkNotificationDelegate>(
weak_factory_.GetWeakPtr()),
vector_icons::kBusinessIcon, color_type);
notification.set_priority(priority);
SystemNotificationHelper::GetInstance()->Display(notification);
}
void UpdateRequiredNotification::Hide() {
SystemNotificationHelper::GetInstance()->Close(kUpdateRequiredNotificationId);
}
void UpdateRequiredNotification::Close(bool by_user) {
if (!notification_close_callback_.is_null())
std::move(notification_close_callback_).Run();
}
void UpdateRequiredNotification::Click(
const std::optional<int>& button_index,
const std::optional<std::u16string>& reply) {
// |button_index| may be empty if the notification body was clicked.
if (!button_index)
return;
Hide();
if (!notification_button_click_callback_.is_null())
std::move(notification_button_click_callback_).Run();
}
} // namespace ash