blob: b66d7479264904f7bb73941ebc41c15d6145d97a [file] [log] [blame]
// 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/app/search_engine_choice_app_agent.h"
#import <memory>
#import "base/check.h"
#import "components/search_engines/search_engine_choice/search_engine_choice_service.h"
#import "ios/chrome/app/application_delegate/app_state.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/shared/coordinator/scene/scene_state.h"
#import "ios/chrome/browser/shared/coordinator/scene/scene_state_observer.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/browser/browser_provider.h"
#import "ios/chrome/browser/shared/model/browser/browser_provider_interface.h"
#import "ios/chrome/browser/signin/model/signin_util.h"
#import "ios/chrome/browser/ui/scoped_ui_blocker/scoped_ui_blocker.h"
#import "ios/chrome/browser/ui/search_engine_choice/search_engine_choice_coordinator.h"
@interface SearchEngineChoiceAppAgent () <SearchEngineChoiceCoordinatorDelegate>
@end
@implementation SearchEngineChoiceAppAgent {
// The coordinator of the search engine choice screen.
SearchEngineChoiceCoordinator* _searchEngineChoiceCoordinator;
// UI blocker used by the search engine selection screen.
std::unique_ptr<ScopedUIBlocker> _searchEngineChoiceUIBlocker;
// Scene state ID where the search engine choice dialog is displayed.
NSString* _searchEngineChoiceSceneStateID;
}
#pragma mark - SceneObservingAppAgent
- (void)sceneState:(SceneState*)sceneState
transitionedToActivationLevel:(SceneActivationLevel)level {
// Ignore SceneState activation if the app is not yet ready.
if (self.appState.initStage > InitStageFirstRun) {
switch (level) {
case SceneActivationLevelForegroundInactive:
case SceneActivationLevelBackground:
break;
case SceneActivationLevelForegroundActive:
[self maybeShowChoiceScreen:sceneState];
break;
case SceneActivationLevelDisconnected:
case SceneActivationLevelUnattached:
if (_searchEngineChoiceCoordinator &&
[_searchEngineChoiceSceneStateID
isEqual:sceneState.sceneSessionID]) {
[self choiceScreenWillBeDismissed:_searchEngineChoiceCoordinator];
// If the scene state where the search engine choice dialog is
// removed, is disconned, the search engine choice dialog needs to be
// added to the next foreground active scene (if one exists).
SceneState* nextActiveSceneState =
self.appState.foregroundActiveScene;
if (nextActiveSceneState) {
[self maybeShowChoiceScreen:nextActiveSceneState];
}
}
break;
}
}
[super sceneState:sceneState transitionedToActivationLevel:level];
}
#pragma mark - AppStateObserver
- (void)appState:(AppState*)appState
didTransitionFromInitStage:(InitStage)previousInitStage {
// Instantiate the `SearchEngineChoiceCoordinator` if necessary if any
// SceneState reached the `SceneActivationLevelForegroundActive` level
// before the app was ready.
if (self.appState.initStage > InitStageFirstRun) {
for (SceneState* sceneState in self.appState.connectedScenes) {
if (sceneState.activationLevel == SceneActivationLevelForegroundActive) {
[self maybeShowChoiceScreen:sceneState];
}
}
}
[super appState:appState didTransitionFromInitStage:previousInitStage];
}
#pragma mark - SearchEngineChoiceCoordinatorDelegate
- (void)choiceScreenWillBeDismissed:
(SearchEngineChoiceCoordinator*)coordinator {
DCHECK_EQ(_searchEngineChoiceCoordinator, coordinator);
DCHECK(_searchEngineChoiceSceneStateID);
_searchEngineChoiceUIBlocker.reset();
_searchEngineChoiceSceneStateID = nil;
[_searchEngineChoiceCoordinator stop];
_searchEngineChoiceCoordinator = nil;
}
#pragma mark - Private
- (void)maybeShowChoiceScreen:(SceneState*)sceneState {
// The application needs to be ready (i.e. the Browser created, ...) before
// the choice screen can be presented. Assert this is the case.
DCHECK_GT(self.appState.initStage, InitStageFirstRun);
// TODO(crbug.com/326035954): Evaluate if this is correct for multiple browser
// states.
BOOL hasPreRestoreAccountInfo =
GetPreRestoreIdentity(GetApplicationContext()->GetLocalState())
.has_value();
if (hasPreRestoreAccountInfo) {
// Do not override the post-restore signin promo. The choice screen should
// come after.
return;
}
if (_searchEngineChoiceCoordinator) {
return;
}
// Using main browser so that, even in incognito mode, the user is not
// re-asked which search engine to use.
if (ShouldDisplaySearchEngineChoiceScreen(
*sceneState.browserProviderInterface.mainBrowserProvider.browser
->GetBrowserState(),
search_engines::ChoicePromo::kDialog)) {
DCHECK(!_searchEngineChoiceUIBlocker);
DCHECK(!_searchEngineChoiceSceneStateID);
_searchEngineChoiceUIBlocker =
std::make_unique<ScopedUIBlocker>(sceneState);
_searchEngineChoiceSceneStateID = sceneState.sceneSessionID;
_searchEngineChoiceCoordinator = [[SearchEngineChoiceCoordinator alloc]
initWithBaseViewController:sceneState.browserProviderInterface
.currentBrowserProvider.viewController
browser:sceneState.browserProviderInterface
.currentBrowserProvider.browser];
_searchEngineChoiceCoordinator.delegate = self;
[_searchEngineChoiceCoordinator start];
}
}
@end