chromium/ios/chrome/browser/enterprise/model/idle/idle_service.h

// 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.

#ifndef IOS_CHROME_BROWSER_ENTERPRISE_MODEL_IDLE_IDLE_SERVICE_H_
#define IOS_CHROME_BROWSER_ENTERPRISE_MODEL_IDLE_IDLE_SERVICE_H_

#import "base/cancelable_callback.h"
#import "base/memory/raw_ptr.h"
#import "base/memory/weak_ptr.h"
#import "base/time/time.h"
#import "components/keyed_service/core/keyed_service.h"
#import "components/prefs/pref_change_registrar.h"
#import "ios/chrome/browser/enterprise/model/idle/action_runner_impl.h"
#import "ios/chrome/browser/enterprise/model/idle/idle_timeout_policy_utils.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios_forward.h"

namespace enterprise_idle {

// Manages the idle state of a profile for the IdleTimeout enterprise
// policy. Keeps track of the policy's value, and triggers actions to run when
// the browser is idle in foreground or on startup.
class IdleService : public KeyedService {
 public:
  enum class LastState { kIdleOnBackground, kIdleOnForeground };

  // Observer handling the browsing timing out without being active.
  class Observer : public base::CheckedObserver {
   public:
    virtual void OnIdleTimeoutInForeground() = 0;
    virtual void OnIdleTimeoutOnStartup() = 0;
    virtual void OnIdleTimeoutActionsCompleted() = 0;
    virtual void OnApplicationWillEnterBackground() = 0;
  };

  explicit IdleService(ChromeBrowserState* browser_state);

  IdleService(const IdleService&) = delete;
  IdleService& operator=(const IdleService&) = delete;
  ~IdleService() override;

  // Adds and removes observers. Called when the scene agent implementing the
  // observer  is created or destroyed.
  void AddObserver(Observer* observer);
  void RemoveObserver(Observer* observer);
  // TODO(b/301676922): Move calls to `OnApplicationWillEnterForeground`
  // and `OnApplicationWillEnterBackground` to an AppStateObserver.
  // Called when the application is foregrounded, which can be after one of
  // two cases:
  // 1. Cold Start when the browser was not already running
  // 2. Warm start when the browser was already running in the background
  void OnApplicationWillEnterForeground();
  // Called when the application is backgrounded to cancel the scheduled task.
  // This ensure that actions do not run when the app is backgrounded or when
  // the screen is locked.
  void OnApplicationWillEnterBackground();
  // Checks whether the browser has been idle for the first time since last
  // being active.
  bool IsIdleAfterPreviouslyBeingActive();
  // Returns true if the `IdleTimeout` pref is set.
  bool IsIdleTimeoutPolicySet();
  // Runs actions on timeout after it has been confirmed that the user is idle.
  void RunActions();
  // Shows the snackbar after actions have completed.
  void OnActionsCompleted();
  // Returns the time `onIdleTimeoutInForeground` is triggered.
  // Used to determine the start of the countdown displayed. Usually the
  // countdown is 30s, but might need to be adjusted if the dialog was already
  // started on a different scene that was closed.
  base::Time GetIdleTriggerTime();
  // Returns the action set at the time of idle timeout detection.
  // Used for consistency of types across observers.
  ActionSet GetLastActionSet();

  // Called when a timeout confirmation dialog has been dismissed or expired to
  // unset `idle_timeout_notification_pending_` which prevent other observers
  // from trying to reshow the dialog.
  void OnIdleTimeoutDialogPresented();
  bool ShouldIdleTimeoutDialogBePresented();
  // Called when the snackbar has been displayed to unset
  // `idle_timeout_notification_pending_` which ensures that the snackbar does
  // not show more than once on start-up.
  void OnIdleTimeoutSnackbarPresented();
  bool ShouldIdleTimeoutSnackbarBePresented();

  void SetActionRunnerForTesting(std::unique_ptr<ActionRunner> action_runner);
  ActionRunner* GetActionRunnerForTesting();
  // Test wrapper for `RunActionsForState`.
  void RunActionsForStateForTesting(LastState last_state);

  void Shutdown() override;

 private:
  // Returns the value of the `kIdleTimeout` pref.
  base::TimeDelta GetTimeout() const;
  // Called when the IdleTimeout policy changes.
  void OnIdleTimeoutPrefChanged();
  // Posts a task that checks whether the browser has been idle for time >=
  // `idle_threshold_".
  void PostCheckIdleTask(base::TimeDelta time_from_now);
  // Checks whether the idle state has been reached. If idle state is reached,
  // it calls `RunActionsForState` to run actions. Otherwise, it posts a task to
  // check again when the browser might possibly become idle.
  void CheckIfIdle();
  // Checks if any action needs to run on idle timeout.
  bool IsAnyActionNeededToRun();
  // Runs the actions based on `IdleTimeoutActions` and update the UI based on
  // the last state the browser was  idle in.
  void MaybeRunActionsForState(LastState last_state);
  // Calculates the time to when the browser might become idle.
  base::TimeDelta GetPossibleTimeToIdle();

  void SetLastActiveTime();
  base::Time GetLastActiveTime();

  base::Time idle_trigger_time_;
  ActionSet last_action_set_;
  bool idle_timeout_dialog_pending_{false};
  bool idle_timeout_snackbar_pending_{false};
  raw_ptr<ChromeBrowserState> browser_state_;
  std::unique_ptr<ActionRunner> action_runner_;
  PrefChangeRegistrar pref_change_registrar_;
  base::CancelableOnceCallback<void()> cancelable_actions_callback_;
  base::ObserverList<Observer, true> observer_list_;
  base::WeakPtrFactory<IdleService> weak_factory_{this};
};

}  // namespace enterprise_idle

#endif  // IOS_CHROME_BROWSER_ENTERPRISE_MODEL_IDLE_IDLE_SERVICE_H_