blob: 5d73a1ba1002193beccc5ef8b7ae051a6032c8bb [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/ui/promos/ios_promos_utils.h"
#include "chrome/browser/feature_engagement/tracker_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/promos/promos_types.h"
#include "chrome/browser/promos/promos_utils.h"
#include "chrome/browser/segmentation_platform/segmentation_platform_service_factory.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/actions/chrome_action_id.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
#include "chrome/browser/ui/views/promos/ios_promo_bubble.h"
#include "components/feature_engagement/public/feature_constants.h"
#include "components/segmentation_platform/embedder/default_model/device_switcher_model.h"
#include "components/segmentation_platform/public/constants.h"
#include "components/segmentation_platform/public/segmentation_platform_service.h"
#include "components/sync/service/sync_service.h"
namespace {
// ShowIOSDesktopPromoBubble shows the iOS Desktop Promo Bubble based on the
// given promo type.
void ShowIOSDesktopPromoBubble(IOSPromoType promo_type,
Profile* profile,
ToolbarButtonProvider* toolbar_button_provider) {
switch (promo_type) {
case IOSPromoType::kPassword:
IOSPromoBubble::ShowPromoBubble(
toolbar_button_provider->GetAnchorView(
kActionShowPasswordsBubbleOrPage),
toolbar_button_provider->GetPageActionIconView(
PageActionIconType::kManagePasswords),
profile, IOSPromoType::kPassword);
break;
case IOSPromoType::kAddress:
IOSPromoBubble::ShowPromoBubble(
toolbar_button_provider->GetAnchorView(
kActionShowAddressesBubbleOrPage),
toolbar_button_provider->GetPageActionIconView(
PageActionIconType::kAutofillAddress),
profile, IOSPromoType::kAddress);
break;
case IOSPromoType::kPayment:
IOSPromoBubble::ShowPromoBubble(
toolbar_button_provider->GetAnchorView(
kActionShowPaymentsBubbleOrPage),
toolbar_button_provider->GetPageActionIconView(
PageActionIconType::kSaveCard),
profile, IOSPromoType::kPayment);
break;
}
}
// Runs a callback if it isn't null.
void RunCallback(std::optional<base::OnceClosure> callback) {
if (callback) {
(*std::exchange(callback, std::nullopt)).Run();
}
}
// OnIOSPromoClassificationResult takes the result of the segmentation platform
// and computes, along with other criteria like impressions, whether the user
// should be shown the promo. If yes, attempts to show the promo.
void OnIOSPromoClassificationResult(
IOSPromoType promo_type,
base::WeakPtr<Browser> browser,
std::optional<base::OnceClosure> promo_will_be_shown_callback,
std::optional<base::OnceClosure> promo_not_shown_callback,
const segmentation_platform::ClassificationResult& result) {
if (!browser) {
RunCallback(std::move(promo_not_shown_callback));
return;
}
feature_engagement::Tracker* tracker =
feature_engagement::TrackerFactory::GetForBrowserContext(
browser->profile());
if (promos_utils::UserNotClassifiedAsMobileDeviceSwitcher(result) &&
tracker->ShouldTriggerHelpUI(
promos_utils::GetIOSDesktopPromoFeatureEngagement(promo_type))) {
RunCallback(std::move(promo_will_be_shown_callback));
promos_utils::IOSDesktopPromoShown(browser->profile(), promo_type);
ShowIOSDesktopPromoBubble(
promo_type, browser->profile(),
browser->GetBrowserView().toolbar_button_provider());
return;
}
RunCallback(std::move(promo_not_shown_callback));
}
void VerifyIOSPromoEligibilityCriteriaAsync(
const IOSPromoType& promo_type,
Browser* browser,
std::optional<base::OnceClosure> promo_will_be_shown_callback =
std::nullopt,
std::optional<base::OnceClosure> promo_not_shown_callback = std::nullopt) {
const syncer::SyncService* sync_service =
SyncServiceFactory::GetForProfile(browser->profile());
// Verify that the user is currently syncing their preferences, hasn't
// exceeded their impression limit, is not in the cooldown period or has not
// opted-out from seeing the promo.
if (sync_service && promos_utils::ShouldShowIOSDesktopPromo(
browser->profile(), sync_service, promo_type)) {
auto input_context =
base::MakeRefCounted<segmentation_platform::InputContext>();
input_context->metadata_args.emplace(
"active_days_limit", promos_utils::kiOSDesktopPromoLookbackWindow);
input_context->metadata_args.emplace(
"wait_for_device_info_in_seconds",
segmentation_platform::processing::ProcessedValue(0));
segmentation_platform::PredictionOptions options;
options.on_demand_execution = true;
// Get segmentation platform classification results and pass callback.
segmentation_platform::SegmentationPlatformServiceFactory::GetForProfile(
browser->profile())
->GetClassificationResult(
segmentation_platform::kDeviceSwitcherKey, options, input_context,
base::BindOnce(&OnIOSPromoClassificationResult, promo_type,
browser->AsWeakPtr(),
std::move(promo_will_be_shown_callback),
std::move(promo_not_shown_callback)));
return;
}
RunCallback(std::move(promo_not_shown_callback));
}
} // namespace
namespace ios_promos_utils {
void VerifyIOSPromoEligibility(IOSPromoType promo_type, Browser* browser) {
VerifyIOSPromoEligibilityCriteriaAsync(promo_type, browser);
}
void MaybeOverrideCardConfirmationBubbleWithIOSPaymentPromo(
Browser* browser,
base::OnceClosure promo_will_be_shown_callback,
base::OnceClosure promo_not_shown_callback) {
VerifyIOSPromoEligibilityCriteriaAsync(
IOSPromoType::kPayment, browser, std::move(promo_will_be_shown_callback),
std::move(promo_not_shown_callback));
}
} // namespace ios_promos_utils