blob: ec6e79ee9b36739bb79365f527f6df764c0d5b85 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/tips_notifications/model/tips_notification_presenter.h"
#import "base/functional/bind.h"
#import "base/functional/callback_helpers.h"
#import "base/metrics/histogram_functions.h"
#import "components/signin/public/identity_manager/identity_manager.h"
#import "components/sync/service/sync_service.h"
#import "components/sync/service/sync_service_utils.h"
#import "components/sync/service/sync_user_settings.h"
#import "ios/chrome/browser/authentication/ui_bundled/signin_presenter.h"
#import "ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_commands.h"
#import "ios/chrome/browser/default_browser/model/promo_source.h"
#import "ios/chrome/browser/default_browser/model/utils.h"
#import "ios/chrome/browser/settings/model/sync/utils/sync_presenter.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/public/commands/application_commands.h"
#import "ios/chrome/browser/shared/public/commands/browser_coordinator_commands.h"
#import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
#import "ios/chrome/browser/shared/public/commands/credential_provider_promo_commands.h"
#import "ios/chrome/browser/shared/public/commands/docking_promo_commands.h"
#import "ios/chrome/browser/shared/public/commands/settings_commands.h"
#import "ios/chrome/browser/shared/public/commands/show_signin_command.h"
#import "ios/chrome/browser/shared/public/commands/whats_new_commands.h"
#import "ios/chrome/browser/signin/model/identity_manager_factory.h"
#import "ios/chrome/browser/sync/model/sync_service_factory.h"
#import "ios/chrome/browser/tips_notifications/model/tips_notification_criteria.h"
#import "ios/chrome/browser/tips_notifications/model/utils.h"
TipsNotificationPresenter::TipsNotificationPresenter(Browser* browser)
: browser_(browser) {}
void TipsNotificationPresenter::Present(base::WeakPtr<Browser> weakBrowser,
TipsNotificationType type) {
Browser* browser = weakBrowser.get();
if (!browser) {
return;
}
id<ApplicationCommands> application_handler =
HandlerForProtocol(browser->GetCommandDispatcher(), ApplicationCommands);
auto show_ui_callback = base::CallbackToBlock(base::BindOnce(
&TipsNotificationPresenter::PresentInternal, weakBrowser, type));
[application_handler
prepareToPresentModalWithSnackbarDismissal:NO
completion:show_ui_callback];
}
void TipsNotificationPresenter::PresentInternal(
base::WeakPtr<Browser> weakBrowser,
TipsNotificationType type) {
Browser* browser = weakBrowser.get();
if (!browser) {
return;
}
TipsNotificationPresenter presenter(browser);
presenter.Present(type);
}
void TipsNotificationPresenter::Present(TipsNotificationType type) {
switch (type) {
case TipsNotificationType::kDefaultBrowser:
ShowDefaultBrowserPromo();
break;
case TipsNotificationType::kWhatsNew:
ShowWhatsNew();
break;
case TipsNotificationType::kSignin:
ShowSignin();
break;
case TipsNotificationType::kSetUpListContinuation:
ShowSetUpListContinuation();
break;
case TipsNotificationType::kDocking:
ShowDocking();
break;
case TipsNotificationType::kOmniboxPosition:
ShowOmniboxPosition();
break;
case TipsNotificationType::kLens:
ShowLensPromo();
break;
case TipsNotificationType::kEnhancedSafeBrowsing:
ShowEnhancedSafeBrowsingPromo();
break;
case TipsNotificationType::kCPE:
ShowCPEPromo();
break;
case TipsNotificationType::kLensOverlay:
ShowLensOverlayPromo();
break;
case TipsNotificationType::kTrustedVaultKeyRetrieval:
StartTrustedVaultKeyRetrievalFlow();
break;
case TipsNotificationType::kIncognitoLock:
case TipsNotificationType::kError:
NOTREACHED();
}
}
void TipsNotificationPresenter::ShowDefaultBrowserPromo() {
id<SettingsCommands> settings_handler =
HandlerForProtocol(browser_->GetCommandDispatcher(), SettingsCommands);
[settings_handler
showDefaultBrowserSettingsFromViewController:nil
sourceForUMA:
DefaultBrowserSettingsPageSource::
kTipsNotification];
}
void TipsNotificationPresenter::ShowWhatsNew() {
[HandlerForProtocol(browser_->GetCommandDispatcher(), WhatsNewCommands)
showWhatsNew];
}
void TipsNotificationPresenter::ShowSignin() {
// The user may have signed in between when the notification was requested
// and when it triggered. If the user can no longer sign in, then open
// the account settings.
if (!TipsNotificationCriteria::CanSignIn(browser_->GetProfile())) {
[HandlerForProtocol(browser_->GetCommandDispatcher(), SettingsCommands)
showAccountsSettingsFromViewController:nil
skipIfUINotAvailable:NO];
return;
}
// If there are 0 identities, kInstantSignin requires less taps.
AuthenticationOperation operation =
HasIdentitiesOnDevice() ? AuthenticationOperation::kSigninOnly
: AuthenticationOperation::kInstantSignin;
ShowSigninCommand* command = [[ShowSigninCommand alloc]
initWithOperation:operation
identity:nil
accessPoint:signin_metrics::AccessPoint::kTipsNotification
promoAction:signin_metrics::PromoAction::
PROMO_ACTION_NO_SIGNIN_PROMO
completion:nil];
[HandlerForProtocol(browser_->GetCommandDispatcher(), SigninPresenter)
showSignin:command];
}
void TipsNotificationPresenter::ShowSetUpListContinuation() {
[HandlerForProtocol(browser_->GetCommandDispatcher(),
ContentSuggestionsCommands)
showSetUpListSeeMoreMenuExpanded:YES];
}
void TipsNotificationPresenter::ShowDocking() {
[HandlerForProtocol(browser_->GetCommandDispatcher(), DockingPromoCommands)
showDockingPromo:YES];
}
void TipsNotificationPresenter::ShowOmniboxPosition() {
[HandlerForProtocol(browser_->GetCommandDispatcher(),
BrowserCoordinatorCommands) showOmniboxPositionChoice];
}
void TipsNotificationPresenter::ShowLensPromo() {
[HandlerForProtocol(browser_->GetCommandDispatcher(),
BrowserCoordinatorCommands) showLensPromo];
}
void TipsNotificationPresenter::StartTrustedVaultKeyRetrievalFlow() {
const char* metric_name =
"IOS.PasswordManager.TrustedVaultNotification.Events";
syncer::SyncService* sync_service =
SyncServiceFactory::GetForProfileIfExists(browser_->GetProfile());
if (sync_service == nullptr) {
// Sync service might be not available for some profiles. If Sync service is
// not available, we don't need to do anything.
//
// For example, this code branch might be activated in the
// following scenario:
// 1) The tips notification has been displayed for a user without a
// trusted vault key.
// 2) The user signs-out (and possibly signs-in to some other profile).
// 3) Afterwards the user notices the tips notification and clicks "Open".
// 4) Since the profile from the step 2 is not guaranteed to have a Sync
// service, we need to check if sync_service exists.
//
// We expect that the described scenario is rare.
base::UmaHistogramEnumeration(
metric_name,
TrustedVaultNotificationEvents::kSyncServiceDoesNotExistForProfile);
return;
}
if (!sync_service->GetUserSettings()
->IsTrustedVaultKeyRequiredForPreferredDataTypes()) {
// The trusted vault key is already available, so we don't need to do
// anything.
//
// For example, this code branch might be activated in the
// following scenario:
// 1) The tips notification has been displayed for a user without a
// trusted vault key.
// 2) The user fixed the issue using some in-browser UI (e.g. via the
// Password Manager settings UI).
// 3) Afterwards the user notices the tips notification and clicks "Open".
// 4) Since the key has been retrieved in the step 2 we don't need to
// perform the key retrieval anymore.
//
// We expect that the described scenario is rare.
base::UmaHistogramEnumeration(
metric_name,
TrustedVaultNotificationEvents::kTrustedVaultKeyAlreadyAvailable);
return;
}
[HandlerForProtocol(browser_->GetCommandDispatcher(),
BrowserCoordinatorCommands)
performReauthToRetrieveTrustedVaultKey:
syncer::TrustedVaultUserActionTriggerForUMA::kNotification];
base::UmaHistogramEnumeration(
metric_name, TrustedVaultNotificationEvents::kKeyRetrievalFlowStarted);
}
void TipsNotificationPresenter::ShowEnhancedSafeBrowsingPromo() {
[HandlerForProtocol(browser_->GetCommandDispatcher(),
BrowserCoordinatorCommands)
showEnhancedSafeBrowsingPromo];
}
void TipsNotificationPresenter::ShowCPEPromo() {
[HandlerForProtocol(browser_->GetCommandDispatcher(),
CredentialProviderPromoCommands)
showCredentialProviderPromoWithTrigger:CredentialProviderPromoTrigger::
TipsNotification];
}
void TipsNotificationPresenter::ShowLensOverlayPromo() {
[HandlerForProtocol(browser_->GetCommandDispatcher(),
BrowserCoordinatorCommands) showSearchWhatYouSeePromo];
}
bool TipsNotificationPresenter::HasIdentitiesOnDevice() {
return !IdentityManagerFactory::GetForProfile(browser_->GetProfile())
->GetAccountsOnDevice()
.empty();
}