chromium/ios/chrome/app/post_restore_app_agent.mm

// Copyright 2022 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/post_restore_app_agent.h"

#import "base/memory/raw_ptr.h"
#import "components/signin/public/identity_manager/identity_manager.h"
#import "components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h"
#import "ios/chrome/app/application_delegate/app_state.h"
#import "ios/chrome/browser/promos_manager/model/constants.h"
#import "ios/chrome/browser/promos_manager/model/promos_manager.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/signin/model/authentication_service.h"
#import "ios/chrome/browser/signin/model/signin_util.h"

@interface PostRestoreAppAgent () <AppStateObserver,
                                   IdentityManagerObserverBridgeDelegate>

// The app state for the app.
@property(nonatomic, weak, readonly) AppState* appState;

// Stores whether we have pre-restore account info.
@property(nonatomic, assign) BOOL hasAccountInfo;

// The PromosManager is used to register promos.
@property(nonatomic, assign) PromosManager* promosManager;

// The AuthenticationManager is used to reset the reauth infobar prompt.
@property(nonatomic, assign) AuthenticationService* authenticationService;

// Local state is used to retrieve and/or clear the pre-restore identity.
@property(nonatomic, assign) PrefService* localState;

@end

@implementation PostRestoreAppAgent {
  raw_ptr<signin::IdentityManager> _identityManager;
  std::unique_ptr<signin::IdentityManagerObserverBridge>
      _identityObserverBridge;
}

#pragma mark - Initializers

- (instancetype)
    initWithPromosManager:(PromosManager*)promosManager
    authenticationService:(AuthenticationService*)authenticationService
          identityManager:(signin::IdentityManager*)identityManager {
  DCHECK(authenticationService);
  DCHECK(identityManager);

  self = [super init];
  if (self) {
    _promosManager = promosManager;
    _authenticationService = authenticationService;
    _identityManager = identityManager;
    _localState = GetApplicationContext()->GetLocalState();
  }
  return self;
}

#pragma mark - AppStateAgent

- (void)setAppState:(AppState*)appState {
  // This should only be called once!
  DCHECK(!_appState);

  _appState = appState;
  [appState addObserver:self];
}

#pragma mark - AppStateObserver

- (void)appState:(AppState*)appState
    didTransitionFromInitStage:(InitStage)previousInitStage {
  if (self.appState.initStage == InitStageFinal) {
    self.hasAccountInfo = GetPreRestoreIdentity(_localState).has_value();
    [self maybeRegisterPromo];
    // AuthenticationService is no longer needed.
    self.authenticationService = nullptr;
    [self.appState removeObserver:self];
    if (self.hasAccountInfo && _identityManager) {
      _identityObserverBridge =
          std::make_unique<signin::IdentityManagerObserverBridge>(
              _identityManager, self);
    } else {
      [self shutdown];
    }
  }
}

#pragma mark - IdentityManagerObserverBridgeDelegate

// Called when a user adds a primary account.
- (void)onPrimaryAccountChanged:
    (const signin::PrimaryAccountChangeEvent&)event {
  switch (event.GetEventTypeFor(signin::ConsentLevel::kSignin)) {
    case signin::PrimaryAccountChangeEvent::Type::kSet:
      if (self.promosManager) {
        [self deregisterPromos];
        ClearPreRestoreIdentity(_localState);
        self.hasAccountInfo = NO;
        [self shutdown];
      }
      break;
    case signin::PrimaryAccountChangeEvent::Type::kCleared:
    case signin::PrimaryAccountChangeEvent::Type::kNone:
      break;
  }
}

- (void)onIdentityManagerShutdown:(signin::IdentityManager*)identityManager {
  [self shutdown];
}

#pragma mark - Private

// Register the promo with the PromosManager, if the conditions are met,
// otherwise deregister the promo.
- (void)maybeRegisterPromo {
  if (_promosManager && _hasAccountInfo) {
    [self registerPromo];
  } else if (_promosManager) {
    [self deregisterPromos];
  } else if (_hasAccountInfo) {
    ClearPreRestoreIdentity(_localState);
  }
}

// Registers the promo with the PromosManager.
- (void)registerPromo {
  DCHECK(_promosManager);
  // Disable the reauth infobar so that we don't prompt the user twice about
  // reauthenticating.
  _authenticationService->ResetReauthPromptForSignInAndSync();

  // Deregister any previously registered promos.
  [self deregisterPromos];
  _promosManager->RegisterPromoForSingleDisplay(
      promos_manager::Promo::PostRestoreSignInAlert);
}

// Deregisters the promo with the PromosManager.
- (void)deregisterPromos {
  DCHECK(_promosManager);
  _promosManager->DeregisterPromo(
      promos_manager::Promo::PostRestoreSignInFullscreen);
  _promosManager->DeregisterPromo(
      promos_manager::Promo::PostRestoreSignInAlert);
}

// Stops observers and clears pointers.
- (void)shutdown {
  CHECK(self.authenticationService == nullptr);
  [self.appState removeAgent:self];
  self.promosManager = nullptr;
  _identityManager = nullptr;
  _identityObserverBridge.reset();
}

@end