1// Copyright 2019 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#import "ios/chrome/browser/ui/settings/google_services/manage_sync_settings_mediator.h" 6 7#include "base/auto_reset.h" 8#include "base/check_op.h" 9#include "base/mac/foundation_util.h" 10#include "base/notreached.h" 11#include "components/autofill/core/common/autofill_prefs.h" 12#import "components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h" 13#include "components/strings/grit/components_strings.h" 14#include "components/sync/driver/sync_service.h" 15#import "ios/chrome/browser/signin/authentication_service.h" 16#include "ios/chrome/browser/sync/profile_sync_service_factory.h" 17#include "ios/chrome/browser/sync/sync_observer_bridge.h" 18#include "ios/chrome/browser/sync/sync_setup_service.h" 19#import "ios/chrome/browser/ui/list_model/list_model.h" 20#import "ios/chrome/browser/ui/settings/cells/settings_image_detail_text_item.h" 21#import "ios/chrome/browser/ui/settings/cells/sync_switch_item.h" 22#import "ios/chrome/browser/ui/settings/google_services/manage_sync_settings_command_handler.h" 23#import "ios/chrome/browser/ui/settings/google_services/manage_sync_settings_constants.h" 24#import "ios/chrome/browser/ui/settings/google_services/manage_sync_settings_consumer.h" 25#import "ios/chrome/browser/ui/settings/google_services/sync_error_settings_command_handler.h" 26#import "ios/chrome/browser/ui/settings/sync/utils/sync_util.h" 27#import "ios/chrome/browser/ui/settings/utils/pref_backed_boolean.h" 28#import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h" 29#import "ios/chrome/browser/ui/table_view/cells/table_view_image_item.h" 30#import "ios/chrome/browser/ui/table_view/cells/table_view_item.h" 31#import "ios/chrome/browser/ui/util/uikit_ui_util.h" 32#import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h" 33#include "ios/chrome/grit/ios_chromium_strings.h" 34#include "ios/chrome/grit/ios_strings.h" 35#include "ui/base/l10n/l10n_util.h" 36 37#if !defined(__has_feature) || !__has_feature(objc_arc) 38#error "This file requires ARC support." 39#endif 40 41using l10n_util::GetNSString; 42 43namespace { 44 45// Enterprise icon. 46NSString* kGoogleServicesEnterpriseImage = @"google_services_enterprise"; 47// Sync error icon. 48NSString* kGoogleServicesSyncErrorImage = @"google_services_sync_error"; 49} // namespace 50 51@interface ManageSyncSettingsMediator () <BooleanObserver, 52 IdentityManagerObserverBridgeDelegate, 53 SyncObserverModelBridge> { 54 // Sync observer. 55 std::unique_ptr<SyncObserverBridge> _syncObserver; 56 // Whether Sync State changes should be currently ignored. 57 BOOL _ignoreSyncStateChanges; 58} 59 60// Preference value for kAutofillWalletImportEnabled. 61@property(nonatomic, strong, readonly) 62 PrefBackedBoolean* autocompleteWalletPreference; 63// Sync service. 64@property(nonatomic, assign) syncer::SyncService* syncService; 65// Model item for sync everything. 66@property(nonatomic, strong) SyncSwitchItem* syncEverythingItem; 67// Model item for each data types. 68@property(nonatomic, strong) NSArray<SyncSwitchItem*>* syncSwitchItems; 69// Autocomplete wallet item. 70@property(nonatomic, strong) SyncSwitchItem* autocompleteWalletItem; 71// Encryption item. 72@property(nonatomic, strong) TableViewImageItem* encryptionItem; 73// Sync error item. 74@property(nonatomic, strong) TableViewItem* syncErrorItem; 75// Returns YES if the sync data items should be enabled. 76@property(nonatomic, assign, readonly) BOOL shouldSyncDataItemEnabled; 77// Returns whether the Sync settings should be disabled because of a Sync error. 78@property(nonatomic, assign, readonly) BOOL disabledBecauseOfSyncError; 79// Returns YES if the user cannot turn on sync for enterprise policy reasons. 80@property(nonatomic, assign, readonly) BOOL isSyncDisabledByAdministrator; 81// Returns YES if the user is authenticated. 82@property(nonatomic, assign, readonly) BOOL isAuthenticated; 83 84@end 85 86@implementation ManageSyncSettingsMediator 87 88- (instancetype)initWithSyncService:(syncer::SyncService*)syncService 89 userPrefService:(PrefService*)userPrefService { 90 self = [super init]; 91 if (self) { 92 DCHECK(syncService); 93 self.syncService = syncService; 94 _syncObserver.reset(new SyncObserverBridge(self, syncService)); 95 _autocompleteWalletPreference = [[PrefBackedBoolean alloc] 96 initWithPrefService:userPrefService 97 prefName:autofill::prefs::kAutofillWalletImportEnabled]; 98 _autocompleteWalletPreference.observer = self; 99 } 100 return self; 101} 102 103#pragma mark - Loads sync data type section 104 105// Loads the sync data type section. 106- (void)loadSyncDataTypeSection { 107 TableViewModel* model = self.consumer.tableViewModel; 108 [model addSectionWithIdentifier:SyncDataTypeSectionIdentifier]; 109 self.syncEverythingItem = 110 [[SyncSwitchItem alloc] initWithType:SyncEverythingItemType]; 111 self.syncEverythingItem.text = GetNSString(IDS_IOS_SYNC_EVERYTHING_TITLE); 112 [self updateSyncEverythingItemNotifyConsumer:NO]; 113 [model addItem:self.syncEverythingItem 114 toSectionWithIdentifier:SyncDataTypeSectionIdentifier]; 115 self.syncSwitchItems = @[ 116 [self switchItemWithDataType:SyncSetupService::kSyncAutofill], 117 [self switchItemWithDataType:SyncSetupService::kSyncBookmarks], 118 [self switchItemWithDataType:SyncSetupService::kSyncOmniboxHistory], 119 [self switchItemWithDataType:SyncSetupService::kSyncOpenTabs], 120 [self switchItemWithDataType:SyncSetupService::kSyncPasswords], 121 [self switchItemWithDataType:SyncSetupService::kSyncReadingList], 122 [self switchItemWithDataType:SyncSetupService::kSyncPreferences] 123 ]; 124 for (SyncSwitchItem* switchItem in self.syncSwitchItems) { 125 [model addItem:switchItem 126 toSectionWithIdentifier:SyncDataTypeSectionIdentifier]; 127 } 128 self.autocompleteWalletItem = 129 [[SyncSwitchItem alloc] initWithType:AutocompleteWalletItemType]; 130 self.autocompleteWalletItem.text = 131 GetNSString(IDS_AUTOFILL_ENABLE_PAYMENTS_INTEGRATION_CHECKBOX_LABEL); 132 [model addItem:self.autocompleteWalletItem 133 toSectionWithIdentifier:SyncDataTypeSectionIdentifier]; 134 [self updateSyncItemsNotifyConsumer:NO]; 135} 136 137// Updates the sync everything item, and notify the consumer if |notifyConsumer| 138// is set to YES. 139- (void)updateSyncEverythingItemNotifyConsumer:(BOOL)notifyConsumer { 140 BOOL shouldSyncEverythingBeEditable = 141 self.syncSetupService->IsSyncEnabled() && 142 (!self.disabledBecauseOfSyncError || self.syncSettingsNotConfirmed); 143 BOOL shouldSyncEverythingItemBeOn = 144 self.syncSetupService->IsSyncEnabled() && 145 self.syncSetupService->IsSyncingAllDataTypes(); 146 BOOL needsUpdate = 147 (self.syncEverythingItem.on != shouldSyncEverythingItemBeOn) || 148 (self.syncEverythingItem.enabled != shouldSyncEverythingBeEditable); 149 self.syncEverythingItem.on = shouldSyncEverythingItemBeOn; 150 self.syncEverythingItem.enabled = shouldSyncEverythingBeEditable; 151 if (needsUpdate && notifyConsumer) { 152 [self.consumer reloadItem:self.syncEverythingItem]; 153 } 154} 155 156// Updates all the items related to sync (sync data items and autocomplete 157// wallet item). The consumer is notified if |notifyConsumer| is set to YES. 158- (void)updateSyncItemsNotifyConsumer:(BOOL)notifyConsumer { 159 [self updateSyncDataItemsNotifyConsumer:notifyConsumer]; 160 [self updateAutocompleteWalletItemNotifyConsumer:notifyConsumer]; 161} 162 163// Updates all the sync data type items, and notify the consumer if 164// |notifyConsumer| is set to YES. 165- (void)updateSyncDataItemsNotifyConsumer:(BOOL)notifyConsumer { 166 for (SyncSwitchItem* syncSwitchItem in self.syncSwitchItems) { 167 SyncSetupService::SyncableDatatype dataType = 168 static_cast<SyncSetupService::SyncableDatatype>( 169 syncSwitchItem.dataType); 170 syncer::ModelType modelType = self.syncSetupService->GetModelType(dataType); 171 BOOL isDataTypeSynced = 172 self.syncSetupService->IsDataTypePreferred(modelType); 173 BOOL needsUpdate = 174 (syncSwitchItem.on != isDataTypeSynced) || 175 (syncSwitchItem.isEnabled != self.shouldSyncDataItemEnabled); 176 syncSwitchItem.on = isDataTypeSynced; 177 syncSwitchItem.enabled = self.shouldSyncDataItemEnabled; 178 if (needsUpdate && notifyConsumer) { 179 [self.consumer reloadItem:syncSwitchItem]; 180 } 181 } 182} 183 184// Updates the autocomplete wallet item. The consumer is notified if 185// |notifyConsumer| is set to YES. 186- (void)updateAutocompleteWalletItemNotifyConsumer:(BOOL)notifyConsumer { 187 syncer::ModelType autofillModelType = 188 self.syncSetupService->GetModelType(SyncSetupService::kSyncAutofill); 189 BOOL isAutofillOn = 190 self.syncSetupService->IsDataTypePreferred(autofillModelType); 191 BOOL autocompleteWalletEnabled = 192 isAutofillOn && self.shouldSyncDataItemEnabled; 193 BOOL autocompleteWalletOn = self.autocompleteWalletPreference.value; 194 BOOL needsUpdate = 195 (self.autocompleteWalletItem.enabled != autocompleteWalletEnabled) || 196 (self.autocompleteWalletItem.on != autocompleteWalletOn); 197 self.autocompleteWalletItem.enabled = autocompleteWalletEnabled; 198 self.autocompleteWalletItem.on = autocompleteWalletOn; 199 if (needsUpdate && notifyConsumer) { 200 [self.consumer reloadItem:self.autocompleteWalletItem]; 201 } 202} 203 204#pragma mark - Loads the advanced settings section 205 206// Loads the advanced settings section. 207- (void)loadAdvancedSettingsSection { 208 TableViewModel* model = self.consumer.tableViewModel; 209 [model addSectionWithIdentifier:AdvancedSettingsSectionIdentifier]; 210 // EncryptionItemType. 211 self.encryptionItem = 212 [[TableViewImageItem alloc] initWithType:EncryptionItemType]; 213 self.encryptionItem.title = GetNSString(IDS_IOS_MANAGE_SYNC_ENCRYPTION); 214 // For kSyncServiceNeedsTrustedVaultKey, the disclosure indicator should not 215 // be shown since the reauth dialog for the trusted vault is presented from 216 // the bottom, and is not part of navigation controller. 217 BOOL hasDisclosureIndicator = 218 self.syncSetupService->GetSyncServiceState() != 219 SyncSetupService::kSyncServiceNeedsTrustedVaultKey; 220 self.encryptionItem.accessoryType = 221 hasDisclosureIndicator ? UITableViewCellAccessoryDisclosureIndicator 222 : UITableViewCellAccessoryNone; 223 [model addItem:self.encryptionItem 224 toSectionWithIdentifier:AdvancedSettingsSectionIdentifier]; 225 [self updateEncryptionItem:NO]; 226 227 // GoogleActivityControlsItemType. 228 TableViewImageItem* googleActivityControlsItem = 229 [[TableViewImageItem alloc] initWithType:GoogleActivityControlsItemType]; 230 googleActivityControlsItem.title = 231 GetNSString(IDS_IOS_MANAGE_SYNC_GOOGLE_ACTIVITY_CONTROLS_TITLE); 232 googleActivityControlsItem.detailText = 233 GetNSString(IDS_IOS_MANAGE_SYNC_GOOGLE_ACTIVITY_CONTROLS_DESCRIPTION); 234 googleActivityControlsItem.accessibilityTraits |= UIAccessibilityTraitButton; 235 [model addItem:googleActivityControlsItem 236 toSectionWithIdentifier:AdvancedSettingsSectionIdentifier]; 237 238 // AdvancedSettingsSectionIdentifier. 239 TableViewImageItem* dataFromChromeSyncItem = 240 [[TableViewImageItem alloc] initWithType:DataFromChromeSync]; 241 dataFromChromeSyncItem.title = 242 GetNSString(IDS_IOS_MANAGE_SYNC_DATA_FROM_CHROME_SYNC_TITLE); 243 dataFromChromeSyncItem.detailText = 244 GetNSString(IDS_IOS_MANAGE_SYNC_DATA_FROM_CHROME_SYNC_DESCRIPTION); 245 dataFromChromeSyncItem.accessibilityIdentifier = 246 kDataFromChromeSyncAccessibilityIdentifier; 247 dataFromChromeSyncItem.accessibilityTraits |= UIAccessibilityTraitButton; 248 [model addItem:dataFromChromeSyncItem 249 toSectionWithIdentifier:AdvancedSettingsSectionIdentifier]; 250} 251 252// Updates encryption item, and notifies the consumer if |notifyConsumer| is set 253// to YES. 254- (void)updateEncryptionItem:(BOOL)notifyConsumer { 255 BOOL needsUpdate = 256 self.shouldEncryptionItemBeEnabled && 257 (self.encryptionItem.enabled != self.shouldEncryptionItemBeEnabled); 258 if (self.shouldEncryptionItemBeEnabled && 259 self.syncSetupService->GetSyncServiceState() == 260 SyncSetupService::kSyncServiceNeedsPassphrase) { 261 needsUpdate = needsUpdate || self.encryptionItem.image == nil; 262 self.encryptionItem.image = 263 [UIImage imageNamed:kGoogleServicesSyncErrorImage]; 264 self.encryptionItem.detailText = GetNSString( 265 IDS_IOS_GOOGLE_SERVICES_SETTINGS_ENTER_PASSPHRASE_TO_START_SYNC); 266 } else if (self.shouldEncryptionItemBeEnabled && 267 self.syncSetupService->GetSyncServiceState() == 268 SyncSetupService::kSyncServiceNeedsTrustedVaultKey) { 269 needsUpdate = needsUpdate || self.encryptionItem.image == nil; 270 self.encryptionItem.image = 271 [UIImage imageNamed:kGoogleServicesSyncErrorImage]; 272 self.encryptionItem.detailText = 273 GetNSString(self.syncSetupService->IsEncryptEverythingEnabled() 274 ? IDS_IOS_SYNC_ERROR_DESCRIPTION 275 : IDS_IOS_SYNC_PASSWORDS_ERROR_DESCRIPTION); 276 } else { 277 needsUpdate = needsUpdate || self.encryptionItem.image != nil; 278 self.encryptionItem.image = nil; 279 self.encryptionItem.detailText = nil; 280 } 281 self.encryptionItem.enabled = self.shouldEncryptionItemBeEnabled; 282 if (self.shouldEncryptionItemBeEnabled) { 283 self.encryptionItem.textColor = nil; 284 } else { 285 self.encryptionItem.textColor = UIColor.cr_secondaryLabelColor; 286 } 287 if (needsUpdate && notifyConsumer) { 288 [self.consumer reloadItem:self.self.encryptionItem]; 289 } 290} 291 292#pragma mark - Private 293 294// Creates a SyncSwitchItem instance. 295- (SyncSwitchItem*)switchItemWithDataType: 296 (SyncSetupService::SyncableDatatype)dataType { 297 NSInteger itemType = 0; 298 int textStringID = 0; 299 switch (dataType) { 300 case SyncSetupService::kSyncBookmarks: 301 itemType = BookmarksDataTypeItemType; 302 textStringID = IDS_SYNC_DATATYPE_BOOKMARKS; 303 break; 304 case SyncSetupService::kSyncOmniboxHistory: 305 itemType = HistoryDataTypeItemType; 306 textStringID = IDS_SYNC_DATATYPE_TYPED_URLS; 307 break; 308 case SyncSetupService::kSyncPasswords: 309 itemType = PasswordsDataTypeItemType; 310 textStringID = IDS_SYNC_DATATYPE_PASSWORDS; 311 break; 312 case SyncSetupService::kSyncOpenTabs: 313 itemType = OpenTabsDataTypeItemType; 314 textStringID = IDS_SYNC_DATATYPE_TABS; 315 break; 316 case SyncSetupService::kSyncAutofill: 317 itemType = AutofillDataTypeItemType; 318 textStringID = IDS_SYNC_DATATYPE_AUTOFILL; 319 break; 320 case SyncSetupService::kSyncPreferences: 321 itemType = SettingsDataTypeItemType; 322 textStringID = IDS_SYNC_DATATYPE_PREFERENCES; 323 break; 324 case SyncSetupService::kSyncReadingList: 325 itemType = ReadingListDataTypeItemType; 326 textStringID = IDS_SYNC_DATATYPE_READING_LIST; 327 break; 328 case SyncSetupService::kNumberOfSyncableDatatypes: 329 NOTREACHED(); 330 break; 331 } 332 DCHECK_NE(itemType, 0); 333 DCHECK_NE(textStringID, 0); 334 SyncSwitchItem* switchItem = [[SyncSwitchItem alloc] initWithType:itemType]; 335 switchItem.text = GetNSString(textStringID); 336 switchItem.dataType = dataType; 337 return switchItem; 338} 339 340#pragma mark - Properties 341 342- (BOOL)syncSettingsNotConfirmed { 343 SyncSetupService::SyncServiceState state = 344 self.syncSetupService->GetSyncServiceState(); 345 return state == SyncSetupService::kSyncSettingsNotConfirmed; 346} 347 348- (BOOL)disabledBecauseOfSyncError { 349 SyncSetupService::SyncServiceState state = 350 self.syncSetupService->GetSyncServiceState(); 351 return state != SyncSetupService::kNoSyncServiceError && 352 state != SyncSetupService::kSyncServiceNeedsPassphrase && 353 state != SyncSetupService::kSyncServiceNeedsTrustedVaultKey; 354} 355 356- (BOOL)shouldSyncDataItemEnabled { 357 return (!self.syncSetupService->IsSyncingAllDataTypes() && 358 self.syncSetupService->IsSyncEnabled() && 359 (!self.disabledBecauseOfSyncError || self.syncSettingsNotConfirmed)); 360} 361 362- (BOOL)shouldEncryptionItemBeEnabled { 363 return self.syncService->IsEngineInitialized() && 364 self.syncSetupService->IsSyncEnabled() && 365 !self.disabledBecauseOfSyncError; 366} 367 368#pragma mark - ManageSyncSettingsTableViewControllerModelDelegate 369 370- (void)manageSyncSettingsTableViewControllerLoadModel: 371 (id<ManageSyncSettingsConsumer>)controller { 372 DCHECK_EQ(self.consumer, controller); 373 [self loadSyncErrorsSection]; 374 [self loadSyncDataTypeSection]; 375 [self loadAdvancedSettingsSection]; 376} 377 378#pragma mark - BooleanObserver 379 380- (void)booleanDidChange:(id<ObservableBoolean>)observableBoolean { 381 [self updateAutocompleteWalletItemNotifyConsumer:YES]; 382} 383 384#pragma mark - SyncObserverModelBridge 385 386- (void)onSyncStateChanged { 387 if (_ignoreSyncStateChanges) { 388 // The UI should not updated so the switch animations can run smoothly. 389 return; 390 } 391 [self updateSyncErrorsSection:YES]; 392 [self updateSyncEverythingItemNotifyConsumer:YES]; 393 [self updateSyncItemsNotifyConsumer:YES]; 394 [self updateEncryptionItem:YES]; 395} 396 397#pragma mark - IdentityManagerObserverBridgeDelegate 398 399- (void)onPrimaryAccountSet:(const CoreAccountInfo&)primaryAccountInfo { 400 [self updateSyncErrorsSection:YES]; 401} 402 403- (void)onPrimaryAccountCleared: 404 (const CoreAccountInfo&)previousPrimaryAccountInfo { 405 [self updateSyncErrorsSection:YES]; 406} 407 408#pragma mark - ManageSyncSettingsServiceDelegate 409 410- (void)toggleSwitchItem:(TableViewItem*)item withValue:(BOOL)value { 411 { 412 // The notifications should be ignored to get smooth switch animations. 413 // Notifications are sent by SyncObserverModelBridge while changing 414 // settings. 415 base::AutoReset<BOOL> autoReset(&_ignoreSyncStateChanges, YES); 416 SyncSwitchItem* syncSwitchItem = base::mac::ObjCCast<SyncSwitchItem>(item); 417 syncSwitchItem.on = value; 418 SyncSettingsItemType itemType = 419 static_cast<SyncSettingsItemType>(item.type); 420 switch (itemType) { 421 case SyncEverythingItemType: 422 self.syncSetupService->SetSyncingAllDataTypes(value); 423 if (value) { 424 // When sync everything is turned on, the autocomplete wallet 425 // should be turned on. This code can be removed once 426 // crbug.com/937234 is fixed. 427 self.autocompleteWalletPreference.value = true; 428 } 429 break; 430 case AutofillDataTypeItemType: 431 case BookmarksDataTypeItemType: 432 case HistoryDataTypeItemType: 433 case OpenTabsDataTypeItemType: 434 case PasswordsDataTypeItemType: 435 case ReadingListDataTypeItemType: 436 case SettingsDataTypeItemType: { 437 DCHECK(syncSwitchItem); 438 SyncSetupService::SyncableDatatype dataType = 439 static_cast<SyncSetupService::SyncableDatatype>( 440 syncSwitchItem.dataType); 441 syncer::ModelType modelType = 442 self.syncSetupService->GetModelType(dataType); 443 self.syncSetupService->SetDataTypeEnabled(modelType, value); 444 if (dataType == SyncSetupService::kSyncAutofill) { 445 // When the auto fill data type is updated, the autocomplete wallet 446 // should be updated too. Autocomplete wallet should not be enabled 447 // when auto fill data type disabled. This behaviour not be 448 // implemented in the UI code. This code can be removed once 449 // crbug.com/937234 is fixed. 450 self.autocompleteWalletPreference.value = value; 451 } 452 break; 453 } 454 case AutocompleteWalletItemType: 455 self.autocompleteWalletPreference.value = value; 456 break; 457 case EncryptionItemType: 458 case GoogleActivityControlsItemType: 459 case DataFromChromeSync: 460 case RestartAuthenticationFlowErrorItemType: 461 case ReauthDialogAsSyncIsInAuthErrorItemType: 462 case ShowPassphraseDialogErrorItemType: 463 case SyncNeedsTrustedVaultKeyErrorItemType: 464 case SyncDisabledByAdministratorErrorItemType: 465 NOTREACHED(); 466 break; 467 } 468 } 469 [self updateSyncEverythingItemNotifyConsumer:YES]; 470 [self updateSyncItemsNotifyConsumer:YES]; 471} 472 473- (void)didSelectItem:(TableViewItem*)item { 474 SyncSettingsItemType itemType = static_cast<SyncSettingsItemType>(item.type); 475 switch (itemType) { 476 case EncryptionItemType: 477 if (self.syncSetupService->GetSyncServiceState() == 478 SyncSetupService::kSyncServiceNeedsTrustedVaultKey) { 479 [self.syncErrorHandler openTrustedVaultReauth]; 480 break; 481 } 482 [self.syncErrorHandler openPassphraseDialog]; 483 break; 484 case GoogleActivityControlsItemType: 485 [self.commandHandler openWebAppActivityDialog]; 486 break; 487 case DataFromChromeSync: 488 [self.commandHandler openDataFromChromeSyncWebPage]; 489 break; 490 case RestartAuthenticationFlowErrorItemType: 491 [self.syncErrorHandler restartAuthenticationFlow]; 492 break; 493 case ReauthDialogAsSyncIsInAuthErrorItemType: 494 [self.syncErrorHandler openReauthDialogAsSyncIsInAuthError]; 495 break; 496 case ShowPassphraseDialogErrorItemType: 497 [self.syncErrorHandler openPassphraseDialog]; 498 break; 499 case SyncNeedsTrustedVaultKeyErrorItemType: 500 [self.syncErrorHandler openTrustedVaultReauth]; 501 break; 502 case SyncEverythingItemType: 503 case AutofillDataTypeItemType: 504 case BookmarksDataTypeItemType: 505 case HistoryDataTypeItemType: 506 case OpenTabsDataTypeItemType: 507 case PasswordsDataTypeItemType: 508 case ReadingListDataTypeItemType: 509 case SettingsDataTypeItemType: 510 case AutocompleteWalletItemType: 511 case SyncDisabledByAdministratorErrorItemType: 512 // Nothing to do. 513 break; 514 } 515} 516 517// Creates an item to display the sync error. |itemType| should only be one of 518// those types: 519// + RestartAuthenticationFlowErrorItemType 520// + ReauthDialogAsSyncIsInAuthErrorItemType 521// + ShowPassphraseDialogErrorItemType 522// + SyncNeedsTrustedVaultKeyErrorItemType 523- (TableViewItem*)createSyncErrorItemWithItemType:(NSInteger)itemType { 524 DCHECK(itemType == RestartAuthenticationFlowErrorItemType || 525 itemType == ReauthDialogAsSyncIsInAuthErrorItemType || 526 itemType == ShowPassphraseDialogErrorItemType || 527 itemType == SyncNeedsTrustedVaultKeyErrorItemType); 528 SettingsImageDetailTextItem* syncErrorItem = 529 [[SettingsImageDetailTextItem alloc] initWithType:itemType]; 530 syncErrorItem.text = GetNSString(IDS_IOS_SYNC_ERROR_TITLE); 531 syncErrorItem.detailText = 532 GetSyncErrorDescriptionForSyncSetupService(self.syncSetupService); 533 if (itemType == ShowPassphraseDialogErrorItemType) { 534 // Special case only for the sync passphrase error message. The regular 535 // error message should be still be displayed in the first settings screen. 536 syncErrorItem.detailText = GetNSString( 537 IDS_IOS_GOOGLE_SERVICES_SETTINGS_ENTER_PASSPHRASE_TO_START_SYNC); 538 } else if (itemType == SyncNeedsTrustedVaultKeyErrorItemType) { 539 // Special case only for the sync encryption key error message. The regular 540 // error message should be still be displayed in the first settings screen. 541 syncErrorItem.detailText = 542 GetNSString(IDS_IOS_GOOGLE_SERVICES_SETTINGS_SYNC_ENCRYPTION_FIX_NOW); 543 544 // Also override the title to be more accurate, if only passwords are being 545 // encrypted. 546 if (!self.syncSetupService->IsEncryptEverythingEnabled()) { 547 syncErrorItem.text = GetNSString(IDS_IOS_SYNC_PASSWORDS_ERROR_TITLE); 548 } 549 } 550 syncErrorItem.image = [UIImage imageNamed:kGoogleServicesSyncErrorImage]; 551 return syncErrorItem; 552} 553 554// Loads the sync errors section. 555- (void)loadSyncErrorsSection { 556 [self.consumer.tableViewModel 557 addSectionWithIdentifier:SyncErrorsSectionIdentifier]; 558 [self updateSyncErrorsSection:NO]; 559} 560 561// Updates the sync errors section. If |notifyConsumer| is YES, the consumer is 562// notified about model changes. 563- (void)updateSyncErrorsSection:(BOOL)notifyConsumer { 564 if (!base::FeatureList::IsEnabled(signin::kMobileIdentityConsistency)) { 565 return; 566 } 567 BOOL needsSyncErrorItemsUpdate = [self updateSyncErrorItems]; 568 if (notifyConsumer && needsSyncErrorItemsUpdate) { 569 NSUInteger sectionIndex = [self.consumer.tableViewModel 570 sectionForSectionIdentifier:SyncErrorsSectionIdentifier]; 571 NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:sectionIndex]; 572 [self.consumer reloadSections:indexSet]; 573 } 574} 575 576// Adds, removes and updates the sync error item in the model as needed. Returns 577// YES if the consumer should be notified. 578- (BOOL)updateSyncErrorItems { 579 TableViewModel* model = self.consumer.tableViewModel; 580 BOOL hasError = NO; 581 SyncSettingsItemType type; 582 583 if (self.isSyncDisabledByAdministrator) { 584 type = SyncDisabledByAdministratorErrorItemType; 585 hasError = YES; 586 } else if (self.isAuthenticated && self.syncSetupService->IsSyncEnabled()) { 587 switch (self.syncSetupService->GetSyncServiceState()) { 588 case SyncSetupService::kSyncServiceUnrecoverableError: 589 type = RestartAuthenticationFlowErrorItemType; 590 hasError = YES; 591 break; 592 case SyncSetupService::kSyncServiceSignInNeedsUpdate: 593 type = ReauthDialogAsSyncIsInAuthErrorItemType; 594 hasError = YES; 595 break; 596 case SyncSetupService::kSyncServiceNeedsPassphrase: 597 type = ShowPassphraseDialogErrorItemType; 598 hasError = YES; 599 break; 600 case SyncSetupService::kSyncServiceNeedsTrustedVaultKey: 601 type = SyncNeedsTrustedVaultKeyErrorItemType; 602 hasError = YES; 603 break; 604 case SyncSetupService::kSyncSettingsNotConfirmed: 605 case SyncSetupService::kNoSyncServiceError: 606 case SyncSetupService::kSyncServiceCouldNotConnect: 607 case SyncSetupService::kSyncServiceServiceUnavailable: 608 break; 609 } 610 } 611 612 if ((!hasError && !self.syncErrorItem) || 613 (hasError && self.syncErrorItem && type == self.syncErrorItem.type)) { 614 // Nothing to update. 615 return NO; 616 } 617 618 if (self.syncErrorItem) { 619 // Remove the previous sync error item, since it is either the wrong error 620 // (if hasError is YES), or there is no error anymore. 621 [model removeItemWithType:self.syncErrorItem.type 622 fromSectionWithIdentifier:SyncErrorsSectionIdentifier]; 623 self.syncErrorItem = nil; 624 if (!hasError) 625 return YES; 626 } 627 // Add the sync error item and its section. 628 if (type == SyncDisabledByAdministratorErrorItemType) { 629 self.syncErrorItem = [self createSyncDisabledByAdministratorErrorItem]; 630 } else { 631 self.syncErrorItem = [self createSyncErrorItemWithItemType:type]; 632 } 633 [model insertItem:self.syncErrorItem 634 inSectionWithIdentifier:SyncErrorsSectionIdentifier 635 atIndex:0]; 636 return YES; 637} 638 639// Returns an item to show to the user the sync cannot be turned on for an 640// enterprise policy reason. 641- (TableViewItem*)createSyncDisabledByAdministratorErrorItem { 642 TableViewImageItem* item = [[TableViewImageItem alloc] 643 initWithType:SyncDisabledByAdministratorErrorItemType]; 644 item.image = [UIImage imageNamed:kGoogleServicesEnterpriseImage]; 645 item.title = GetNSString( 646 IDS_IOS_GOOGLE_SERVICES_SETTINGS_SYNC_DISABLBED_BY_ADMINISTRATOR_TITLE); 647 item.enabled = NO; 648 item.textColor = UIColor.cr_secondaryLabelColor; 649 return item; 650} 651 652#pragma mark - Properties 653 654- (BOOL)isSyncDisabledByAdministrator { 655 return self.syncService->GetDisableReasons().Has( 656 syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY); 657} 658 659- (BOOL)isAuthenticated { 660 return self.authService->IsAuthenticated(); 661} 662 663@end 664