chromium/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator.mm

// Copyright 2018 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/ui/tab_switcher/tab_grid/tab_grid_mediator.h"

#import "base/feature_list.h"
#import "base/memory/raw_ptr.h"
#import "base/metrics/histogram_macros.h"
#import "base/notimplemented.h"
#import "components/feature_engagement/public/event_constants.h"
#import "components/feature_engagement/public/tracker.h"
#import "components/prefs/ios/pref_observer_bridge.h"
#import "components/prefs/pref_change_registrar.h"
#import "components/prefs/pref_service.h"
#import "components/signin/public/identity_manager/identity_manager.h"
#import "components/signin/public/identity_manager/tribool.h"
#import "components/supervised_user/core/browser/supervised_user_preferences.h"
#import "components/supervised_user/core/common/features.h"
#import "components/supervised_user/core/common/pref_names.h"
#import "ios/chrome/browser/policy/model/policy_util.h"
#import "ios/chrome/browser/supervised_user/model/supervised_user_capabilities_observer_bridge.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_toolbars_mutator.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_consumer.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_metrics.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mode_holder.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mode_observing.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_page_mutator.h"

@interface TabGridMediator () <PrefObserverDelegate,
                               SupervisedUserCapabilitiesObserving,
                               TabGridModeObserving>
@end

@implementation TabGridMediator {
  // Current selected grid.
  id<TabGridPageMutator> _currentPageMutator;
  // Preference service from the application context.
  raw_ptr<PrefService> _prefService;
  // Feature engagement tracker.
  raw_ptr<feature_engagement::Tracker> _engagementTracker;
  // Pref observer to track changes to prefs.
  std::unique_ptr<PrefObserverBridge> _prefObserverBridge;
  // Registrar for pref changes notifications.
  PrefChangeRegistrar _prefChangeRegistrar;
  // Identity manager providing AccountInfo capabilities.
  raw_ptr<signin::IdentityManager> _identityManager;
  // Observer to track changes to supervision-related capabilities.
  std::unique_ptr<supervised_user::SupervisedUserCapabilitiesObserverBridge>
      _supervisedUserCapabilitiesObserver;
  // Holder for the current mode of the TabGrid.
  TabGridModeHolder* _modeHolder;
}

- (instancetype)initWithIdentityManager:
                    (signin::IdentityManager*)identityManager
                            prefService:(PrefService*)prefService
               featureEngagementTracker:(feature_engagement::Tracker*)tracker
                             modeHolder:(TabGridModeHolder*)modeHolder {
  self = [super init];
  if (self) {
    CHECK(identityManager);
    CHECK(prefService);
    CHECK(tracker);
    CHECK(modeHolder);
    _engagementTracker = tracker;
    _modeHolder = modeHolder;
    [_modeHolder addObserver:self];

    if (base::FeatureList::IsEnabled(
            supervised_user::
                kReplaceSupervisionPrefsWithAccountCapabilitiesOnIOS)) {
      _identityManager = identityManager;
      _supervisedUserCapabilitiesObserver = std::make_unique<
          supervised_user::SupervisedUserCapabilitiesObserverBridge>(
          _identityManager, self);
    } else {
      _prefService = prefService;
      _prefChangeRegistrar.Init(_prefService);
      _prefObserverBridge.reset(new PrefObserverBridge(self));

      // Register to observe any changes on supervised_user status.
      _prefObserverBridge->ObserveChangesForPreference(prefs::kSupervisedUserId,
                                                       &_prefChangeRegistrar);
    }
  }
  return self;
}

- (void)disconnect {
  _prefChangeRegistrar.RemoveAll();
  _prefObserverBridge.reset();
  _prefService = nil;
  _supervisedUserCapabilitiesObserver.reset();
  _identityManager = nil;
  _consumer = nil;

  [_modeHolder removeObserver:self];
  _modeHolder = nil;
}

#pragma mark - Public

- (void)setActivePage:(TabGridPage)page {
  [self notifyPageMutatorAboutPage:page];
  [_currentPageMutator setPageAsActive];
}

- (void)setConsumer:(id<TabGridConsumer>)consumer {
  _consumer = consumer;
  BOOL isSubjectToParentalControls;
  if (base::FeatureList::IsEnabled(
          supervised_user::
              kReplaceSupervisionPrefsWithAccountCapabilitiesOnIOS)) {
    isSubjectToParentalControls =
        supervised_user::IsPrimaryAccountSubjectToParentalControls(
            _identityManager) == signin::Tribool::kTrue;
  } else {
    isSubjectToParentalControls =
        supervised_user::IsSubjectToParentalControls(*_prefService);
  }
  [_consumer updateParentalControlStatus:isSubjectToParentalControls];
}

