chromium/ios/chrome/app/application_delegate/observing_app_state_agent.mm

// Copyright 2021 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/application_delegate/observing_app_state_agent.h"

#import "base/check.h"
#import "ios/chrome/app/application_delegate/app_state.h"
#import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"

@implementation ObservingAppAgent

#pragma mark - AppStateAgent

+ (instancetype)agentFromApp:(AppState*)appState {
  for (id agent in appState.connectedAgents) {
    if ([agent isMemberOfClass:[self class]]) {
      return agent;
    }
  }

  return nil;
}

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

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

@end

#pragma mark - SceneObservingAppAgent

@interface SceneObservingAppAgent ()

// Tracks if the app has already notified that some scenes are in foreground.
// Reset when the app goes background.
@property(nonatomic, assign) BOOL notifiedForeground;
// Tracks if the app has already notified that some scenes are in background.
// Reset when the app goes foreground.
@property(nonatomic, assign) BOOL notifiedBackground;

@end

@implementation SceneObservingAppAgent

- (instancetype)init {
  self = [super init];
  if (self) {
    // The app starts with no connected scenes, so the first event should be
    // foreground.
    _notifiedBackground = YES;
  }
  return self;
}

#pragma mark - AppStateAgent

- (void)setAppState:(AppState*)appState {
  [super setAppState:appState];

  // If there are already connected scenes, start observing them.
  for (SceneState* scene in self.appState.connectedScenes) {
    [scene addObserver:self];
  }
  [self notifyOfConvenienceEventsIfNecessary];
}

#pragma mark - AppStateObserver

- (void)appState:(AppState*)appState sceneConnected:(SceneState*)sceneState {
  [sceneState addObserver:self];
}

- (void)appState:(AppState*)appState
    didTransitionFromInitStage:(InitStage)previousInitStage {
  if (appState.initStage == InitStageFinal) {
    [self notifyOfConvenienceEventsIfNecessary];
  }
}

#pragma mark - SceneStateObserver

- (void)sceneState:(SceneState*)sceneState
    transitionedToActivationLevel:(SceneActivationLevel)level {
  if (self.appState.initStage < InitStageFinal) {
    return;
  }

  [self notifyOfConvenienceEventsIfNecessary];
}

- (void)notifyOfConvenienceEventsIfNecessary {
  if (self.appState.foregroundScenes.count > 0 && !self.notifiedForeground) {
    self.notifiedForeground = YES;
    self.notifiedBackground = NO;
    [self appDidEnterForeground];
  }

  if (self.appState.foregroundScenes.count == 0 && !self.notifiedBackground) {
    self.notifiedBackground = YES;
    self.notifiedForeground = NO;
    [self appDidEnterBackground];
  }
}

#pragma mark - template methods
- (void)appDidEnterForeground {
}

- (void)appDidEnterBackground {
}

@end