| // Copyright 2017 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/content_suggestions/content_suggestions_mediator.h" |
| |
| #import <AuthenticationServices/AuthenticationServices.h> |
| #import <MaterialComponents/MaterialSnackbar.h> |
| |
| #import "base/apple/foundation_util.h" |
| #import "base/functional/bind.h" |
| #import "base/functional/callback.h" |
| #import "base/metrics/histogram_macros.h" |
| #import "base/metrics/user_metrics.h" |
| #import "base/metrics/user_metrics_action.h" |
| #import "base/strings/sys_string_conversions.h" |
| #import "components/favicon/ios/web_favicon_driver.h" |
| #import "components/feed/core/v2/public/ios/pref_names.h" |
| #import "components/history/core/browser/features.h" |
| #import "components/ntp_tiles/features.h" |
| #import "components/ntp_tiles/metrics.h" |
| #import "components/ntp_tiles/most_visited_sites.h" |
| #import "components/ntp_tiles/ntp_tile.h" |
| #import "components/pref_registry/pref_registry_syncable.h" |
| #import "components/prefs/ios/pref_observer_bridge.h" |
| #import "components/reading_list/core/reading_list_model.h" |
| #import "components/reading_list/ios/reading_list_model_bridge_observer.h" |
| #import "components/search_engines/search_terms_data.h" |
| #import "components/search_engines/template_url.h" |
| #import "components/segmentation_platform/public/constants.h" |
| #import "components/segmentation_platform/public/features.h" |
| #import "components/segmentation_platform/public/segmentation_platform_service.h" |
| #import "components/signin/public/identity_manager/identity_manager.h" |
| #import "components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h" |
| #import "components/strings/grit/components_strings.h" |
| #import "ios/chrome/app/application_delegate/app_state.h" |
| #import "ios/chrome/browser/default_browser/utils.h" |
| #import "ios/chrome/browser/ntp/features.h" |
| #import "ios/chrome/browser/ntp/new_tab_page_tab_helper.h" |
| #import "ios/chrome/browser/ntp/set_up_list.h" |
| #import "ios/chrome/browser/ntp/set_up_list_delegate.h" |
| #import "ios/chrome/browser/ntp/set_up_list_item.h" |
| #import "ios/chrome/browser/ntp/set_up_list_item_type.h" |
| #import "ios/chrome/browser/ntp/set_up_list_prefs.h" |
| #import "ios/chrome/browser/ntp_tiles/most_visited_sites_observer_bridge.h" |
| #import "ios/chrome/browser/policy/policy_util.h" |
| #import "ios/chrome/browser/shared/coordinator/scene/scene_state.h" |
| #import "ios/chrome/browser/shared/coordinator/scene/scene_state_browser_agent.h" |
| #import "ios/chrome/browser/shared/model/application_context/application_context.h" |
| #import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h" |
| #import "ios/chrome/browser/shared/model/prefs/pref_names.h" |
| #import "ios/chrome/browser/shared/model/url/chrome_url_constants.h" |
| #import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h" |
| #import "ios/chrome/browser/shared/public/commands/application_commands.h" |
| #import "ios/chrome/browser/shared/public/commands/browser_coordinator_commands.h" |
| #import "ios/chrome/browser/shared/public/commands/open_new_tab_command.h" |
| #import "ios/chrome/browser/shared/public/commands/snackbar_commands.h" |
| #import "ios/chrome/browser/shared/public/features/features.h" |
| #import "ios/chrome/browser/shared/public/features/system_flags.h" |
| #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h" |
| #import "ios/chrome/browser/signin/authentication_service.h" |
| #import "ios/chrome/browser/signin/authentication_service_factory.h" |
| #import "ios/chrome/browser/signin/authentication_service_observer_bridge.h" |
| #import "ios/chrome/browser/sync/enterprise_utils.h" |
| #import "ios/chrome/browser/sync/sync_observer_bridge.h" |
| #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_item.h" |
| #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.h" |
| #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_item.h" |
| #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_constants.h" |
| #import "ios/chrome/browser/ui/content_suggestions/cells/query_suggestion_view.h" |
| #import "ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h" |
| #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h" |
| #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_favicon_mediator.h" |
| #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.h" |
| #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator_util.h" |
| #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recorder.h" |
| #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_tile_saver.h" |
| #import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestions_section_information.h" |
| #import "ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_view_data.h" |
| #import "ios/chrome/browser/ui/content_suggestions/set_up_list/utils.h" |
| #import "ios/chrome/browser/ui/content_suggestions/start_suggest_service_factory.h" |
| #import "ios/chrome/browser/ui/credential_provider_promo/credential_provider_promo_metrics.h" |
| #import "ios/chrome/browser/ui/ntp/feed_delegate.h" |
| #import "ios/chrome/browser/ui/ntp/new_tab_page_feature.h" |
| #import "ios/chrome/browser/ui/ntp/new_tab_page_metrics_delegate.h" |
| #import "ios/chrome/browser/ui/start_surface/start_surface_util.h" |
| #import "ios/chrome/browser/ui/whats_new/whats_new_util.h" |
| #import "ios/chrome/browser/url_loading/url_loading_browser_agent.h" |
| #import "ios/chrome/browser/url_loading/url_loading_params.h" |
| #import "ios/chrome/common/app_group/app_group_constants.h" |
| #import "ios/chrome/grit/ios_strings.h" |
| #import "third_party/abseil-cpp/absl/types/optional.h" |
| #import "ui/base/l10n/l10n_util_mac.h" |
| |
| namespace { |
| |
| using credential_provider_promo::IOSCredentialProviderPromoAction; |
| using CSCollectionViewItem = CollectionViewItem<SuggestedContent>; |
| using RequestSource = SearchTermsData::RequestSource; |
| |
| // Maximum number of most visited tiles fetched. |
| const NSInteger kMaxNumMostVisitedTiles = 4; |
| |
| // Checks the last action the user took on the Credential Provider Promo to |
| // determine if it was dismissed. |
| bool CredentialProviderPromoDismissed(PrefService* local_state) { |
| IOSCredentialProviderPromoAction last_action = |
| static_cast<IOSCredentialProviderPromoAction>(local_state->GetInteger( |
| prefs::kIosCredentialProviderPromoLastActionTaken)); |
| return last_action == IOSCredentialProviderPromoAction::kNo; |
| } |
| |
| } // namespace |
| |
| @interface ContentSuggestionsMediator () <AuthenticationServiceObserving, |
| SyncObserverModelBridge, |
| IdentityManagerObserverBridgeDelegate, |
| MostVisitedSitesObserving, |
| ReadingListModelBridgeObserver, |
| PrefObserverDelegate, |
| SceneStateObserver, |
| SetUpListDelegate> { |
| std::unique_ptr<ntp_tiles::MostVisitedSites> _mostVisitedSites; |
| std::unique_ptr<ntp_tiles::MostVisitedSitesObserverBridge> _mostVisitedBridge; |
| std::unique_ptr<ReadingListModelBridge> _readingListModelBridge; |
| } |
| |
| // Whether the contents section should be hidden completely. |
| // Don't use PrefBackedBoolean or PrefMember as this value needs to be checked |
| // when the Preference is updated. |
| @property(nonatomic, assign, readonly) BOOL contentSuggestionsEnabled; |
| |
| // Don't use PrefBackedBoolean or PrefMember as those values needs to be checked |
| // when the Preference is updated. |
| // Whether the suggestions have been disabled in Chrome Settings. |
| @property(nonatomic, assign) |
| const PrefService::Preference* articleForYouEnabled; |
| // Whether the suggestions have been disabled by a policy. |
| @property(nonatomic, assign) |
| const PrefService::Preference* contentSuggestionsPolicyEnabled; |
| |
| // Most visited items from the MostVisitedSites service currently displayed. |
| @property(nonatomic, strong) |
| NSMutableArray<ContentSuggestionsMostVisitedItem*>* mostVisitedItems; |
| @property(nonatomic, strong) |
| NSArray<ContentSuggestionsMostVisitedActionItem*>* actionButtonItems; |
| // Most visited items from the MostVisitedSites service (copied upon receiving |
| // the callback). Those items are up to date with the model. |
| @property(nonatomic, strong) |
| NSMutableArray<ContentSuggestionsMostVisitedItem*>* freshMostVisitedItems; |
| // Section Info for the logo and omnibox section. |
| @property(nonatomic, strong) |
| ContentSuggestionsSectionInformation* logoSectionInfo; |
| // Section Info for the "Return to Recent Tab" section. |
| @property(nonatomic, strong) |
| ContentSuggestionsSectionInformation* returnToRecentTabSectionInfo; |
| // Item for the "Return to Recent Tab" tile. |
| @property(nonatomic, strong) |
| ContentSuggestionsReturnToRecentTabItem* returnToRecentTabItem; |
| // Section Info for the Most Visited section. |
| @property(nonatomic, strong) |
| ContentSuggestionsSectionInformation* mostVisitedSectionInfo; |
| // Whether the page impression has been recorded. |
| @property(nonatomic, assign) BOOL recordedPageImpression; |
| // Mediator fetching the favicons for the items. |
| @property(nonatomic, strong) ContentSuggestionsFaviconMediator* faviconMediator; |
| // Item for the reading list action item. Reference is used to update the |
| // reading list count. |
| @property(nonatomic, strong) |
| ContentSuggestionsMostVisitedActionItem* readingListItem; |
| // Indicates if reading list model is loaded. Readlist cannot be triggered until |
| // it is. |
| @property(nonatomic, assign) NSInteger readingListModelIsLoaded; |
| // Number of unread items in reading list model. |
| @property(nonatomic, assign) NSInteger readingListUnreadCount; |
| // YES if the Return to Recent Tab tile is being shown. |
| @property(nonatomic, assign, getter=mostRecentTabStartSurfaceTileIsShowing) |
| BOOL showMostRecentTabStartSurfaceTile; |
| // Whether the incognito mode is available. |
| @property(nonatomic, assign) BOOL incognitoAvailable; |
| // Browser reference. |
| @property(nonatomic, assign) Browser* browser; |
| // The SetUpList, a list of tasks a new user might want to complete. |
| @property(nonatomic, strong) SetUpList* setUpList; |
| |
| // For testing-only |
| @property(nonatomic, assign) BOOL hasReceivedMagicStackResponse; |
| |
| @end |
| |
| @implementation ContentSuggestionsMediator { |
| // Bridge to listen to pref changes. |
| std::unique_ptr<PrefObserverBridge> _prefObserverBridge; |
| // Registrar for pref changes notifications. |
| PrefChangeRegistrar _prefChangeRegistrar; |
| // Local State prefs. |
| PrefService* _localState; |
| // Used by SetUpList to get the sync status. |
| syncer::SyncService* _syncService; |
| // Used by SetUpList to get signed-in status. |
| AuthenticationService* _authenticationService; |
| // Used by SetUpList to observe changes to signed-in status. |
| std::unique_ptr<signin::IdentityManagerObserverBridge> |
| _identityObserverBridge; |
| // Observer for sync service status changes. |
| std::unique_ptr<SyncObserverBridge> _syncObserverBridge; |
| // Observer for auth service status changes. |
| std::unique_ptr<AuthenticationServiceObserverBridge> |
| _authServiceObserverBridge; |
| } |
| |
| #pragma mark - Public |
| |
| - (instancetype) |
| initWithLargeIconService:(favicon::LargeIconService*)largeIconService |
| largeIconCache:(LargeIconCache*)largeIconCache |
| mostVisitedSite:(std::unique_ptr<ntp_tiles::MostVisitedSites>) |
| mostVisitedSites |
| readingListModel:(ReadingListModel*)readingListModel |
| prefService:(PrefService*)prefService |
| isGoogleDefaultSearchProvider:(BOOL)isGoogleDefaultSearchProvider |
| syncService:(syncer::SyncService*)syncService |
| authenticationService:(AuthenticationService*)authenticationService |
| identityManager:(signin::IdentityManager*)identityManager |
| browser:(Browser*)browser { |
| self = [super init]; |
| if (self) { |
| _localState = GetApplicationContext()->GetLocalState(); |
| _incognitoAvailable = !IsIncognitoModeDisabled(prefService); |
| _articleForYouEnabled = |
| prefService->FindPreference(prefs::kArticlesForYouEnabled); |
| _contentSuggestionsPolicyEnabled = |
| prefService->FindPreference(prefs::kNTPContentSuggestionsEnabled); |
| |
| _faviconMediator = [[ContentSuggestionsFaviconMediator alloc] |
| initWithLargeIconService:largeIconService |
| largeIconCache:largeIconCache]; |
| |
| _logoSectionInfo = LogoSectionInformation(); |
| _mostVisitedSectionInfo = MostVisitedSectionInformation(); |
| |
| _mostVisitedSites = std::move(mostVisitedSites); |
| _mostVisitedBridge = |
| std::make_unique<ntp_tiles::MostVisitedSitesObserverBridge>(self); |
| _mostVisitedSites->AddMostVisitedURLsObserver(_mostVisitedBridge.get(), |
| kMaxNumMostVisitedTiles); |
| |
| _readingListModelBridge = |
| std::make_unique<ReadingListModelBridge>(self, readingListModel); |
| |
| _authenticationService = authenticationService; |
| _syncService = syncService; |
| if (IsIOSSetUpListEnabled() && |
| set_up_list_utils::IsSetUpListActive(_localState)) { |
| _authServiceObserverBridge = |
| std::make_unique<AuthenticationServiceObserverBridge>( |
| _authenticationService, self); |
| _syncObserverBridge = |
| std::make_unique<SyncObserverBridge>(self, _syncService); |
| _identityObserverBridge = |
| std::make_unique<signin::IdentityManagerObserverBridge>( |
| identityManager, self); |
| _prefObserverBridge = std::make_unique<PrefObserverBridge>(self); |
| _prefChangeRegistrar.Init(_localState); |
| _prefObserverBridge->ObserveChangesForPreference( |
| prefs::kIosCredentialProviderPromoLastActionTaken, |
| &_prefChangeRegistrar); |
| _prefObserverBridge->ObserveChangesForPreference( |
| set_up_list_prefs::kDisabled, &_prefChangeRegistrar); |
| if (CredentialProviderPromoDismissed(_localState)) { |
| set_up_list_prefs::MarkItemComplete(_localState, |
| SetUpListItemType::kAutofill); |
| } else { |
| [self checkIfCPEEnabled]; |
| } |
| _setUpList = [SetUpList buildFromPrefs:prefService |
| localState:_localState |
| syncService:syncService |
| authenticationService:authenticationService]; |
| } |
| SceneState* sceneState = |
| SceneStateBrowserAgent::FromBrowser(browser)->GetSceneState(); |
| [sceneState addObserver:self]; |
| _browser = browser; |
| } |
| return self; |
| } |
| |
| + (void)registerBrowserStatePrefs:(user_prefs::PrefRegistrySyncable*)registry { |
| registry->RegisterInt64Pref(prefs::kIosDiscoverFeedLastRefreshTime, 0); |
| registry->RegisterInt64Pref(prefs::kIosDiscoverFeedLastUnseenRefreshTime, 0); |
| } |
| |
| - (void)disconnect { |
| _mostVisitedBridge.reset(); |
| _mostVisitedSites.reset(); |
| _readingListModelBridge.reset(); |
| _authenticationService = nullptr; |
| _authServiceObserverBridge.reset(); |
| _identityObserverBridge.reset(); |
| if (_prefObserverBridge) { |
| _prefChangeRegistrar.RemoveAll(); |
| _prefObserverBridge.reset(); |
| } |
| [_setUpList disconnect]; |
| _setUpList = nil; |
| SceneState* sceneState = |
| SceneStateBrowserAgent::FromBrowser(self.browser)->GetSceneState(); |
| [sceneState removeObserver:self]; |
| _localState = nullptr; |
| } |
| |
| - (void)refreshMostVisitedTiles { |
| // Refresh in case there are new MVT to show. |
| _mostVisitedSites->Refresh(); |
| } |
| |
| - (void)reloadAllData { |
| if (!self.consumer) { |
| return; |
| } |
| if (IsMagicStackEnabled()) { |
| if (base::FeatureList::IsEnabled( |
| segmentation_platform::features:: |
| kSegmentationPlatformIosModuleRanker)) { |
| [self fetchMagicStackModuleRankingFromSegmentationPlatform]; |
| } else { |
| [self.consumer setMagicStackOrder:[self magicStackOrder]]; |
| } |
| } |
| if (self.returnToRecentTabItem) { |
| [self.consumer |
| showReturnToRecentTabTileWithConfig:self.returnToRecentTabItem]; |
| } |
| if ([self.mostVisitedItems count] && !ShouldHideMVT()) { |
| [self.consumer setMostVisitedTilesWithConfigs:self.mostVisitedItems]; |
| } |
| if ([self shouldShowSetUpList]) { |
| self.setUpList.delegate = self; |
| NSArray<SetUpListItemViewData*>* items = [self setUpListItems]; |
| if (IsMagicStackEnabled() && [self.setUpList allItemsComplete]) { |
| SetUpListItemViewData* allSetItem = |
| [[SetUpListItemViewData alloc] initWithType:SetUpListItemType::kAllSet |
| complete:NO]; |
| [self.consumer showSetUpListWithItems:@[ allSetItem ]]; |
| } else { |
| [self.consumer showSetUpListWithItems:items]; |
| } |
| [self.contentSuggestionsMetricsRecorder recordSetUpListShown]; |
| for (SetUpListItemViewData* item in items) { |
| [self.contentSuggestionsMetricsRecorder |
| recordSetUpListItemShown:item.type]; |
| } |
| } |
| // Show shorcuts if: |
| // 1) Magic Stack is enabled (always show shortcuts in Magic Stack). |
| // 2) The Set Up List and Magic Stack are not enabled (Set Up List replaced |
| // Shortcuts). |
| if (!ShouldHideShortcuts() && |
| (IsMagicStackEnabled() || ![self shouldShowSetUpList])) { |
| [self.consumer setShortcutTilesWithConfigs:self.actionButtonItems]; |
| } |
| } |
| |
| - (void)blockMostVisitedURL:(GURL)URL { |
| _mostVisitedSites->AddOrRemoveBlockedUrl(URL, true); |
| [self useFreshMostVisited]; |
| } |
| |
| - (void)allowMostVisitedURL:(GURL)URL { |
| _mostVisitedSites->AddOrRemoveBlockedUrl(URL, false); |
| [self useFreshMostVisited]; |
| } |
| |
| - (void)setConsumer:(id<ContentSuggestionsConsumer>)consumer { |
| _consumer = consumer; |
| self.faviconMediator.consumer = consumer; |
| [self reloadAllData]; |
| } |
| |
| + (NSUInteger)maxSitesShown { |
| return kMaxNumMostVisitedTiles; |
| } |
| |
| - (void)configureMostRecentTabItemWithWebState:(web::WebState*)webState |
| timeLabel:(NSString*)timeLabel { |
| self.returnToRecentTabSectionInfo = ReturnToRecentTabSectionInformation(); |
| if (!self.returnToRecentTabItem) { |
| self.returnToRecentTabItem = |
| [[ContentSuggestionsReturnToRecentTabItem alloc] init]; |
| } |
| |
| // Retrieve favicon associated with the page. |
| favicon::WebFaviconDriver* driver = |
| favicon::WebFaviconDriver::FromWebState(webState); |
| if (driver->FaviconIsValid()) { |
| gfx::Image favicon = driver->GetFavicon(); |
| if (!favicon.IsEmpty()) { |
| self.returnToRecentTabItem.icon = favicon.ToUIImage(); |
| } |
| } |
| if (!self.returnToRecentTabItem.icon) { |
| driver->FetchFavicon(webState->GetLastCommittedURL(), false); |
| } |
| |
| self.returnToRecentTabItem.title = |
| l10n_util::GetNSString(IDS_IOS_RETURN_TO_RECENT_TAB_TITLE); |
| self.returnToRecentTabItem.subtitle = [self |
| constructReturnToRecentTabSubtitleWithPageTitle:base::SysUTF16ToNSString( |
| webState->GetTitle()) |
| timeString:timeLabel]; |
| self.showMostRecentTabStartSurfaceTile = YES; |
| [self.consumer |
| showReturnToRecentTabTileWithConfig:self.returnToRecentTabItem]; |
| } |
| |
| - (void)hideRecentTabTile { |
| if (self.showMostRecentTabStartSurfaceTile) { |
| self.showMostRecentTabStartSurfaceTile = NO; |
| self.returnToRecentTabItem = nil; |
| [self.consumer hideReturnToRecentTabTile]; |
| } |
| } |
| |
| - (void)disableSetUpList { |
| set_up_list_prefs::DisableSetUpList(_localState); |
| } |
| |
| #pragma mark - IdentityManagerObserverBridgeDelegate |
| |
| // Called when a user changes the syncing state. |
| - (void)onPrimaryAccountChanged: |
| (const signin::PrimaryAccountChangeEvent&)event { |
| switch (event.GetEventTypeFor(signin::ConsentLevel::kSignin)) { |
| case signin::PrimaryAccountChangeEvent::Type::kSet: |
| if (IsIOSSetUpListEnabled()) { |
| // User has signed in, mark SetUpList item complete. Delayed to allow |
| // Signin UI flow to be fully dismissed before starting SetUpList |
| // completion animation. |
| PrefService* localState = _localState; |
| base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE, base::BindOnce(^{ |
| set_up_list_prefs::MarkItemComplete( |
| localState, SetUpListItemType::kSignInSync); |
| }), |
| base::Seconds(0.5)); |
| } |
| break; |
| case signin::PrimaryAccountChangeEvent::Type::kCleared: |
| case signin::PrimaryAccountChangeEvent::Type::kNone: |
| break; |
| } |
| } |
| |
| #pragma mark - SetUpListDelegate |
| |
| - (void)setUpListItemDidComplete:(SetUpListItem*)item { |
| __weak __typeof(self) weakSelf = self; |
| ProceduralBlock completion = ^{ |
| if ([weakSelf.setUpList allItemsComplete]) { |
| [weakSelf.consumer showSetUpListDoneWithAnimations:^{ |
| if (!IsMagicStackEnabled()) { |
| [self.feedDelegate contentSuggestionsWasUpdated]; |
| } |
| }]; |
| } else if (IsMagicStackEnabled()) { |
| [self.consumer scrollToNextMagicStackModuleForCompletedModule: |
| SetUpListModuleTypeForSetUpListType(item.type)]; |
| } |
| }; |
| [self.consumer markSetUpListItemComplete:item.type completion:completion]; |
| } |
| |
| #pragma mark - ContentSuggestionsCommands |
| |
| - (void)openMostVisitedItem:(NSObject*)item |
| atIndex:(NSInteger)mostVisitedIndex { |
| // Checks if the item is a shortcut tile. Does not include Most Visited URL |
| // tiles. |
| if ([item isKindOfClass:[ContentSuggestionsMostVisitedActionItem class]]) { |
| ContentSuggestionsMostVisitedActionItem* mostVisitedItem = |
| base::apple::ObjCCastStrict<ContentSuggestionsMostVisitedActionItem>( |
| item); |
| if (mostVisitedItem.disabled) { |
| return; |
| } |
| [self.NTPMetricsDelegate shortcutTileOpened]; |
| [self.contentSuggestionsMetricsRecorder |
| recordShortcutTileTapped:mostVisitedItem.collectionShortcutType]; |
| switch (mostVisitedItem.collectionShortcutType) { |
| case NTPCollectionShortcutTypeBookmark: |
| LogBookmarkUseForDefaultBrowserPromo(); |
| [self.dispatcher showBookmarksManager]; |
| break; |
| case NTPCollectionShortcutTypeReadingList: |
| [self.dispatcher showReadingList]; |
| break; |
| case NTPCollectionShortcutTypeRecentTabs: |
| [self.dispatcher showRecentTabs]; |
| break; |
| case NTPCollectionShortcutTypeHistory: |
| [self.dispatcher showHistory]; |
| break; |
| case NTPCollectionShortcutTypeWhatsNew: |
| SetWhatsNewUsed(self.promosManager); |
| [self.dispatcher showWhatsNew]; |
| break; |
| case NTPCollectionShortcutTypeCount: |
| NOTREACHED(); |
| break; |
| } |
| return; |
| } |
| |
| ContentSuggestionsMostVisitedItem* mostVisitedItem = |
| base::apple::ObjCCastStrict<ContentSuggestionsMostVisitedItem>(item); |
| |
| [self logMostVisitedOpening:mostVisitedItem atIndex:mostVisitedIndex]; |
| |
| UrlLoadParams params = UrlLoadParams::InCurrentTab(mostVisitedItem.URL); |
| params.web_params.transition_type = ui::PAGE_TRANSITION_AUTO_BOOKMARK; |
| UrlLoadingBrowserAgent::FromBrowser(self.browser)->Load(params); |
| } |
| |
| - (void)openMostRecentTab { |
| [self.NTPMetricsDelegate recentTabTileOpened]; |
| [self.contentSuggestionsMetricsRecorder recordMostRecentTabOpened]; |
| [self hideRecentTabTile]; |
| WebStateList* web_state_list = self.browser->GetWebStateList(); |
| web::WebState* web_state = |
| StartSurfaceRecentTabBrowserAgent::FromBrowser(self.browser) |
| ->most_recent_tab(); |
| if (!web_state) { |
| return; |
| } |
| int index = web_state_list->GetIndexOfWebState(web_state); |
| web_state_list->ActivateWebStateAt(index); |
| } |
| |
| #pragma mark - ContentSuggestionsGestureCommands |
| |
| - (void)openNewTabWithMostVisitedItem:(ContentSuggestionsMostVisitedItem*)item |
| incognito:(BOOL)incognito |
| atIndex:(NSInteger)index |
| fromPoint:(CGPoint)point { |
| if (incognito && |
| IsIncognitoModeDisabled(self.browser->GetBrowserState()->GetPrefs())) { |
| // This should only happen when the policy changes while the option is |
| // presented. |
| return; |
| } |
| [self logMostVisitedOpening:item atIndex:index]; |
| [self openNewTabWithURL:item.URL incognito:incognito originPoint:point]; |
| } |
| |
| - (void)openNewTabWithMostVisitedItem:(ContentSuggestionsMostVisitedItem*)item |
| incognito:(BOOL)incognito |
| atIndex:(NSInteger)index { |
| if (incognito && |
| IsIncognitoModeDisabled(self.browser->GetBrowserState()->GetPrefs())) { |
| // This should only happen when the policy changes while the option is |
| // presented. |
| return; |
| } |
| [self logMostVisitedOpening:item atIndex:index]; |
| [self openNewTabWithURL:item.URL incognito:incognito originPoint:CGPointZero]; |
| } |
| |
| - (void)openNewTabWithMostVisitedItem:(ContentSuggestionsMostVisitedItem*)item |
| incognito:(BOOL)incognito { |
| [self openNewTabWithMostVisitedItem:item |
| incognito:incognito |
| atIndex:item.index]; |
| } |
| |
| - (void)removeMostVisited:(ContentSuggestionsMostVisitedItem*)item { |
| [self.contentSuggestionsMetricsRecorder recordMostVisitedTileRemoved]; |
| [self blockMostVisitedURL:item.URL]; |
| [self showMostVisitedUndoForURL:item.URL]; |
| } |
| |
| #pragma mark - StartSurfaceRecentTabObserving |
| |
| - (void)mostRecentTabWasRemoved:(web::WebState*)web_state { |
| [self hideRecentTabTile]; |
| } |
| |
| - (void)mostRecentTabFaviconUpdatedWithImage:(UIImage*)image { |
| if (self.returnToRecentTabItem) { |
| self.returnToRecentTabItem.icon = image; |
| [self.consumer |
| updateReturnToRecentTabTileWithConfig:self.returnToRecentTabItem]; |
| } |
| } |
| |
| - (void)mostRecentTabTitleWasUpdated:(NSString*)title { |
| if (self.returnToRecentTabItem) { |
| SceneState* scene = |
| SceneStateBrowserAgent::FromBrowser(self.browser)->GetSceneState(); |
| NSString* time_label = GetRecentTabTileTimeLabelForSceneState(scene); |
| self.returnToRecentTabItem.subtitle = |
| [self constructReturnToRecentTabSubtitleWithPageTitle:title |
| timeString:time_label]; |
| [self.consumer |
| updateReturnToRecentTabTileWithConfig:self.returnToRecentTabItem]; |
| } |
| } |
| |
| #pragma mark - MostVisitedSitesObserving |
| |
| - (void)onMostVisitedURLsAvailable: |
| (const ntp_tiles::NTPTilesVector&)mostVisited { |
| if (ShouldHideMVT()) { |
| return; |
| } |
| |
| // This is used by the content widget. |
| content_suggestions_tile_saver::SaveMostVisitedToDisk( |
| mostVisited, self.faviconMediator.mostVisitedAttributesProvider, |
| app_group::ContentWidgetFaviconsFolder()); |
| |
| self.freshMostVisitedItems = [NSMutableArray array]; |
| int index = 0; |
| for (const ntp_tiles::NTPTile& tile : mostVisited) { |
| ContentSuggestionsMostVisitedItem* item = |
| ConvertNTPTile(tile, self.mostVisitedSectionInfo); |
| item.commandHandler = self; |
| item.incognitoAvailable = self.incognitoAvailable; |
| item.index = index; |
| DCHECK(index < kShortcutMinimumIndex); |
| index++; |
| [self.faviconMediator fetchFaviconForMostVisited:item]; |
| [self.freshMostVisitedItems addObject:item]; |
| } |
| |
| [self useFreshMostVisited]; |
| |
| if (mostVisited.size() && !self.recordedPageImpression) { |
| self.recordedPageImpression = YES; |
| [self recordMostVisitedTilesDisplayed]; |
| [self.faviconMediator setMostVisitedDataForLogging:mostVisited]; |
| ntp_tiles::metrics::RecordPageImpression(mostVisited.size()); |
| } |
| } |
| |
| - (void)onIconMadeAvailable:(const GURL&)siteURL { |
| // This is used by the content widget. |
| content_suggestions_tile_saver::UpdateSingleFavicon( |
| siteURL, self.faviconMediator.mostVisitedAttributesProvider, |
| app_group::ContentWidgetFaviconsFolder()); |
| |
| for (ContentSuggestionsMostVisitedItem* item in self.mostVisitedItems) { |
| if (item.URL == siteURL) { |
| [self.faviconMediator fetchFaviconForMostVisited:item]; |
| return; |
| } |
| } |
| } |
| |
| #pragma mark - SceneStateObserver |
| |
| - (void)sceneState:(SceneState*)sceneState |
| transitionedToActivationLevel:(SceneActivationLevel)level { |
| if (level == SceneActivationLevelForegroundActive) { |
| if (IsIOSSetUpListEnabled() && _setUpList) { |
| [self checkIfCPEEnabled]; |
| } |
| } |
| } |
| #pragma mark - Private |
| |
| // Updates `prefs::kIosSyncSegmentsNewTabPageDisplayCount` with the number of |
| // remaining New Tab Page displays that include synced history in the Most |
| // Visited Tiles. |
| - (void)recordMostVisitedTilesDisplayed { |
| const int displayCount = |
| _localState->GetInteger(prefs::kIosSyncSegmentsNewTabPageDisplayCount) + |
| 1; |
| |
| _localState->SetInteger(prefs::kIosSyncSegmentsNewTabPageDisplayCount, |
| displayCount); |
| } |
| |
| // Replaces the Most Visited items currently displayed by the most recent ones. |
| - (void)useFreshMostVisited { |
| if (ShouldHideMVT()) { |
| return; |
| } |
| |
| if (IsMagicStackEnabled()) { |
| const base::Value::List& oldMostVisitedSites = |
| _localState->GetList(prefs::kIosLatestMostVisitedSites); |
| base::Value::List freshMostVisitedSites; |
| for (ContentSuggestionsMostVisitedItem* item in self |
| .freshMostVisitedItems) { |
| freshMostVisitedSites.Append(item.URL.spec()); |
| } |
| // Don't check for a change in the Most Visited Sites if the device doesn't |
| // have any saved sites to begin with. This will not log for users with no |
| // top sites that have a new top site, but the benefit of not logging for |
| // new installs outweighs it. |
| if (!oldMostVisitedSites.empty()) { |
| [self lookForNewMostVisitedSite:freshMostVisitedSites |
| oldMostVisitedSites:oldMostVisitedSites]; |
| } |
| _localState->SetList(prefs::kIosLatestMostVisitedSites, |
| std::move(freshMostVisitedSites)); |
| } |
| |
| self.mostVisitedItems = self.freshMostVisitedItems; |
| [self.consumer setMostVisitedTilesWithConfigs:self.mostVisitedItems]; |
| [self.feedDelegate contentSuggestionsWasUpdated]; |
| } |
| |
| // Logs a User Action if `freshMostVisitedSites` has at least one site that |
| // isn't in `oldMostVisitedSites`. |
| - (void) |
| lookForNewMostVisitedSite:(const base::Value::List&)freshMostVisitedSites |
| oldMostVisitedSites:(const base::Value::List&)oldMostVisitedSites { |
| for (auto const& freshSiteURLValue : freshMostVisitedSites) { |
| BOOL freshSiteInOldList = NO; |
| for (auto const& oldSiteURLValue : oldMostVisitedSites) { |
| if (freshSiteURLValue.GetString() == oldSiteURLValue.GetString()) { |
| freshSiteInOldList = YES; |
| break; |
| } |
| } |
| if (!freshSiteInOldList) { |
| // Reset impressions since freshness. |
| _localState->SetInteger( |
| prefs::kIosMagicStackSegmentationMVTImpressionsSinceFreshness, 0); |
| base::RecordAction( |
| base::UserMetricsAction("IOSMostVisitedTopSitesChanged")); |
| return; |
| } |
| } |
| } |
| |
| // Opens the `URL` in a new tab `incognito` or not. `originPoint` is the origin |
| // of the new tab animation if the tab is opened in background, in window |
| // coordinates. |
| - (void)openNewTabWithURL:(const GURL&)URL |
| incognito:(BOOL)incognito |
| originPoint:(CGPoint)originPoint { |
| // Open the tab in background if it is non-incognito only. |
| UrlLoadParams params = UrlLoadParams::InNewTab(URL); |
| params.SetInBackground(!incognito); |
| params.in_incognito = incognito; |
| params.append_to = OpenPosition::kCurrentTab; |
| params.origin_point = originPoint; |
| UrlLoadingBrowserAgent::FromBrowser(self.browser)->Load(params); |
| } |
| |
| // Logs a histogram due to a Most Visited item being opened. |
| - (void)logMostVisitedOpening:(ContentSuggestionsMostVisitedItem*)item |
| atIndex:(NSInteger)mostVisitedIndex { |
| [self.NTPMetricsDelegate mostVisitedTileOpened]; |
| [self.contentSuggestionsMetricsRecorder |
| recordMostVisitedTileOpened:item |
| atIndex:mostVisitedIndex |
| webState:self.webState]; |
| } |
| |
| // Shows a snackbar with an action to undo the removal of the most visited item |
| // with a `URL`. |
| - (void)showMostVisitedUndoForURL:(GURL)URL { |
| GURL copiedURL = URL; |
| |
| MDCSnackbarMessageAction* action = [[MDCSnackbarMessageAction alloc] init]; |
| __weak ContentSuggestionsMediator* weakSelf = self; |
| action.handler = ^{ |
| ContentSuggestionsMediator* strongSelf = weakSelf; |
| if (!strongSelf) |
| return; |
| [strongSelf allowMostVisitedURL:copiedURL]; |
| }; |
| action.title = l10n_util::GetNSString(IDS_NEW_TAB_UNDO_THUMBNAIL_REMOVE); |
| action.accessibilityIdentifier = @"Undo"; |
| |
| TriggerHapticFeedbackForNotification(UINotificationFeedbackTypeSuccess); |
| MDCSnackbarMessage* message = [MDCSnackbarMessage |
| messageWithText:l10n_util::GetNSString( |
| IDS_IOS_NEW_TAB_MOST_VISITED_ITEM_REMOVED)]; |
| message.action = action; |
| message.category = @"MostVisitedUndo"; |
| [self.dispatcher showSnackbarMessage:message]; |
| } |
| |
| - (NSString*)constructReturnToRecentTabSubtitleWithPageTitle: |
| (NSString*)pageTitle |
| timeString:(NSString*)time { |
| return [NSString stringWithFormat:@"%@%@", pageTitle, time]; |
| } |
| |
| - (BOOL)shouldShowWhatsNewActionItem { |
| if (WasWhatsNewUsed()) { |
| return NO; |
| } |
| |
| AuthenticationService* authService = |
| AuthenticationServiceFactory::GetForBrowserState( |
| self.browser->GetBrowserState()); |
| BOOL isSignedIn = |
| authService->HasPrimaryIdentity(signin::ConsentLevel::kSignin); |
| |
| return !isSignedIn; |
| } |
| |
| // Returns an array that represents the order of the modules to be shown in the |
| // Magic Stack. |
| - (NSArray<NSNumber*>*)magicStackOrder { |
| NSMutableArray* magicStackModules = [NSMutableArray array]; |
| if ([self shouldShowSetUpList]) { |
| [self addSetUpListToMagicStackOrder:magicStackModules]; |
| } |
| if (ShouldPutMostVisitedSitesInMagicStack()) { |
| [magicStackModules |
| addObject:@(int(ContentSuggestionsModuleType::kMostVisited))]; |
| } |
| [magicStackModules |
| addObject:@(int(ContentSuggestionsModuleType::kShortcuts))]; |
| |
| if (IsSafetyCheckMagicStackEnabled()) { |
| [self addSafetyCheckToMagicStackOrder:magicStackModules]; |
| } |
| |
| return magicStackModules; |
| } |
| |
| - (void)fetchMagicStackModuleRankingFromSegmentationPlatform { |
| auto input_context = |
| base::MakeRefCounted<segmentation_platform::InputContext>(); |
| int mvt_freshness_impression_count = _localState->GetInteger( |
| prefs::kIosMagicStackSegmentationMVTImpressionsSinceFreshness); |
| input_context->metadata_args.emplace( |
| segmentation_platform::kMostVisitedTilesFreshness, |
| segmentation_platform::processing::ProcessedValue::FromFloat( |
| mvt_freshness_impression_count)); |
| int shortcuts_freshness_impression_count = _localState->GetInteger( |
| prefs::kIosMagicStackSegmentationShortcutsImpressionsSinceFreshness); |
| input_context->metadata_args.emplace( |
| segmentation_platform::kShortcutsFreshness, |
| segmentation_platform::processing::ProcessedValue::FromFloat( |
| shortcuts_freshness_impression_count)); |
| int safety_check_freshness_impression_count = _localState->GetInteger( |
| prefs::kIosMagicStackSegmentationSafetyCheckImpressionsSinceFreshness); |
| input_context->metadata_args.emplace( |
| segmentation_platform::kSafetyCheckFreshness, |
| segmentation_platform::processing::ProcessedValue::FromFloat( |
| safety_check_freshness_impression_count)); |
| __weak ContentSuggestionsMediator* weakSelf = self; |
| segmentation_platform::PredictionOptions options; |
| options.on_demand_execution = true; |
| self.segmentationService->GetClassificationResult( |
| segmentation_platform::kIosModuleRankerKey, options, input_context, |
| base::BindOnce( |
| ^(const segmentation_platform::ClassificationResult& result) { |
| weakSelf.hasReceivedMagicStackResponse = YES; |
| [weakSelf didReceiveSegmentationServiceResult:result]; |
| })); |
| } |
| |
| - (void)didReceiveSegmentationServiceResult: |
| (const segmentation_platform::ClassificationResult&)result { |
| CHECK(IsMagicStackEnabled()); |
| if (result.status != segmentation_platform::PredictionStatus::kSucceeded) { |
| return; |
| } |
| |
| NSMutableArray* magicStackOrder = [NSMutableArray array]; |
| // Always add Set Up List at the front. |
| if ([self shouldShowSetUpList]) { |
| [self addSetUpListToMagicStackOrder:magicStackOrder]; |
| } |
| for (const std::string& label : result.ordered_labels) { |
| if (label == segmentation_platform::kMostVisitedTiles) { |
| [magicStackOrder |
| addObject:@(int(ContentSuggestionsModuleType::kMostVisited))]; |
| } else if (label == segmentation_platform::kShortcuts) { |
| [magicStackOrder |
| addObject:@(int(ContentSuggestionsModuleType::kShortcuts))]; |
| } |
| } |
| [self.consumer setMagicStackOrder:magicStackOrder]; |
| [self.feedDelegate contentSuggestionsWasUpdated]; |
| } |
| |
| - (void)addSetUpListToMagicStackOrder:(NSMutableArray*)order { |
| if (set_up_list_utils::ShouldShowCompactedSetUpListModule()) { |
| [order addObject:@(int(ContentSuggestionsModuleType::kCompactedSetUpList))]; |
| } else { |
| if ([self.setUpList allItemsComplete]) { |
| [order addObject:@(int(ContentSuggestionsModuleType::kSetUpListAllSet))]; |
| } else { |
| for (SetUpListItem* model in self.setUpList.items) { |
| [order |
| addObject:@(int(SetUpListModuleTypeForSetUpListType(model.type)))]; |
| } |
| } |
| } |
| } |
| |
| - (void)addSafetyCheckToMagicStackOrder:(NSMutableArray*)order { |
| CHECK(IsSafetyCheckMagicStackEnabled()); |
| |
| // TODO(crbug.com/1472382): In a follow-up CL, the module will be placed at |
| // different places in the Magic Stack depending on the Safety Check state(s). |
| // However, for now, the module will simply be inserted at the front of the |
| // Magic Stack. |
| [order insertObject:@(int(ContentSuggestionsModuleType::kSafetyCheck)) |
| atIndex:0]; |
| } |
| |
| // Returns YES if the conditions are right to display the Set Up List. |
| - (BOOL)shouldShowSetUpList { |
| if (!IsIOSSetUpListEnabled()) { |
| return NO; |
| } |
| if (!set_up_list_utils::IsSetUpListActive(_localState)) { |
| return NO; |
| } |
| |
| SetUpList* setUpList = self.setUpList; |
| if (!setUpList || setUpList.items.count == 0) { |
| return NO; |
| } |
| |
| return YES; |
| } |
| |
| // Returns an array of all possible items in the Set Up List. |
| - (NSArray<SetUpListItemViewData*>*)allSetUpListItems { |
| NSArray<SetUpListItem*>* items = [self.setUpList allItems]; |
| |
| NSMutableArray<SetUpListItemViewData*>* allItems = |
| [[NSMutableArray alloc] init]; |
| for (SetUpListItem* model in items) { |
| SetUpListItemViewData* item = |
| [[SetUpListItemViewData alloc] initWithType:model.type |
| complete:model.complete]; |
| [allItems addObject:item]; |
| } |
| return allItems; |
| } |
| |
| // Returns an array of items to display in the Set Up List. |
| - (NSArray<SetUpListItemViewData*>*)setUpListItems { |
| // Map the model objects to view objects. |
| NSMutableArray<SetUpListItemViewData*>* items = [[NSMutableArray alloc] init]; |
| for (SetUpListItem* model in self.setUpList.items) { |
| SetUpListItemViewData* item = |
| [[SetUpListItemViewData alloc] initWithType:model.type |
| complete:model.complete]; |
| [items addObject:item]; |
| } |
| // For the compacted Set Up List Module in the Magic Stack, there will only be |
| // two items shown. |
| if (IsMagicStackEnabled() && |
| set_up_list_utils::ShouldShowCompactedSetUpListModule() && |
| [items count] > 2) { |
| return [items subarrayWithRange:NSMakeRange(0, 2)]; |
| } |
| return items; |
| } |
| |
| // Checks if the CPE is enabled and marks the SetUpList Autofill item complete |
| // if it is. |
| - (void)checkIfCPEEnabled { |
| __weak __typeof(self) weakSelf = self; |
| scoped_refptr<base::SequencedTaskRunner> runner = |
| base::SequencedTaskRunner::GetCurrentDefault(); |
| [ASCredentialIdentityStore.sharedStore |
| getCredentialIdentityStoreStateWithCompletion:^( |
| ASCredentialIdentityStoreState* state) { |
| if (state.isEnabled) { |
| // The completion handler sent to ASCredentialIdentityStore is |
| // executed on a background thread. Putting it back onto the main |
| // thread to update local state prefs. |
| runner->PostTask(FROM_HERE, base::BindOnce(^{ |
| __typeof(self) strongSelf = weakSelf; |
| if (!strongSelf) { |
| return; |
| } |
| set_up_list_prefs::MarkItemComplete( |
| strongSelf->_localState, |
| SetUpListItemType::kAutofill); |
| })); |
| } |
| }]; |
| } |
| |
| // Hides the Set Up List with an animation. |
| - (void)hideSetUpList { |
| [self.consumer hideSetUpListWithAnimations:^{ |
| [self.feedDelegate contentSuggestionsWasUpdated]; |
| }]; |
| } |
| |
| #pragma mark - Properties |
| |
| - (NSArray<ContentSuggestionsMostVisitedActionItem*>*)actionButtonItems { |
| if (!_actionButtonItems) { |
| self.readingListItem = ReadingListActionItem(); |
| self.readingListItem.count = self.readingListUnreadCount; |
| self.readingListItem.disabled = !self.readingListModelIsLoaded; |
| _actionButtonItems = @[ |
| [self shouldShowWhatsNewActionItem] ? WhatsNewActionItem() |
| : BookmarkActionItem(), |
| self.readingListItem, RecentTabsActionItem(), HistoryActionItem() |
| ]; |
| for (ContentSuggestionsMostVisitedActionItem* item in _actionButtonItems) { |
| item.accessibilityTraits = UIAccessibilityTraitButton; |
| } |
| } |
| return _actionButtonItems; |
| } |
| |
| - (void)setCommandHandler: |
| (id<ContentSuggestionsCommands, ContentSuggestionsGestureCommands>) |
| commandHandler { |
| if (_commandHandler == commandHandler) |
| return; |
| |
| _commandHandler = commandHandler; |
| |
| for (ContentSuggestionsMostVisitedItem* item in self.freshMostVisitedItems) { |
| item.commandHandler = commandHandler; |
| } |
| } |
| |
| - (void)setContentSuggestionsMetricsRecorder: |
| (ContentSuggestionsMetricsRecorder*)contentSuggestionsMetricsRecorder { |
| CHECK(self.faviconMediator); |
| _contentSuggestionsMetricsRecorder = contentSuggestionsMetricsRecorder; |
| self.faviconMediator.contentSuggestionsMetricsRecorder = |
| self.contentSuggestionsMetricsRecorder; |
| } |
| |
| - (BOOL)contentSuggestionsEnabled { |
| return self.articleForYouEnabled->GetValue()->GetBool() && |
| self.contentSuggestionsPolicyEnabled->GetValue()->GetBool(); |
| } |
| |
| #pragma mark - PrefObserverDelegate |
| |
| - (void)onPreferenceChanged:(const std::string&)preferenceName { |
| if (IsIOSSetUpListEnabled()) { |
| if (preferenceName == prefs::kIosCredentialProviderPromoLastActionTaken && |
| CredentialProviderPromoDismissed(_localState)) { |
| set_up_list_prefs::MarkItemComplete(_localState, |
| SetUpListItemType::kAutofill); |
| } else if (preferenceName == set_up_list_prefs::kDisabled && |
| set_up_list_prefs::IsSetUpListDisabled(_localState)) { |
| [self hideSetUpList]; |
| } |
| } |
| } |
| |
| #pragma mark - ReadingListModelBridgeObserver |
| |
| - (void)readingListModelLoaded:(const ReadingListModel*)model { |
| [self readingListModelDidApplyChanges:model]; |
| } |
| |
| - (void)readingListModelDidApplyChanges:(const ReadingListModel*)model { |
| self.readingListUnreadCount = model->unread_size(); |
| self.readingListModelIsLoaded = model->loaded(); |
| if (self.readingListItem) { |
| self.readingListItem.count = self.readingListUnreadCount; |
| self.readingListItem.disabled = !self.readingListModelIsLoaded; |
| [self.consumer updateShortcutTileConfig:self.readingListItem]; |
| } |
| } |
| |
| #pragma mark - SyncObserverModelBridge |
| |
| - (void)onSyncStateChanged { |
| if (!_setUpList) { |
| return; |
| } |
| if (_syncService->HasDisableReason( |
| syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY) || |
| HasManagedSyncDataType(_syncService)) { |
| // Sync is now disabled, so mark the SetUpList item complete so that it |
| // cannot be used again. |
| set_up_list_prefs::MarkItemComplete(_localState, |
| SetUpListItemType::kSignInSync); |
| } |
| } |
| |
| #pragma mark - AuthenticationServiceObserving |
| |
| - (void)onServiceStatusChanged { |
| if (_setUpList) { |
| switch (_authenticationService->GetServiceStatus()) { |
| case AuthenticationService::ServiceStatus::SigninForcedByPolicy: |
| case AuthenticationService::ServiceStatus::SigninAllowed: |
| break; |
| case AuthenticationService::ServiceStatus::SigninDisabledByUser: |
| case AuthenticationService::ServiceStatus::SigninDisabledByPolicy: |
| case AuthenticationService::ServiceStatus::SigninDisabledByInternal: |
| // Signin is now disabled, so mark the SetUpList item complete so that |
| // it cannot be used again. |
| set_up_list_prefs::MarkItemComplete(_localState, |
| SetUpListItemType::kSignInSync); |
| } |
| } |
| } |
| |
| @end |