#pragma mark - PrefObserverDelegate

// TODO(b/295307282): Migrate to IncognitoGridMediator once the incognito grid
// coordinator and view controller is ready.
- (void)onPreferenceChanged:(const std::string&)preferenceName {
  if (!base::FeatureList::IsEnabled(
          supervised_user::
              kReplaceSupervisionPrefsWithAccountCapabilitiesOnIOS) &&
      preferenceName == prefs::kSupervisedUserId) {
    [_consumer updateParentalControlStatus:
        supervised_user::IsSubjectToParentalControls(*_prefService)];
    [_consumer updateTabGridForIncognitoModeDisabled:IsIncognitoModeDisabled(
                                                         _prefService)];
  }
}

#pragma mark - SupervisedUserCapabilitiesObserving

- (void)onIsSubjectToParentalControlsCapabilityChanged:
    (supervised_user::CapabilityUpdateState)capabilityUpdateState {
  if (base::FeatureList::IsEnabled(
          supervised_user::
              kReplaceSupervisionPrefsWithAccountCapabilitiesOnIOS)) {
    BOOL isSubjectToParentalControl =
        (capabilityUpdateState ==
         supervised_user::CapabilityUpdateState::kSetToTrue);
    [_consumer updateParentalControlStatus:isSubjectToParentalControl];
    [_consumer updateTabGridForIncognitoModeDisabled:IsIncognitoModeDisabled(
                                                         _prefService)];
  }
}

#pragma mark - Private

// Sets the current selected mutator according to the page.
- (void)updateCurrentPageMutatorForPage:(TabGridPage)page {
  switch (page) {
    case TabGridPageIncognitoTabs:
      _currentPageMutator = self.incognitoPageMutator;
      break;
    case TabGridPageRegularTabs:
      _currentPageMutator = self.regularPageMutator;
      break;
    case TabGridPageRemoteTabs:
      _currentPageMutator = self.remotePageMutator;
      break;
    case TabGridPage::TabGridPageTabGroups:
      _currentPageMutator = self.tabGroupsPageMutator;
      break;
  }
}

// Notifies mutators if it is the current selected one or not.
- (void)notifyPageMutatorAboutPage:(TabGridPage)page {
  [_currentPageMutator currentlySelectedGrid:NO];
  [self updateCurrentPageMutatorForPage:page];
  [_currentPageMutator currentlySelectedGrid:YES];
}

#pragma mark - TabGridModeObserving

- (void)tabGridModeDidChange:(TabGridModeHolder*)modeHolder {
  [self.consumer setMode:modeHolder.mode];
}

#pragma mark - TabGridMutator

- (void)pageChanged:(TabGridPage)currentPage
        interaction:(TabSwitcherPageChangeInteraction)interaction {
  UMA_HISTOGRAM_ENUMERATION(kUMATabSwitcherPageChangeInteractionHistogram,
                            interaction);

  [self notifyPageMutatorAboutPage:currentPage];
  if (currentPage == TabGridPageIncognitoTabs) {
    switch (interaction) {
      case TabSwitcherPageChangeInteraction::kScrollDrag:
      case TabSwitcherPageChangeInteraction::kAccessibilitySwipe:
        _engagementTracker->NotifyEvent(
            feature_engagement::events::kIOSSwipeRightForIncognitoUsed);
        break;
      case TabSwitcherPageChangeInteraction::kControlTap:
      case TabSwitcherPageChangeInteraction::kControlDrag:
        _engagementTracker->NotifyEvent(
            feature_engagement::events::kIOSIncognitoPageControlTapped);
        break;
      case TabSwitcherPageChangeInteraction::kNone:
      case TabSwitcherPageChangeInteraction::kItemDrag:
        break;
    }
  }
  // TODO(crbug.com/40921760): Implement the incognito grid or content visible
  // notification.
}

- (void)dragAndDropSessionStarted {
  [self.toolbarsMutator setButtonsEnabled:NO];
}

- (void)dragAndDropSessionEnded {
  [self.toolbarsMutator setButtonsEnabled:YES];
}

- (void)quitSearchMode {
  _modeHolder.mode = TabGridMode::kNormal;
}

@end