// 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/policy/ui_bundled/idle/idle_timeout_confirmation_coordinator.h"
#import "base/strings/sys_string_conversions.h"
#import "base/time/time.h"
#import "components/enterprise/idle/idle_pref_names.h"
#import "components/prefs/pref_service.h"
#import "ios/chrome/browser/enterprise/model/idle/idle_service.h"
#import "ios/chrome/browser/enterprise/model/idle/idle_service_factory.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/ui/elements/activity_overlay_coordinator.h"
#import "ios/chrome/browser/signin/model/authentication_service.h"
#import "ios/chrome/browser/signin/model/authentication_service_factory.h"
#import "ios/chrome/browser/policy/ui_bundled/idle/idle_timeout_cofirmation_presenter.h"
#import "ios/chrome/browser/policy/ui_bundled/idle/idle_timeout_confirmation_coordinator_delegate.h"
#import "ios/chrome/browser/policy/ui_bundled/idle/idle_timeout_confirmation_mediator.h"
#import "ios/chrome/browser/policy/ui_bundled/idle/idle_timeout_confirmation_view_controller.h"
#import "ios/chrome/browser/policy/ui_bundled/idle/idle_timeout_policy_utils.h"
#import "ios/chrome/common/ui/confirmation_alert/confirmation_alert_action_handler.h"
namespace {
constexpr CGFloat kHalfSheetCornerRadius = 20.0;
constexpr base::TimeDelta kDialogTimeout = base::Seconds(30);
} // namespace
@interface IdleTimeoutConfirmationCoordinator () <
IdleTimeoutConfirmationPresenter>
@end
@implementation IdleTimeoutConfirmationCoordinator {
// View controller for the idle timeout confirmation dialog.
IdleTimeoutConfirmationViewController* _presentedViewController;
// Mediator for the idle timeout confirmation dialog ro respond to cancel and
// UI timeout.
IdleTimeoutConfirmationMediator* _mediator;
}
#pragma mark - ChromeCoordinator
- (void)start {
_mediator = [[IdleTimeoutConfirmationMediator alloc]
initWithPresenter:self
dialogDuration:[self countDownStart]];
enterprise_idle::IdleService* idleService = [self idleService];
AuthenticationService* authService = [self authService];
enterprise_idle::ActionSet actions = idleService->GetLastActionSet();
std::optional<int> titleId =
enterprise_idle::GetIdleTimeoutActionsTitleId(actions);
CHECK(titleId)
<< "The idle timeout confirmation dialog title id should not be empty";
int subtitleId = enterprise_idle::GetIdleTimeoutActionsSubtitleId(
actions, authService->ShouldClearDataForSignedInPeriodOnSignOut());
_presentedViewController = [[IdleTimeoutConfirmationViewController alloc]
initWithIdleTimeoutTitleId:*titleId
idleTimeoutSubtitleId:subtitleId
idleTimeoutThreshold:[self idleTimeout]];
_presentedViewController.actionHandler = _mediator;
_mediator.consumer = _presentedViewController;
_presentedViewController.modalPresentationStyle =
UIModalPresentationPageSheet;
UISheetPresentationController* presentationController =
_presentedViewController.sheetPresentationController;
presentationController.prefersEdgeAttachedInCompactHeight = YES;
presentationController.detents = @[
UISheetPresentationControllerDetent.mediumDetent,
UISheetPresentationControllerDetent.largeDetent
];
presentationController.preferredCornerRadius = kHalfSheetCornerRadius;
_presentedViewController.modalInPresentation = YES;
__weak __typeof(self) weakSelf = self;
[self.baseViewController presentViewController:_presentedViewController
animated:YES
completion:^{
[weakSelf setInitialVoiceOverFocus];
}];
}
- (void)stop {
// Reset the timers before dismising the view. This is needed for the case
// when the app is backgrounded because the mediator does not stop the timers
// in this case before this funciton is called.
[_mediator stop];
if (_presentedViewController) {
[self.baseViewController.presentedViewController
dismissViewControllerAnimated:YES
completion:nil];
_presentedViewController = nil;
}
}
#pragma mark - IdleTimeoutConfirmationPresenter
- (void)stopPresentingAfterUserClickedContinue {
[self.delegate stopPresentingAndRunActionsAfterwards:false];
}
- (void)stopPresentingAfterDialogExpired {
[self.delegate stopPresentingAndRunActionsAfterwards:true];
}
#pragma mark - Private
// Returns the idle timeout the admin has set.
- (int)idleTimeout {
return self.browser->GetBrowserState()
->GetPrefs()
->GetTimeDelta(enterprise_idle::prefs::kIdleTimeout)
.InMinutes();
}
// Returns the time delta from which the remaining time countdown should start.
// The time is usually 30 seconds. However, this is used to cover the
// multi-window case when the dialog is reshown on a new window after the first
// one has been closed.
- (base::TimeDelta)countDownStart {
return kDialogTimeout - (base::Time::Now() - self.triggerTime);
}
- (enterprise_idle::IdleService*)idleService {
return enterprise_idle::IdleServiceFactory::GetForBrowserState(
self.browser->GetBrowserState());
}
- (AuthenticationService*)authService {
return AuthenticationServiceFactory::GetForBrowserState(
self.browser->GetBrowserState());
}
- (void)setInitialVoiceOverFocus {
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification,
_presentedViewController.image);
}
@end