blob: 5bc65bad856398340a9f36f1dd82ec34f6bb4a43 [file] [log] [blame]
// Copyright 2024 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/growth/show_notification_action_performer.h"
#include <memory>
#include <string>
#include <vector>
#include "ash/constants/notifier_catalogs.h"
#include "ash/public/cpp/notification_utils.h"
#include "ash/system/notification_center/message_view_factory.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/ash/growth/metrics.h"
#include "chromeos/ash/components/growth/action_performer.h"
#include "chromeos/ash/components/growth/campaigns_manager.h"
#include "chromeos/ash/components/growth/campaigns_model.h"
#include "chromeos/ash/components/growth/growth_metrics.h"
#include "ui/gfx/vector_icon_utils.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/public/cpp/notification.h"
#include "ui/message_center/public/cpp/notification_delegate.h"
#include "url/gurl.h"
namespace {
constexpr char kTitlePath[] = "title";
constexpr char kMessagePath[] = "message";
constexpr char kIconPath[] = "icon";
constexpr char kButtonsPath[] = "buttons";
constexpr char kLabelPath[] = "label";
constexpr char kActionPath[] = "action";
constexpr char kNotificationIdTemplate[] = "growth_campaign_%d";
struct ShowNotificationParams {
std::string title;
std::string message;
raw_ptr<const gfx::VectorIcon> icon = nullptr;
std::vector<message_center::ButtonInfo> buttons_info;
};
std::unique_ptr<ShowNotificationParams>
ParseShowNotificationActionPerformerParams(const base::Value::Dict* params) {
if (!params) {
LOG(ERROR) << "Empty parameter to ShowNotificationActionPerformer.";
return nullptr;
}
auto show_notification_params = std::make_unique<ShowNotificationParams>();
const auto* title = params->FindString(kTitlePath);
const auto* message = params->FindString(kMessagePath);
show_notification_params->title = title ? *title : std::string();
show_notification_params->message = message ? *message : std::string();
// Set icons if available.
const auto* icon_value = params->FindDict(kIconPath);
if (!icon_value) {
// TODO: b/331633771 - Consider adding default icon for notification.
growth::RecordCampaignsManagerError(
growth::CampaignsManagerError::kNotificationPayloadMissingIcon);
LOG(ERROR) << "icon is required for notification.";
return nullptr;
}
const auto* icon = growth::Image(icon_value).GetVectorIcon();
if (!icon) {
growth::RecordCampaignsManagerError(
growth::CampaignsManagerError::kNotificationPayloadInvalidIcon);
return nullptr;
}
show_notification_params->icon = icon;
// Set buttons info.
const auto* buttons = params->FindList(kButtonsPath);
if (buttons) {
for (auto button_it = buttons->begin(); button_it != buttons->end();
button_it++) {
if (!button_it->is_dict()) {
growth::RecordCampaignsManagerError(
growth::CampaignsManagerError::kNotificationPayloadInvalidButton);
continue;
}
auto* const label = button_it->GetDict().FindString(kLabelPath);
if (!label) {
growth::RecordCampaignsManagerError(
growth::CampaignsManagerError::
kNotificationPayloadMissingButtonLabel);
continue;
}
show_notification_params->buttons_info.emplace_back(
base::UTF8ToUTF16(*label));
}
}
return show_notification_params;
}
} // namespace
ShowNotificationActionPerformer::ShowNotificationActionPerformer() = default;
ShowNotificationActionPerformer::~ShowNotificationActionPerformer() = default;
void ShowNotificationActionPerformer::Run(
int campaign_id,
const base::Value::Dict* params,
growth::ActionPerformer::Callback callback) {
auto show_notification_params =
ParseShowNotificationActionPerformerParams(params);
if (!show_notification_params) {
growth::RecordCampaignsManagerError(
growth::CampaignsManagerError::kInvalidNotificationPayload);
std::move(callback).Run(growth::ActionResult::kFailure,
growth::ActionResultReason::kParsingActionFailed);
return;
}
message_center::RichNotificationData optional_fields;
optional_fields.buttons = std::move(show_notification_params->buttons_info);
auto id = base::StringPrintf(kNotificationIdTemplate, campaign_id);
std::unique_ptr<message_center::Notification> notification =
ash::CreateSystemNotificationPtr(
message_center::NOTIFICATION_TYPE_SIMPLE, id,
base::UTF8ToUTF16(show_notification_params->title),
base::UTF8ToUTF16(show_notification_params->message),
/*display_source=*/std::u16string(),
/*origin_url=*/GURL(),
message_center::NotifierId(
message_center::NotifierType::SYSTEM_COMPONENT, id,
ash::NotificationCatalogName::kGrowthFramework),
optional_fields,
base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
base::BindRepeating(
&ShowNotificationActionPerformer::HandleNotificationClicked,
weak_ptr_factory_.GetWeakPtr(), params, id, campaign_id)),
*show_notification_params->icon,
message_center::SystemNotificationWarningLevel::NORMAL);
auto* message_center = message_center::MessageCenter::Get();
CHECK(message_center);
message_center->RemoveNotification(notification->id(),
/*by_user=*/false);
message_center->AddNotification(std::move(notification));
NotifyReadyToLogImpression(campaign_id);
std::move(callback).Run(growth::ActionResult::kSuccess,
/*action_result_reason=*/std::nullopt);
}
growth::ActionType ShowNotificationActionPerformer::ActionType() const {
return growth::ActionType::kShowNotification;
}
void ShowNotificationActionPerformer::HandleNotificationClicked(
const base::Value::Dict* params,
const std::string& notification_id,
int campaign_id,
std::optional<int> button_index) {
if (!button_index) {
// Notification message body clicked.
return;
}
auto button_index_value = button_index.value();
auto button_id = CampaignButtonId::kOthers;
if (button_index_value == 0) {
button_id = CampaignButtonId::kPrimary;
} else if (button_index_value == 1) {
button_id = CampaignButtonId::kSecondary;
}
NotifyButtonPressed(campaign_id, button_id, /*should_mark_dismissed=*/true);
const auto* buttons_value = params->FindList(kButtonsPath);
CHECK(buttons_value);
const auto& button_value = (*buttons_value)[button_index_value];
if (!button_value.is_dict()) {
LOG(ERROR) << "Invalid button payload.";
}
const auto* action_value = button_value.GetDict().FindDict(kActionPath);
if (!action_value) {
growth::RecordCampaignsManagerError(
growth::CampaignsManagerError::kNotificationPayloadMissingButtonAction);
LOG(ERROR) << "Missing action.";
return;
}
auto action = growth::Action(action_value);
if (action.GetActionType() == growth::ActionType::kDismiss) {
message_center::MessageCenter::Get()->RemoveNotification(
notification_id, false /* by_user */);
return;
}
auto* campaigns_manager = growth::CampaignsManager::Get();
CHECK(campaigns_manager);
campaigns_manager->PerformAction(campaign_id, &action);
}