// Copyright 2018 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/ui/settings/google_services/google_services_settings_coordinator.h"
#import "base/apple/foundation_util.h"
#import "components/google/core/common/google_util.h"
#import "components/signin/public/identity_manager/identity_manager.h"
#import "components/strings/grit/components_strings.h"
#import "components/sync/service/sync_service.h"
#import "components/sync/service/sync_user_settings.h"
#import "ios/chrome/browser/shared/coordinator/alert/action_sheet_coordinator.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/shared/model/url/chrome_url_constants.h"
#import "ios/chrome/browser/shared/public/commands/application_commands.h"
#import "ios/chrome/browser/shared/public/commands/browser_commands.h"
#import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
#import "ios/chrome/browser/shared/public/commands/open_new_tab_command.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/snackbar_commands.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/ui/table_view/table_view_utils.h"
#import "ios/chrome/browser/signin/model/authentication_service.h"
#import "ios/chrome/browser/signin/model/authentication_service_factory.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/ui/authentication/authentication_flow.h"
#import "ios/chrome/browser/ui/authentication/authentication_ui_util.h"
#import "ios/chrome/browser/ui/authentication/signout_action_sheet/signout_action_sheet_coordinator.h"
#import "ios/chrome/browser/ui/settings/google_services/google_services_settings_command_handler.h"
#import "ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.h"
#import "ios/chrome/browser/ui/settings/google_services/parcel_tracking_settings_coordinator.h"
#import "ios/chrome/browser/ui/settings/sync/sync_encryption_passphrase_table_view_controller.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ui/base/l10n/l10n_util.h"
using signin_metrics::PromoAction;
@interface GoogleServicesSettingsCoordinator () <
GoogleServicesSettingsCommandHandler,
GoogleServicesSettingsViewControllerPresentationDelegate,
SignoutActionSheetCoordinatorDelegate>
// Google services settings mediator.
@property(nonatomic, strong) GoogleServicesSettingsMediator* mediator;
// Returns the authentication service.
@property(nonatomic, assign, readonly) AuthenticationService* authService;
// Manages the authentication flow for a given identity.
@property(nonatomic, strong) AuthenticationFlow* authenticationFlow;
// Manages user's Google identities.
@property(nonatomic, assign, readonly) signin::IdentityManager* identityManager;
// View controller presented by this coordinator.
@property(nonatomic, strong, readonly)
GoogleServicesSettingsViewController* googleServicesSettingsViewController;
// Action sheets that provides options for sign out.
@property(nonatomic, strong) ActionSheetCoordinator* signOutCoordinator;
@property(nonatomic, strong)
SignoutActionSheetCoordinator* signoutActionSheetCoordinator;
@end
@implementation GoogleServicesSettingsCoordinator {
ParcelTrackingSettingsCoordinator* _parcelTrackingSettingsCoordinator;
}
@synthesize baseNavigationController = _baseNavigationController;
- (instancetype)initWithBaseNavigationController:
(UINavigationController*)navigationController
browser:(Browser*)browser {
self = [super initWithBaseViewController:navigationController
browser:browser];
if (self) {
_baseNavigationController = navigationController;
}
return self;
}
- (void)start {
GoogleServicesSettingsViewController* viewController =
[[GoogleServicesSettingsViewController alloc]
initWithStyle:ChromeTableViewStyle()];
viewController.presentationDelegate = self;
viewController.forcedSigninEnabled =
self.authService->GetServiceStatus() ==
AuthenticationService::ServiceStatus::SigninForcedByPolicy;
self.viewController = viewController;
self.mediator = [[GoogleServicesSettingsMediator alloc]
initWithIdentityManager:IdentityManagerFactory::GetForBrowserState(
self.browser->GetBrowserState())
userPrefService:self.browser->GetBrowserState()->GetPrefs()
localPrefService:GetApplicationContext()->GetLocalState()];
self.mediator.consumer = viewController;
self.mediator.authService = self.authService;
self.mediator.commandHandler = self;
viewController.modelDelegate = self.mediator;
viewController.serviceDelegate = self.mediator;
CommandDispatcher* dispatcher = self.browser->GetCommandDispatcher();
viewController.applicationHandler =
HandlerForProtocol(dispatcher, ApplicationCommands);
viewController.browserHandler =
HandlerForProtocol(dispatcher, BrowserCommands);
viewController.settingsHandler =
HandlerForProtocol(dispatcher, SettingsCommands);
viewController.snackbarHandler =
HandlerForProtocol(dispatcher, SnackbarCommands);
DCHECK(self.baseNavigationController);
[self.baseNavigationController pushViewController:self.viewController
animated:YES];
}
- (void)stop {
[self.signOutCoordinator stop];
_signOutCoordinator = nil;
[_parcelTrackingSettingsCoordinator stop];
_parcelTrackingSettingsCoordinator = nil;
}
#pragma mark - Private
- (void)authenticationFlowDidComplete {
DCHECK(self.authenticationFlow);
self.authenticationFlow = nil;
[self.googleServicesSettingsViewController allowUserInteraction];
}
#pragma mark - Properties
- (AuthenticationService*)authService {
return AuthenticationServiceFactory::GetForBrowserState(
self.browser->GetBrowserState());
}
- (GoogleServicesSettingsViewController*)googleServicesSettingsViewController {
return base::apple::ObjCCast<GoogleServicesSettingsViewController>(
self.viewController);
}
- (signin::IdentityManager*)identityManager {
return IdentityManagerFactory::GetForBrowserState(
self.browser->GetBrowserState());
}
#pragma mark - GoogleServicesSettingsCommandHandler
- (void)showSignOutFromTargetRect:(CGRect)targetRect
completion:(signin_ui::CompletionCallback)completion {
DCHECK(completion);
syncer::SyncService* syncService =
SyncServiceFactory::GetForBrowserState(self.browser->GetBrowserState());
BOOL isSyncConsentGiven =
syncService &&
syncService->GetUserSettings()->IsInitialSyncFeatureSetupComplete();
BOOL shouldClearDataOnSignOut =
self.authService->ShouldClearDataForSignedInPeriodOnSignOut();
self.signOutCoordinator = [[ActionSheetCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
title:nil
message:nil
rect:targetRect
view:self.viewController.view];
// Because setting `title` to nil automatically forces the title-style text on
// `message` in the UIAlertController, the attributed message below
// specifically denotes the font style to apply.
if (isSyncConsentGiven) {
self.signOutCoordinator.attributedMessage = [[NSAttributedString alloc]
initWithString:l10n_util::GetNSString(
IDS_IOS_SIGNOUT_DIALOG_MESSAGE_WITH_SYNC)
attributes:@{
NSFontAttributeName :
[UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline]
}];
[self.signOutCoordinator updateAttributedText];
} else if (shouldClearDataOnSignOut) {
self.signOutCoordinator.attributedMessage = [[NSAttributedString alloc]
initWithString:
l10n_util::GetNSString(
IDS_IOS_SIGNOUT_AND_DISALLOW_SIGNIN_CLEARS_DATA_MESSAGE_WITH_MANAGED_ACCOUNT)
attributes:@{
NSFontAttributeName :
[UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline]
}];
[self.signOutCoordinator updateAttributedText];
}
__weak GoogleServicesSettingsCoordinator* weakSelf = self;
[self.signOutCoordinator
addItemWithTitle:l10n_util::GetNSString(
IDS_IOS_SIGNOUT_DIALOG_SIGN_OUT_BUTTON)
action:^{
if (!weakSelf) {
return;
}
// Provide additional data retention options if the user is
// syncing their data.
// TODO(crbug.com/40066949): Simplify once kSync becomes
// unreachable or is deleted from the codebase. See
// ConsentLevel::kSync documentation for details.
if (weakSelf.identityManager->HasPrimaryAccount(
signin::ConsentLevel::kSync)) {
[weakSelf
showDataRetentionOptionsWithTargetRect:targetRect
completion:completion];
return;
}
[weakSelf signOutWithCompletion:completion];
}
style:UIAlertActionStyleDestructive];
[self.signOutCoordinator
addItemWithTitle:l10n_util::GetNSString(IDS_CANCEL)
action:^{
weakSelf.signOutCoordinator = nil;
completion(NO);
}
style:UIAlertActionStyleCancel];
[self.signOutCoordinator start];
}
- (void)showParcelTrackingSettingsPage {
_parcelTrackingSettingsCoordinator =
[[ParcelTrackingSettingsCoordinator alloc]
initWithBaseNavigationController:_baseNavigationController
browser:self.browser];
[_parcelTrackingSettingsCoordinator start];
}
// Displays the option to keep or clear data for a syncing user.
- (void)showDataRetentionOptionsWithTargetRect:(CGRect)targetRect
completion:(signin_ui::CompletionCallback)
completion {
DCHECK(completion);
self.signoutActionSheetCoordinator = [[SignoutActionSheetCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
rect:targetRect
view:self.viewController.view
withSource:signin_metrics::ProfileSignout::
kUserClickedSignoutSettings];
__weak GoogleServicesSettingsCoordinator* weakSelf = self;
self.signoutActionSheetCoordinator.delegate = self;
self.signoutActionSheetCoordinator.completion = ^(BOOL success) {
if (completion)
completion(success);
[weakSelf.signoutActionSheetCoordinator stop];
weakSelf.signoutActionSheetCoordinator = nil;
};
[self.signoutActionSheetCoordinator start];
}
// Signs the user out of Chrome, only clears data for managed accounts.
- (void)signOutWithCompletion:(signin_ui::CompletionCallback)completion {
DCHECK(completion);
[self.googleServicesSettingsViewController preventUserInteraction];
__weak GoogleServicesSettingsCoordinator* weakSelf = self;
self.authService->SignOut(
signin_metrics::ProfileSignout::kUserClickedSignoutSettings,
/*force_clear_browsing_data=*/NO, ^{
if (!weakSelf) {
return;
}
[weakSelf.googleServicesSettingsViewController allowUserInteraction];
completion(YES);
});
}
#pragma mark - GoogleServicesSettingsViewControllerPresentationDelegate
- (void)googleServicesSettingsViewControllerDidRemove:
(GoogleServicesSettingsViewController*)controller {
DCHECK_EQ(self.viewController, controller);
[self.delegate googleServicesSettingsCoordinatorDidRemove:self];
}
#pragma mark - SignoutActionSheetCoordinatorDelegate
- (void)signoutActionSheetCoordinatorPreventUserInteraction:
(SignoutActionSheetCoordinator*)coordinator {
[self.googleServicesSettingsViewController preventUserInteraction];
}
- (void)signoutActionSheetCoordinatorAllowUserInteraction:
(SignoutActionSheetCoordinator*)coordinator {
[self.googleServicesSettingsViewController allowUserInteraction];
}
@end