// Copyright 2023 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/search_engine_choice/search_engine_choice_coordinator.h"
#import "base/check_op.h"
#import "base/time/time.h"
#import "components/search_engines/search_engine_choice/search_engine_choice_utils.h"
#import "components/search_engines/search_engines_switches.h"
#import "components/strings/grit/components_strings.h"
#import "ios/chrome/app/application_delegate/app_state.h"
#import "ios/chrome/browser/first_run/ui_bundled/first_run_screen_delegate.h"
#import "ios/chrome/browser/search_engine_choice/model/search_engine_choice_util.h"
#import "ios/chrome/browser/search_engines/model/search_engine_choice_service_factory.h"
#import "ios/chrome/browser/search_engines/model/template_url_service_factory.h"
#import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/ui/scoped_iphone_portrait_only/scoped_iphone_portrait_only.h"
#import "ios/chrome/browser/ui/search_engine_choice/search_engine_choice_constants.h"
#import "ios/chrome/browser/ui/search_engine_choice/search_engine_choice_learn_more/search_engine_choice_learn_more_coordinator.h"
#import "ios/chrome/browser/ui/search_engine_choice/search_engine_choice_learn_more/search_engine_choice_learn_more_view_controller.h"
#import "ios/chrome/browser/ui/search_engine_choice/search_engine_choice_mediator.h"
#import "ios/chrome/browser/ui/search_engine_choice/search_engine_choice_view_controller.h"
#import "ui/base/device_form_factor.h"
#import "ui/base/l10n/l10n_util_mac.h"
@interface SearchEngineChoiceCoordinator () <
SearchEngineChoiceActionDelegate,
SearchEngineChoiceLearnMoreCoordinatorDelegate>
@end
@implementation SearchEngineChoiceCoordinator {
// The mediator that fetches the list of search engines.
SearchEngineChoiceMediator* _mediator;
// The view controller for the search engines.
SearchEngineChoiceViewController* _viewController;
// Coordinator for the informational popup that may be displayed to the user.
SearchEngineChoiceLearnMoreCoordinator*
_searchEngineChoiceLearnMoreCoordinator;
// Whether the screen is being shown in the FRE.
BOOL _firstRun;
// Whether the primary account button was already tapped.
BOOL _didTapPrimaryButton;
// Timestamp of the previous call to `-(void)_didTapPrimaryButton`.
base::Time _lastCallToDidTapPrimaryButtonTimestamp;
// First run screen delegate.
__weak id<FirstRunScreenDelegate> _firstRunDelegate;
// Force iPhone to be in portrait only for this coordinator.
std::unique_ptr<ScopedIphonePortraitOnly> _scopedIphonePortraitOnly;
}
@synthesize baseNavigationController = _baseNavigationController;
- (instancetype)initWithBaseViewController:(UIViewController*)viewController
browser:(Browser*)browser {
self = [super initWithBaseViewController:viewController browser:browser];
if (self) {
_firstRun = NO;
_didTapPrimaryButton = NO;
}
return self;
}
- (instancetype)initForFirstRunWithBaseNavigationController:
(UINavigationController*)navigationController
browser:(Browser*)browser
firstRunDelegate:
(id<FirstRunScreenDelegate>)
delegate {
self = [self initWithBaseViewController:navigationController browser:browser];
if (self) {
_baseNavigationController = navigationController;
_firstRun = YES;
_firstRunDelegate = delegate;
}
return self;
}
- (void)start {
[super start];
// Make sure we use the original browser state (non-incognito).
ChromeBrowserState* originalBrowserState =
self.browser->GetBrowserState()->GetOriginalChromeBrowserState();
const search_engines::ChoicePromo choicePromo =
_firstRun ? search_engines::ChoicePromo::kFre
: search_engines::ChoicePromo::kDialog;
if (!ShouldDisplaySearchEngineChoiceScreen(
*originalBrowserState, choicePromo,
/*app_started_via_external_intent=*/false)) {
// If the search engine enterprise pocliy has been loaded, just before to
// open the Search Engine Choice dialog, it should be skipped.
[self dismissChoiceScreen];
return;
}
_viewController =
[[SearchEngineChoiceViewController alloc] initWithFirstRunMode:_firstRun];
_viewController.actionDelegate = self;
TemplateURLService* templateURLService =
ios::TemplateURLServiceFactory::GetForBrowserState(originalBrowserState);
search_engines::SearchEngineChoiceService* searchEngineChoiceService =
ios::SearchEngineChoiceServiceFactory::GetForBrowserState(
originalBrowserState);
_mediator = [[SearchEngineChoiceMediator alloc]
initWithTemplateURLService:templateURLService
searchEngineChoiceService:searchEngineChoiceService];
_mediator.consumer = _viewController;
_viewController.mutator = _mediator;
_viewController.modalInPresentation = YES;
if (_firstRun) {
BOOL animated = self.baseNavigationController.topViewController != nil;
[self.baseNavigationController setViewControllers:@[ _viewController ]
animated:animated];
search_engines::RecordChoiceScreenEvent(
search_engines::SearchEngineChoiceScreenEvents::
kFreChoiceScreenWasDisplayed);
} else {
ui::DeviceFormFactor deviceFormFactor = ui::GetDeviceFormFactor();
if (deviceFormFactor == ui::DEVICE_FORM_FACTOR_PHONE) {
AppState* appState = self.browser->GetSceneState().appState;
_scopedIphonePortraitOnly =
std::make_unique<ScopedIphonePortraitOnly>(appState);
} else {
_viewController.modalPresentationStyle = UIModalPresentationFormSheet;
_viewController.preferredContentSize =
CGSizeMake(kIPadSearchEngineChoiceScreenPreferredWidth,
kIPadSearchEngineChoiceScreenPreferredHeight);
}
[self.baseViewController presentViewController:_viewController
animated:YES
completion:nil];
search_engines::RecordChoiceScreenEvent(
search_engines::SearchEngineChoiceScreenEvents::
kChoiceScreenWasDisplayed);
}
}
- (void)stop {
if (!_firstRun) {
[_viewController.presentingViewController
dismissViewControllerAnimated:YES
completion:nil];
}
[_searchEngineChoiceLearnMoreCoordinator stop];
_searchEngineChoiceLearnMoreCoordinator = nil;
[_mediator disconnect];
_mediator.consumer = nil;
_mediator = nil;
_viewController.mutator = nil;
_viewController = nil;
_baseNavigationController = nil;
_firstRunDelegate = nil;
_scopedIphonePortraitOnly.reset();
[super stop];
}
#pragma mark - SearchEngineChoiceViewControllerDelegate
- (void)showLearnMore {
_searchEngineChoiceLearnMoreCoordinator =
[[SearchEngineChoiceLearnMoreCoordinator alloc]
initWithBaseViewController:_viewController
browser:self.browser];
_searchEngineChoiceLearnMoreCoordinator.forcePresentationFormSheet =
_firstRun;
_searchEngineChoiceLearnMoreCoordinator.delegate = self;
[_searchEngineChoiceLearnMoreCoordinator start];
if (_firstRun) {
search_engines::RecordChoiceScreenEvent(
search_engines::SearchEngineChoiceScreenEvents::
kFreLearnMoreWasDisplayed);
} else {
search_engines::RecordChoiceScreenEvent(
search_engines::SearchEngineChoiceScreenEvents::kLearnMoreWasDisplayed);
}
}
- (void)didTapPrimaryButton {
if (_didTapPrimaryButton) {
NOTREACHED(base::NotFatalUntil::M127)
<< "Double tap on primary button [_firstRun = " << _firstRun
<< " ; delay : "
<< (base::Time::Now() - _lastCallToDidTapPrimaryButtonTimestamp)
.InMilliseconds()
<< " ms]";
return;
}
_didTapPrimaryButton = YES;
_lastCallToDidTapPrimaryButtonTimestamp = base::Time::Now();
if (_firstRun) {
search_engines::RecordChoiceScreenEvent(
search_engines::SearchEngineChoiceScreenEvents::kFreDefaultWasSet);
} else {
search_engines::RecordChoiceScreenEvent(
search_engines::SearchEngineChoiceScreenEvents::kDefaultWasSet);
}
[_mediator saveDefaultSearchEngine];
[self dismissChoiceScreen];
}
#pragma mark - SearchEngineChoiceLearnMoreCoordinatorDelegate
- (void)learnMoreDidDismiss {
[_searchEngineChoiceLearnMoreCoordinator stop];
_searchEngineChoiceLearnMoreCoordinator.delegate = nil;
_searchEngineChoiceLearnMoreCoordinator = nil;
}
#pragma mark - Private
- (void)dismissChoiceScreen {
if (_firstRun) {
[_firstRunDelegate screenWillFinishPresenting];
} else {
[self.delegate choiceScreenWillBeDismissed:self];
}
}
@end