1// Copyright 2015 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/settings_table_view_controller.h" 6 7#include <memory> 8 9#include "base/feature_list.h" 10#import "base/mac/foundation_util.h" 11#include "base/metrics/user_metrics.h" 12#include "base/metrics/user_metrics_action.h" 13#include "base/strings/sys_string_conversions.h" 14#include "build/branding_buildflags.h" 15#include "components/autofill/core/common/autofill_prefs.h" 16#include "components/keyed_service/core/service_access_type.h" 17#include "components/password_manager/core/browser/password_store.h" 18#include "components/password_manager/core/common/password_manager_pref_names.h" 19#import "components/prefs/ios/pref_observer_bridge.h" 20#include "components/prefs/pref_member.h" 21#include "components/prefs/pref_service.h" 22#include "components/search_engines/search_engines_pref_names.h" 23#include "components/search_engines/util.h" 24#include "components/signin/public/base/account_consistency_method.h" 25#include "components/signin/public/base/signin_metrics.h" 26#include "components/signin/public/identity_manager/identity_manager.h" 27#import "components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h" 28#include "components/strings/grit/components_strings.h" 29#include "components/sync/driver/sync_service.h" 30#include "ios/chrome/browser/application_context.h" 31#include "ios/chrome/browser/browser_state/chrome_browser_state.h" 32#include "ios/chrome/browser/main/browser.h" 33#include "ios/chrome/browser/passwords/ios_chrome_password_check_manager.h" 34#include "ios/chrome/browser/passwords/ios_chrome_password_check_manager_factory.h" 35#include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h" 36#include "ios/chrome/browser/passwords/password_check_observer_bridge.h" 37#include "ios/chrome/browser/pref_names.h" 38#include "ios/chrome/browser/search_engines/search_engine_observer_bridge.h" 39#include "ios/chrome/browser/search_engines/template_url_service_factory.h" 40#import "ios/chrome/browser/signin/authentication_service.h" 41#include "ios/chrome/browser/signin/authentication_service_factory.h" 42#import "ios/chrome/browser/signin/chrome_identity_service_observer_bridge.h" 43#include "ios/chrome/browser/signin/identity_manager_factory.h" 44#include "ios/chrome/browser/sync/profile_sync_service_factory.h" 45#import "ios/chrome/browser/sync/sync_observer_bridge.h" 46#include "ios/chrome/browser/sync/sync_setup_service_factory.h" 47#include "ios/chrome/browser/system_flags.h" 48#import "ios/chrome/browser/ui/authentication/cells/signin_promo_view_consumer.h" 49#import "ios/chrome/browser/ui/authentication/cells/table_view_account_item.h" 50#import "ios/chrome/browser/ui/authentication/cells/table_view_signin_promo_item.h" 51#import "ios/chrome/browser/ui/authentication/signin_promo_view_mediator.h" 52#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.h" 53#import "ios/chrome/browser/ui/settings/about_chrome_table_view_controller.h" 54#import "ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.h" 55#import "ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.h" 56#import "ios/chrome/browser/ui/settings/bandwidth_management_table_view_controller.h" 57#import "ios/chrome/browser/ui/settings/cells/account_sign_in_item.h" 58#import "ios/chrome/browser/ui/settings/cells/settings_check_item.h" 59#import "ios/chrome/browser/ui/settings/cells/settings_image_detail_text_item.h" 60#import "ios/chrome/browser/ui/settings/cells/settings_switch_cell.h" 61#import "ios/chrome/browser/ui/settings/cells/settings_switch_item.h" 62#import "ios/chrome/browser/ui/settings/content_settings_table_view_controller.h" 63#import "ios/chrome/browser/ui/settings/default_browser/default_browser_settings_table_view_controller.h" 64#import "ios/chrome/browser/ui/settings/elements/enterprise_info_popover_view_controller.h" 65#import "ios/chrome/browser/ui/settings/google_services/accounts_table_view_controller.h" 66#import "ios/chrome/browser/ui/settings/google_services/google_services_settings_coordinator.h" 67#import "ios/chrome/browser/ui/settings/google_services/manage_sync_settings_coordinator.h" 68#import "ios/chrome/browser/ui/settings/language/language_settings_mediator.h" 69#import "ios/chrome/browser/ui/settings/language/language_settings_table_view_controller.h" 70#import "ios/chrome/browser/ui/settings/password/passwords_coordinator.h" 71#import "ios/chrome/browser/ui/settings/privacy/privacy_coordinator.h" 72#import "ios/chrome/browser/ui/settings/safety_check/safety_check_constants.h" 73#import "ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.h" 74#import "ios/chrome/browser/ui/settings/safety_check/safety_check_utils.h" 75#import "ios/chrome/browser/ui/settings/search_engine_table_view_controller.h" 76#import "ios/chrome/browser/ui/settings/settings_table_view_controller_constants.h" 77#import "ios/chrome/browser/ui/settings/sync/utils/sync_util.h" 78#import "ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.h" 79#import "ios/chrome/browser/ui/settings/utils/pref_backed_boolean.h" 80#import "ios/chrome/browser/ui/settings/voice_search_table_view_controller.h" 81#import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h" 82#include "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h" 83#import "ios/chrome/browser/ui/table_view/cells/table_view_detail_icon_item.h" 84#import "ios/chrome/browser/ui/table_view/cells/table_view_info_button_cell.h" 85#import "ios/chrome/browser/ui/table_view/cells/table_view_info_button_item.h" 86#import "ios/chrome/browser/ui/table_view/cells/table_view_text_item.h" 87#import "ios/chrome/browser/ui/table_view/table_view_model.h" 88#include "ios/chrome/browser/ui/ui_feature_flags.h" 89#import "ios/chrome/browser/ui/util/uikit_ui_util.h" 90#include "ios/chrome/browser/upgrade/upgrade_utils.h" 91#include "ios/chrome/browser/voice/speech_input_locale_config.h" 92#import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h" 93#import "ios/chrome/common/ui/colors/semantic_color_names.h" 94#include "ios/chrome/grit/ios_chromium_strings.h" 95#include "ios/chrome/grit/ios_strings.h" 96#import "ios/public/provider/chrome/browser/chrome_browser_provider.h" 97#import "ios/public/provider/chrome/browser/signin/chrome_identity.h" 98#import "ios/public/provider/chrome/browser/signin/signin_presenter.h" 99#import "ios/public/provider/chrome/browser/signin/signin_resources_provider.h" 100#include "ios/public/provider/chrome/browser/voice/voice_search_prefs.h" 101#import "net/base/mac/url_conversions.h" 102#include "ui/base/l10n/l10n_util_mac.h" 103 104#if !defined(__has_feature) || !__has_feature(objc_arc) 105#error "This file requires ARC support." 106#endif 107 108namespace { 109 110const CGFloat kAccountProfilePhotoDimension = 40.0f; 111 112NSString* const kSyncAndGoogleServicesImageName = @"sync_and_google_services"; 113NSString* const kSyncAndGoogleServicesSyncErrorImageName = 114 @"sync_and_google_services_sync_error"; 115NSString* const kSyncAndGoogleServicesSyncOffImageName = 116 @"sync_and_google_services_sync_off"; 117NSString* const kSyncAndGoogleServicesSyncOnImageName = 118 @"sync_and_google_services_sync_on"; 119NSString* const kSettingsGoogleServicesImageName = @"settings_google_services"; 120NSString* const kSettingsSearchEngineImageName = @"settings_search_engine"; 121NSString* const kSettingsPasswordsImageName = @"settings_passwords"; 122NSString* const kSettingsAutofillCreditCardImageName = 123 @"settings_payment_methods"; 124NSString* const kSettingsAutofillProfileImageName = @"settings_addresses"; 125NSString* const kSettingsVoiceSearchImageName = @"settings_voice_search"; 126NSString* const kSettingsSafetyCheckImageName = @"settings_safety_check"; 127NSString* const kSettingsPrivacyImageName = @"settings_privacy"; 128NSString* const kSettingsLanguageSettingsImageName = 129 @"settings_language_settings"; 130NSString* const kSettingsContentSettingsImageName = 131 @"settings_content_settings"; 132NSString* const kSettingsBandwidthImageName = @"settings_bandwidth"; 133NSString* const kSettingsAboutChromeImageName = @"settings_about_chrome"; 134NSString* const kSettingsDebugImageName = @"settings_debug"; 135NSString* const kSettingsArticleSuggestionsImageName = 136 @"settings_article_suggestions"; 137NSString* const kDefaultBrowserWorldImageName = @"default_browser_world"; 138 139#if BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG) 140NSString* kDevViewSourceKey = @"DevViewSource"; 141#endif // BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG) 142 143} // namespace 144 145#pragma mark - SettingsTableViewController 146 147@interface SettingsTableViewController () < 148 BooleanObserver, 149 ChromeIdentityServiceObserver, 150 GoogleServicesSettingsCoordinatorDelegate, 151 IdentityManagerObserverBridgeDelegate, 152 ManageSyncSettingsCoordinatorDelegate, 153 PasswordCheckObserver, 154 PasswordsCoordinatorDelegate, 155 PopoverLabelViewControllerDelegate, 156 PrefObserverDelegate, 157 PrivacyCoordinatorDelegate, 158 SafetyCheckCoordinatorDelegate, 159 SettingsControllerProtocol, 160 SearchEngineObserving, 161 SigninPresenter, 162 SigninPromoViewConsumer, 163 SyncObserverModelBridge> { 164 // The browser where the settings are being displayed. 165 Browser* _browser; 166 // The browser state for |_browser|. Never off the record. 167 ChromeBrowserState* _browserState; // weak 168 // Bridge for TemplateURLServiceObserver. 169 std::unique_ptr<SearchEngineObserverBridge> _searchEngineObserverBridge; 170 std::unique_ptr<signin::IdentityManagerObserverBridge> 171 _identityObserverBridge; 172 std::unique_ptr<SyncObserverBridge> _syncObserverBridge; 173 // A helper object for observing changes in the password check status 174 // and changes to the compromised credentials list. 175 std::unique_ptr<PasswordCheckObserverBridge> _passwordCheckObserver; 176 // The service responsible for password check feature. 177 scoped_refptr<IOSChromePasswordCheckManager> _passwordCheckManager; 178 // Whether the impression of the Signin button has already been recorded. 179 BOOL _hasRecordedSigninImpression; 180 // PrefBackedBoolean for ShowMemoryDebugTools switch. 181 PrefBackedBoolean* _showMemoryDebugToolsEnabled; 182 // PrefBackedBoolean for ArticlesForYou switch. 183 PrefBackedBoolean* _articlesEnabled; 184 // The item related to the switch for the show suggestions setting. 185 SettingsSwitchItem* _showMemoryDebugToolsItem; 186 // The item related to the switch for the show suggestions setting. 187 SettingsSwitchItem* _articlesForYouItem; 188 // The item related to the safety check. 189 SettingsCheckItem* _safetyCheckItem; 190 191 // Mediator to configure the sign-in promo cell. Also used to received 192 // identity update notifications. 193 SigninPromoViewMediator* _signinPromoViewMediator; 194 GoogleServicesSettingsCoordinator* _googleServicesSettingsCoordinator; 195 ManageSyncSettingsCoordinator* _manageSyncSettingsCoordinator; 196 197 // Privacy coordinator. 198 PrivacyCoordinator* _privacyCoordinator; 199 200 // Safety Check coordinator. 201 SafetyCheckCoordinator* _safetyCheckCoordinator; 202 203 // Passwords coordinator. 204 PasswordsCoordinator* _passwordsCoordinator; 205 206 // Cached resized profile image. 207 UIImage* _resizedImage; 208 __weak UIImage* _oldImage; 209 210 // Identity object and observer used for Account Item refresh. 211 ChromeIdentity* _identity; 212 std::unique_ptr<ChromeIdentityServiceObserverBridge> _identityServiceObserver; 213 214 // PrefMember for voice locale code. 215 StringPrefMember _voiceLocaleCode; 216 // Pref observer to track changes to prefs. 217 std::unique_ptr<PrefObserverBridge> _prefObserverBridge; 218 // TODO(crbug.com/662435): Refactor PrefObserverBridge so it owns the 219 // PrefChangeRegistrar. 220 // Registrar for pref changes notifications. 221 PrefChangeRegistrar _prefChangeRegistrar; 222 223 // Updatable Items. 224 TableViewDetailIconItem* _voiceSearchDetailItem; 225 TableViewDetailIconItem* _defaultSearchEngineItem; 226 TableViewDetailIconItem* _passwordsDetailItem; 227 TableViewDetailIconItem* _autoFillProfileDetailItem; 228 TableViewDetailIconItem* _autoFillCreditCardDetailItem; 229 230 // YES if view has been dismissed. 231 BOOL _settingsHasBeenDismissed; 232} 233 234@property(nonatomic, readonly, weak) 235 id<ApplicationCommands, BrowserCommands, BrowsingDataCommands> 236 dispatcher; 237 238// YES if the sign-in is in progress. 239@property(nonatomic, assign) BOOL isSigninInProgress; 240 241// Stops observing browser state services. This is required during the shutdown 242// phase to avoid observing services for a profile that is being killed. 243- (void)stopBrowserStateServiceObservers; 244 245@end 246 247@implementation SettingsTableViewController 248@synthesize dispatcher = _dispatcher; 249 250#pragma mark Initialization 251 252- (instancetype) 253 initWithBrowser:(Browser*)browser 254 dispatcher: 255 (id<ApplicationCommands, BrowserCommands, BrowsingDataCommands>) 256 dispatcher { 257 DCHECK(browser); 258 DCHECK(!browser->GetBrowserState()->IsOffTheRecord()); 259 UITableViewStyle style = base::FeatureList::IsEnabled(kSettingsRefresh) 260 ? UITableViewStylePlain 261 : UITableViewStyleGrouped; 262 self = [super initWithStyle:style]; 263 if (self) { 264 _browser = browser; 265 _browserState = _browser->GetBrowserState(); 266 self.title = l10n_util::GetNSStringWithFixup(IDS_IOS_SETTINGS_TITLE); 267 _searchEngineObserverBridge.reset(new SearchEngineObserverBridge( 268 self, 269 ios::TemplateURLServiceFactory::GetForBrowserState(_browserState))); 270 signin::IdentityManager* identityManager = 271 IdentityManagerFactory::GetForBrowserState(_browserState); 272 // It is expected that |identityManager| should never be nil except in 273 // tests. In that case, the tests should be fixed. 274 DCHECK(identityManager); 275 _identityObserverBridge.reset( 276 new signin::IdentityManagerObserverBridge(identityManager, self)); 277 syncer::SyncService* syncService = 278 ProfileSyncServiceFactory::GetForBrowserState(_browserState); 279 _syncObserverBridge.reset(new SyncObserverBridge(self, syncService)); 280 281 _showMemoryDebugToolsEnabled = [[PrefBackedBoolean alloc] 282 initWithPrefService:GetApplicationContext()->GetLocalState() 283 prefName:prefs::kShowMemoryDebuggingTools]; 284 [_showMemoryDebugToolsEnabled setObserver:self]; 285 286 AuthenticationService* authService = 287 AuthenticationServiceFactory::GetForBrowserState(_browserState); 288 authService->WaitUntilCacheIsPopulated(); 289 _identity = authService->GetAuthenticatedIdentity(); 290 _identityServiceObserver.reset( 291 new ChromeIdentityServiceObserverBridge(self)); 292 293 PrefService* prefService = _browserState->GetPrefs(); 294 295 if (base::FeatureList::IsEnabled(kSafetyCheckIOS)) { 296 _passwordCheckManager = 297 IOSChromePasswordCheckManagerFactory::GetForBrowserState( 298 _browserState); 299 _passwordCheckObserver = std::make_unique<PasswordCheckObserverBridge>( 300 self, _passwordCheckManager.get()); 301 } 302 303 _articlesEnabled = [[PrefBackedBoolean alloc] 304 initWithPrefService:prefService 305 prefName:prefs::kArticlesForYouEnabled]; 306 [_articlesEnabled setObserver:self]; 307 308 _voiceLocaleCode.Init(prefs::kVoiceSearchLocale, prefService); 309 310 _prefChangeRegistrar.Init(prefService); 311 _prefObserverBridge.reset(new PrefObserverBridge(self)); 312 // Register to observe any changes on Perf backed values displayed by the 313 // screen. 314 _prefObserverBridge->ObserveChangesForPreference(prefs::kVoiceSearchLocale, 315 &_prefChangeRegistrar); 316 _prefObserverBridge->ObserveChangesForPreference( 317 password_manager::prefs::kCredentialsEnableService, 318 &_prefChangeRegistrar); 319 _prefObserverBridge->ObserveChangesForPreference( 320 autofill::prefs::kAutofillCreditCardEnabled, &_prefChangeRegistrar); 321 _prefObserverBridge->ObserveChangesForPreference( 322 autofill::prefs::kAutofillProfileEnabled, &_prefChangeRegistrar); 323 324 _dispatcher = dispatcher; 325 326 // TODO(crbug.com/764578): -loadModel should not be called from 327 // initializer. A possible fix is to move this call to -viewDidLoad. 328 [self loadModel]; 329 } 330 return self; 331} 332 333- (void)dealloc { 334 DCHECK(_settingsHasBeenDismissed) 335 << "-settingsWillBeDismissed must be called before -dealloc"; 336} 337 338- (void)stopBrowserStateServiceObservers { 339 _syncObserverBridge.reset(); 340 _identityObserverBridge.reset(); 341 _identityServiceObserver.reset(); 342 [_showMemoryDebugToolsEnabled setObserver:nil]; 343 [_articlesEnabled setObserver:nil]; 344} 345 346#pragma mark View lifecycle 347 348- (void)viewDidLoad { 349 [super viewDidLoad]; 350 351 self.tableView.accessibilityIdentifier = kSettingsTableViewId; 352 353 // Change the separator inset from the settings default because this 354 // TableView shows leading icons. 355 self.tableView.separatorInset = 356 UIEdgeInsetsMake(0, kTableViewSeparatorInsetWithIcon, 0, 0); 357 358 self.navigationItem.largeTitleDisplayMode = 359 UINavigationItemLargeTitleDisplayModeAlways; 360} 361 362#pragma mark SettingsRootTableViewController 363 364- (void)loadModel { 365 [super loadModel]; 366 367 TableViewModel<TableViewItem*>* model = self.tableViewModel; 368 369 AuthenticationService* authService = 370 AuthenticationServiceFactory::GetForBrowserState(_browserState); 371 if (!authService->IsAuthenticated()) { 372 // Sign in section 373 [model addSectionWithIdentifier:SettingsSectionIdentifierSignIn]; 374 if ([SigninPromoViewMediator 375 shouldDisplaySigninPromoViewWithAccessPoint: 376 signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS 377 browserState:_browserState]) { 378 if (!_signinPromoViewMediator) { 379 _signinPromoViewMediator = [[SigninPromoViewMediator alloc] 380 initWithBrowserState:_browserState 381 accessPoint:signin_metrics::AccessPoint:: 382 ACCESS_POINT_SETTINGS 383 presenter:self /* id<SigninPresenter> */]; 384 _signinPromoViewMediator.consumer = self; 385 } 386 } else { 387 [_signinPromoViewMediator signinPromoViewIsRemoved]; 388 _signinPromoViewMediator = nil; 389 } 390 [model addItem:[self signInTextItem] 391 toSectionWithIdentifier:SettingsSectionIdentifierSignIn]; 392 } else { 393 // Account section 394 [model addSectionWithIdentifier:SettingsSectionIdentifierAccount]; 395 _hasRecordedSigninImpression = NO; 396 [_signinPromoViewMediator signinPromoViewIsRemoved]; 397 _signinPromoViewMediator = nil; 398 [model addItem:[self accountCellItem] 399 toSectionWithIdentifier:SettingsSectionIdentifierAccount]; 400 } 401 if (![model 402 hasSectionForSectionIdentifier:SettingsSectionIdentifierAccount]) { 403 // Add the Account section for the Sync & Google services cell, if the user 404 // is signed-out. 405 [model addSectionWithIdentifier:SettingsSectionIdentifierAccount]; 406 } 407 408 // Adds experimental Google Services item separate from Sync. 409 if (base::FeatureList::IsEnabled(signin::kMobileIdentityConsistency)) { 410 if (authService->IsAuthenticated()) { 411 [model addItem:[self googleSyncCellItem] 412 toSectionWithIdentifier:SettingsSectionIdentifierAccount]; 413 } 414 [model addItem:[self googleServicesCellItem] 415 toSectionWithIdentifier:SettingsSectionIdentifierAccount]; 416 } else { 417 [model addItem:[self syncAndGoogleServicesCellItem] 418 toSectionWithIdentifier:SettingsSectionIdentifierAccount]; 419 } 420 421 // Defaults section. 422 if (@available(iOS 14, *)) { 423 if (base::FeatureList::IsEnabled(kDefaultBrowserSettings)) { 424 [model addSectionWithIdentifier:SettingsSectionIdentifierDefaults]; 425 [model addItem:[self defaultBrowserCellItem] 426 toSectionWithIdentifier:SettingsSectionIdentifierDefaults]; 427 } 428 } 429 430 // Basics section 431 [model addSectionWithIdentifier:SettingsSectionIdentifierBasics]; 432 // Show managed UI if default search engine is managed by policy. 433 if (base::FeatureList::IsEnabled(kEnableIOSManagedSettingsUI) && 434 [self isDefaultSearchEngineManagedByPolicy]) { 435 [model addItem:[self managedSearchEngineItem] 436 toSectionWithIdentifier:SettingsSectionIdentifierBasics]; 437 } else { 438 if (@available(iOS 14, *)) { 439 if (base::FeatureList::IsEnabled(kDefaultBrowserSettings)) { 440 [model addItem:[self searchEngineDetailItem] 441 toSectionWithIdentifier:SettingsSectionIdentifierDefaults]; 442 } else { 443 [model addItem:[self searchEngineDetailItem] 444 toSectionWithIdentifier:SettingsSectionIdentifierBasics]; 445 } 446 } else { 447 [model addItem:[self searchEngineDetailItem] 448 toSectionWithIdentifier:SettingsSectionIdentifierBasics]; 449 } 450 } 451 [model addItem:[self passwordsDetailItem] 452 toSectionWithIdentifier:SettingsSectionIdentifierBasics]; 453 [model addItem:[self autoFillCreditCardDetailItem] 454 toSectionWithIdentifier:SettingsSectionIdentifierBasics]; 455 [model addItem:[self autoFillProfileDetailItem] 456 toSectionWithIdentifier:SettingsSectionIdentifierBasics]; 457 458 // Advanced Section 459 [model addSectionWithIdentifier:SettingsSectionIdentifierAdvanced]; 460 [model addItem:[self voiceSearchDetailItem] 461 toSectionWithIdentifier:SettingsSectionIdentifierAdvanced]; 462 if (base::FeatureList::IsEnabled(kSafetyCheckIOS)) { 463 [model addItem:[self safetyCheckDetailItem] 464 toSectionWithIdentifier:SettingsSectionIdentifierAdvanced]; 465 } 466 [model addItem:[self privacyDetailItem] 467 toSectionWithIdentifier:SettingsSectionIdentifierAdvanced]; 468 _articlesForYouItem = [self articlesForYouSwitchItem]; 469 [model addItem:_articlesForYouItem 470 toSectionWithIdentifier:SettingsSectionIdentifierAdvanced]; 471 [model addItem:[self languageSettingsDetailItem] 472 toSectionWithIdentifier:SettingsSectionIdentifierAdvanced]; 473 [model addItem:[self contentSettingsDetailItem] 474 toSectionWithIdentifier:SettingsSectionIdentifierAdvanced]; 475 [model addItem:[self bandwidthManagementDetailItem] 476 toSectionWithIdentifier:SettingsSectionIdentifierAdvanced]; 477 478 // Info Section 479 [model addSectionWithIdentifier:SettingsSectionIdentifierInfo]; 480 [model addItem:[self aboutChromeDetailItem] 481 toSectionWithIdentifier:SettingsSectionIdentifierInfo]; 482 483 // Debug Section 484 if ([self hasDebugSection]) { 485 [model addSectionWithIdentifier:SettingsSectionIdentifierDebug]; 486 } 487 488 if (experimental_flags::IsMemoryDebuggingEnabled()) { 489 _showMemoryDebugToolsItem = [self showMemoryDebugSwitchItem]; 490 [model addItem:_showMemoryDebugToolsItem 491 toSectionWithIdentifier:SettingsSectionIdentifierDebug]; 492 } 493 494#if BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG) 495 [model addItem:[self viewSourceSwitchItem] 496 toSectionWithIdentifier:SettingsSectionIdentifierDebug]; 497 [model addItem:[self tableViewCatalogDetailItem] 498 toSectionWithIdentifier:SettingsSectionIdentifierDebug]; 499#endif // BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG) 500} 501 502#pragma mark - Model Items 503 504- (TableViewItem*)signInTextItem { 505 if (_signinPromoViewMediator) { 506 TableViewSigninPromoItem* signinPromoItem = 507 [[TableViewSigninPromoItem alloc] 508 initWithType:SettingsItemTypeSigninPromo]; 509 signinPromoItem.text = 510 l10n_util::GetNSString(IDS_IOS_SIGNIN_PROMO_SETTINGS_WITH_UNITY); 511 signinPromoItem.configurator = 512 [_signinPromoViewMediator createConfigurator]; 513 signinPromoItem.delegate = _signinPromoViewMediator; 514 [_signinPromoViewMediator signinPromoViewIsVisible]; 515 return signinPromoItem; 516 } 517 if (!_hasRecordedSigninImpression) { 518 // Once the Settings are open, this button impression will at most be 519 // recorded once until they are closed. 520 signin_metrics::RecordSigninImpressionUserActionForAccessPoint( 521 signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS); 522 _hasRecordedSigninImpression = YES; 523 } 524 AccountSignInItem* signInTextItem = 525 [[AccountSignInItem alloc] initWithType:SettingsItemTypeSignInButton]; 526 signInTextItem.accessibilityIdentifier = kSettingsSignInCellId; 527 signInTextItem.detailText = 528 l10n_util::GetNSString(IDS_IOS_SIGN_IN_TO_CHROME_SETTING_SUBTITLE); 529 530 return signInTextItem; 531} 532 533- (TableViewItem*)googleServicesCellItem { 534 SettingsImageDetailTextItem* googleServicesItem = 535 [[SettingsImageDetailTextItem alloc] 536 initWithType:SettingsItemTypeGoogleServices]; 537 googleServicesItem.accessoryType = 538 UITableViewCellAccessoryDisclosureIndicator; 539 googleServicesItem.text = 540 l10n_util::GetNSString(IDS_IOS_GOOGLE_SERVICES_SETTINGS_TITLE); 541 googleServicesItem.accessibilityIdentifier = kSettingsGoogleServicesCellId; 542 googleServicesItem.image = 543 [UIImage imageNamed:kSettingsGoogleServicesImageName]; 544 return googleServicesItem; 545} 546 547- (TableViewItem*)googleSyncCellItem { 548 // TODO(crbug.com/805214): This branded icon image needs to come from 549 // BrandedImageProvider. 550 TableViewDetailIconItem* googleSyncCellItem = 551 [self detailItemWithType:SettingsItemTypeGoogleSync 552 text:l10n_util::GetNSString( 553 IDS_IOS_GOOGLE_SYNC_SETTINGS_TITLE) 554 detailText:l10n_util::GetNSString(IDS_IOS_SETTING_ON) 555 iconImageName:kSyncAndGoogleServicesSyncOnImageName 556 accessibilityIdentifier:kSettingsGoogleSyncAndServicesCellId]; 557 [self updateGoogleSyncCellItem:googleSyncCellItem]; 558 return googleSyncCellItem; 559} 560 561- (TableViewItem*)syncAndGoogleServicesCellItem { 562 // TODO(crbug.com/805214): This branded icon image needs to come from 563 // BrandedImageProvider. 564 SettingsImageDetailTextItem* googleServicesItem = 565 [[SettingsImageDetailTextItem alloc] 566 initWithType:SettingsItemTypeSyncAndGoogleServices]; 567 googleServicesItem.accessoryType = 568 UITableViewCellAccessoryDisclosureIndicator; 569 googleServicesItem.text = 570 l10n_util::GetNSString(IDS_IOS_GOOGLE_SERVICES_SYNC_SETTINGS_TITLE); 571 googleServicesItem.accessibilityIdentifier = 572 kSettingsGoogleSyncAndServicesCellId; 573 [self updateSyncAndGoogleServicesItem:googleServicesItem]; 574 return googleServicesItem; 575} 576 577- (TableViewItem*)defaultBrowserCellItem { 578 TableViewDetailIconItem* defaultBrowser = [[TableViewDetailIconItem alloc] 579 initWithType:SettingsItemTypeDefaultBrowser]; 580 defaultBrowser.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 581 defaultBrowser.text = 582 l10n_util::GetNSString(IDS_IOS_SETTINGS_SET_DEFAULT_BROWSER); 583 defaultBrowser.iconImageName = kDefaultBrowserWorldImageName; 584 585 return defaultBrowser; 586} 587 588- (TableViewItem*)accountCellItem { 589 TableViewAccountItem* identityAccountItem = 590 [[TableViewAccountItem alloc] initWithType:SettingsItemTypeAccount]; 591 identityAccountItem.accessoryType = 592 UITableViewCellAccessoryDisclosureIndicator; 593 identityAccountItem.accessibilityIdentifier = kSettingsAccountCellId; 594 [self updateIdentityAccountItem:identityAccountItem]; 595 return identityAccountItem; 596} 597 598- (TableViewItem*)searchEngineDetailItem { 599 NSString* defaultSearchEngineName = 600 base::SysUTF16ToNSString(GetDefaultSearchEngineName( 601 ios::TemplateURLServiceFactory::GetForBrowserState(_browserState))); 602 603 _defaultSearchEngineItem = 604 [self detailItemWithType:SettingsItemTypeSearchEngine 605 text:l10n_util::GetNSString( 606 IDS_IOS_SEARCH_ENGINE_SETTING_TITLE) 607 detailText:defaultSearchEngineName 608 iconImageName:kSettingsSearchEngineImageName 609 accessibilityIdentifier:kSettingsSearchEngineCellId]; 610 return _defaultSearchEngineItem; 611} 612 613- (TableViewInfoButtonItem*)managedSearchEngineItem { 614 TableViewInfoButtonItem* managedDefaultSearchEngineItem = 615 [[TableViewInfoButtonItem alloc] 616 initWithType:SettingsItemTypeManagedDefaultSearchEngine]; 617 managedDefaultSearchEngineItem.text = 618 l10n_util::GetNSString(IDS_IOS_SEARCH_ENGINE_SETTING_TITLE); 619 managedDefaultSearchEngineItem.iconImageName = kSettingsSearchEngineImageName; 620 managedDefaultSearchEngineItem.accessibilityHint = 621 l10n_util::GetNSString(IDS_IOS_TOGGLE_SETTING_MANAGED_ACCESSIBILITY_HINT); 622 623 const base::DictionaryValue* dict = _browserState->GetPrefs()->GetDictionary( 624 DefaultSearchManager::kDefaultSearchProviderDataPrefName); 625 if (dict->FindBoolPath(DefaultSearchManager::kDisabledByPolicy)) { 626 // Default search engine is disabled by policy. 627 managedDefaultSearchEngineItem.statusText = 628 l10n_util::GetNSString(IDS_IOS_SEARCH_ENGINE_SETTING_DISABLED_STATUS); 629 } else { 630 // Default search engine is enabled and set by policy. 631 const std::string* status = 632 dict->FindStringPath(DefaultSearchManager::kShortName); 633 managedDefaultSearchEngineItem.statusText = 634 base::SysUTF8ToNSString(*status); 635 } 636 637 managedDefaultSearchEngineItem.accessibilityIdentifier = 638 kSettingsManagedSearchEngineCellId; 639 return managedDefaultSearchEngineItem; 640} 641 642- (TableViewItem*)passwordsDetailItem { 643 BOOL passwordsEnabled = _browserState->GetPrefs()->GetBoolean( 644 password_manager::prefs::kCredentialsEnableService); 645 NSString* passwordsDetail = passwordsEnabled 646 ? l10n_util::GetNSString(IDS_IOS_SETTING_ON) 647 : l10n_util::GetNSString(IDS_IOS_SETTING_OFF); 648 _passwordsDetailItem = 649 [self detailItemWithType:SettingsItemTypePasswords 650 text:l10n_util::GetNSString(IDS_IOS_PASSWORDS) 651 detailText:passwordsDetail 652 iconImageName:kSettingsPasswordsImageName 653 accessibilityIdentifier:kSettingsPasswordsCellId]; 654 655 return _passwordsDetailItem; 656} 657 658- (TableViewItem*)autoFillCreditCardDetailItem { 659 BOOL autofillCreditCardEnabled = 660 autofill::prefs::IsAutofillCreditCardEnabled(_browserState->GetPrefs()); 661 NSString* detailText = autofillCreditCardEnabled 662 ? l10n_util::GetNSString(IDS_IOS_SETTING_ON) 663 : l10n_util::GetNSString(IDS_IOS_SETTING_OFF); 664 _autoFillCreditCardDetailItem = 665 [self detailItemWithType:SettingsItemTypeAutofillCreditCard 666 text:l10n_util::GetNSString( 667 IDS_AUTOFILL_PAYMENT_METHODS) 668 detailText:detailText 669 iconImageName:kSettingsAutofillCreditCardImageName 670 accessibilityIdentifier:kSettingsPaymentMethodsCellId]; 671 672 return _autoFillCreditCardDetailItem; 673} 674 675- (TableViewItem*)autoFillProfileDetailItem { 676 BOOL autofillProfileEnabled = 677 autofill::prefs::IsAutofillProfileEnabled(_browserState->GetPrefs()); 678 NSString* detailText = autofillProfileEnabled 679 ? l10n_util::GetNSString(IDS_IOS_SETTING_ON) 680 : l10n_util::GetNSString(IDS_IOS_SETTING_OFF); 681 _autoFillProfileDetailItem = 682 [self detailItemWithType:SettingsItemTypeAutofillProfile 683 text:l10n_util::GetNSString( 684 IDS_AUTOFILL_ADDRESSES_SETTINGS_TITLE) 685 detailText:detailText 686 iconImageName:kSettingsAutofillProfileImageName 687 accessibilityIdentifier:kSettingsAddressesAndMoreCellId]; 688 689 return _autoFillProfileDetailItem; 690} 691 692- (TableViewItem*)voiceSearchDetailItem { 693 voice::SpeechInputLocaleConfig* localeConfig = 694 voice::SpeechInputLocaleConfig::GetInstance(); 695 voice::SpeechInputLocale locale = 696 _voiceLocaleCode.GetValue().length() 697 ? localeConfig->GetLocaleForCode(_voiceLocaleCode.GetValue()) 698 : localeConfig->GetDefaultLocale(); 699 NSString* languageName = base::SysUTF16ToNSString(locale.display_name); 700 _voiceSearchDetailItem = 701 [self detailItemWithType:SettingsItemTypeVoiceSearch 702 text:l10n_util::GetNSString( 703 IDS_IOS_VOICE_SEARCH_SETTING_TITLE) 704 detailText:languageName 705 iconImageName:kSettingsVoiceSearchImageName 706 accessibilityIdentifier:kSettingsVoiceSearchCellId]; 707 return _voiceSearchDetailItem; 708} 709 710- (SettingsCheckItem*)safetyCheckDetailItem { 711 NSString* safetyCheckTitle = 712 l10n_util::GetNSString(IDS_OPTIONS_ADVANCED_SECTION_TITLE_SAFETY_CHECK); 713 _safetyCheckItem = 714 [[SettingsCheckItem alloc] initWithType:SettingsItemTypeSafetyCheck]; 715 _safetyCheckItem.text = safetyCheckTitle; 716 _safetyCheckItem.enabled = YES; 717 _safetyCheckItem.indicatorHidden = YES; 718 _safetyCheckItem.infoButtonHidden = YES; 719 _safetyCheckItem.trailingImage = nil; 720 _safetyCheckItem.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 721 UIImage* safetyCheckIcon = [UIImage imageNamed:kSettingsSafetyCheckImageName]; 722 _safetyCheckItem.leadingImage = safetyCheckIcon; 723 724 // Check if an issue state should be shown for updates. 725 if (!IsAppUpToDate() && PreviousSafetyCheckIssueFound()) { 726 UIImage* unSafeIconImage = [[UIImage imageNamed:@"settings_unsafe_state"] 727 imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 728 _safetyCheckItem.trailingImage = unSafeIconImage; 729 _safetyCheckItem.trailingImageTintColor = [UIColor colorNamed:kRedColor]; 730 } 731 732 return _safetyCheckItem; 733} 734 735- (TableViewItem*)privacyDetailItem { 736 return [self 737 detailItemWithType:SettingsItemTypePrivacy 738 text:l10n_util::GetNSString( 739 IDS_OPTIONS_ADVANCED_SECTION_TITLE_PRIVACY) 740 detailText:nil 741 iconImageName:kSettingsPrivacyImageName 742 accessibilityIdentifier:kSettingsPrivacyCellId]; 743} 744 745- (TableViewItem*)languageSettingsDetailItem { 746 return [self detailItemWithType:SettingsItemTypeLanguageSettings 747 text:l10n_util::GetNSString( 748 IDS_IOS_LANGUAGE_SETTINGS_TITLE) 749 detailText:nil 750 iconImageName:kSettingsLanguageSettingsImageName 751 accessibilityIdentifier:kSettingsLanguagesCellId]; 752} 753 754- (TableViewItem*)contentSettingsDetailItem { 755 return [self detailItemWithType:SettingsItemTypeContentSettings 756 text:l10n_util::GetNSString( 757 IDS_IOS_CONTENT_SETTINGS_TITLE) 758 detailText:nil 759 iconImageName:kSettingsContentSettingsImageName 760 accessibilityIdentifier:kSettingsContentSettingsCellId]; 761} 762 763- (TableViewItem*)bandwidthManagementDetailItem { 764 return [self detailItemWithType:SettingsItemTypeBandwidth 765 text:l10n_util::GetNSString( 766 IDS_IOS_BANDWIDTH_MANAGEMENT_SETTINGS) 767 detailText:nil 768 iconImageName:kSettingsBandwidthImageName 769 accessibilityIdentifier:kSettingsBandwidthCellId]; 770} 771 772- (TableViewItem*)aboutChromeDetailItem { 773 return [self detailItemWithType:SettingsItemTypeAboutChrome 774 text:l10n_util::GetNSString(IDS_IOS_PRODUCT_NAME) 775 detailText:nil 776 iconImageName:kSettingsAboutChromeImageName 777 accessibilityIdentifier:kSettingsAboutCellId]; 778} 779 780- (SettingsSwitchItem*)showMemoryDebugSwitchItem { 781 SettingsSwitchItem* showMemoryDebugSwitchItem = 782 [self switchItemWithType:SettingsItemTypeMemoryDebugging 783 title:@"Show memory debug tools" 784 iconImageName:kSettingsDebugImageName 785 withDefaultsKey:nil 786 accessibilityIdentifier:nil]; 787 showMemoryDebugSwitchItem.on = [_showMemoryDebugToolsEnabled value]; 788 789 return showMemoryDebugSwitchItem; 790} 791 792- (SettingsSwitchItem*)articlesForYouSwitchItem { 793 NSString* settingTitle = 794 IsDiscoverFeedEnabled() 795 ? l10n_util::GetNSString(IDS_IOS_DISCOVER_FEED_TITLE) 796 : l10n_util::GetNSString(IDS_IOS_CONTENT_SUGGESTIONS_SETTING_TITLE); 797 798 SettingsSwitchItem* articlesForYouSwitchItem = 799 [self switchItemWithType:SettingsItemTypeArticlesForYou 800 title:settingTitle 801 iconImageName:kSettingsArticleSuggestionsImageName 802 withDefaultsKey:nil 803 accessibilityIdentifier:kSettingsArticleSuggestionsCellId]; 804 articlesForYouSwitchItem.on = [_articlesEnabled value]; 805 806 return articlesForYouSwitchItem; 807} 808#if BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG) 809 810- (SettingsSwitchItem*)viewSourceSwitchItem { 811 return [self switchItemWithType:SettingsItemTypeViewSource 812 title:@"View source menu" 813 iconImageName:kSettingsDebugImageName 814 withDefaultsKey:kDevViewSourceKey 815 accessibilityIdentifier:nil]; 816} 817 818- (TableViewDetailIconItem*)tableViewCatalogDetailItem { 819 return [self detailItemWithType:SettingsItemTypeTableCellCatalog 820 text:@"TableView Cell Catalog" 821 detailText:nil 822 iconImageName:kSettingsDebugImageName 823 accessibilityIdentifier:nil]; 824} 825#endif // BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG) 826 827#pragma mark Item Constructors 828 829- (TableViewDetailIconItem*)detailItemWithType:(NSInteger)type 830 text:(NSString*)text 831 detailText:(NSString*)detailText 832 iconImageName:(NSString*)iconImageName 833 accessibilityIdentifier: 834 (NSString*)accessibilityIdentifier { 835 TableViewDetailIconItem* detailItem = 836 [[TableViewDetailIconItem alloc] initWithType:type]; 837 detailItem.text = text; 838 detailItem.detailText = detailText; 839 detailItem.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 840 detailItem.iconImageName = iconImageName; 841 detailItem.accessibilityTraits |= UIAccessibilityTraitButton; 842 detailItem.accessibilityIdentifier = accessibilityIdentifier; 843 844 return detailItem; 845} 846 847- (SettingsSwitchItem*)switchItemWithType:(NSInteger)type 848 title:(NSString*)title 849 iconImageName:(NSString*)iconImageName 850 withDefaultsKey:(NSString*)key 851 accessibilityIdentifier:(NSString*)accessibilityIdentifier { 852 SettingsSwitchItem* switchItem = 853 [[SettingsSwitchItem alloc] initWithType:type]; 854 switchItem.text = title; 855 switchItem.iconImageName = iconImageName; 856 switchItem.accessibilityIdentifier = accessibilityIdentifier; 857 858 if (key) { 859 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; 860 switchItem.on = [defaults boolForKey:key]; 861 } 862 863 return switchItem; 864} 865 866#pragma mark - UITableViewDataSource 867 868- (UITableViewCell*)tableView:(UITableView*)tableView 869 cellForRowAtIndexPath:(NSIndexPath*)indexPath { 870 UITableViewCell* cell = [super tableView:tableView 871 cellForRowAtIndexPath:indexPath]; 872 NSInteger itemType = [self.tableViewModel itemTypeForIndexPath:indexPath]; 873 874 if ([cell isKindOfClass:[TableViewDetailIconCell class]]) { 875 TableViewDetailIconCell* detailCell = 876 base::mac::ObjCCastStrict<TableViewDetailIconCell>(cell); 877 if (itemType == SettingsItemTypePasswords) { 878 scoped_refptr<password_manager::PasswordStore> passwordStore = 879 IOSChromePasswordStoreFactory::GetForBrowserState( 880 _browserState, ServiceAccessType::EXPLICIT_ACCESS); 881 if (!passwordStore) { 882 // The password store factory returns a NULL password store if something 883 // goes wrong during the password store initialization. Disable the save 884 // passwords cell in this case. 885 LOG(ERROR) << "Save passwords cell was disabled as the password store" 886 " cannot be created."; 887 [detailCell setUserInteractionEnabled:NO]; 888 detailCell.textLabel.textColor = UIColor.cr_secondaryLabelColor; 889 return cell; 890 } 891 } 892 893 [detailCell setUserInteractionEnabled:YES]; 894 detailCell.textLabel.textColor = UIColor.cr_labelColor; 895 } 896 897 switch (itemType) { 898 case SettingsItemTypeMemoryDebugging: { 899 SettingsSwitchCell* switchCell = 900 base::mac::ObjCCastStrict<SettingsSwitchCell>(cell); 901 [switchCell.switchView addTarget:self 902 action:@selector(memorySwitchToggled:) 903 forControlEvents:UIControlEventValueChanged]; 904 break; 905 } 906 case SettingsItemTypeArticlesForYou: { 907 SettingsSwitchCell* switchCell = 908 base::mac::ObjCCastStrict<SettingsSwitchCell>(cell); 909 [switchCell.switchView addTarget:self 910 action:@selector(articlesForYouSwitchToggled:) 911 forControlEvents:UIControlEventValueChanged]; 912 break; 913 } 914 case SettingsItemTypeViewSource: { 915#if BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG) 916 SettingsSwitchCell* switchCell = 917 base::mac::ObjCCastStrict<SettingsSwitchCell>(cell); 918 [switchCell.switchView addTarget:self 919 action:@selector(viewSourceSwitchToggled:) 920 forControlEvents:UIControlEventValueChanged]; 921#else 922 NOTREACHED(); 923#endif // BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG) 924 break; 925 } 926 case SettingsItemTypeManagedDefaultSearchEngine: { 927 TableViewInfoButtonCell* managedCell = 928 base::mac::ObjCCastStrict<TableViewInfoButtonCell>(cell); 929 [managedCell.trailingButton 930 addTarget:self 931 action:@selector(didTapManagedUIInfoButton:) 932 forControlEvents:UIControlEventTouchUpInside]; 933 break; 934 } 935 default: 936 break; 937 } 938 939 return cell; 940} 941 942#pragma mark UITableViewDelegate 943 944- (void)tableView:(UITableView*)tableView 945 didSelectRowAtIndexPath:(NSIndexPath*)indexPath { 946 id object = [self.tableViewModel itemAtIndexPath:indexPath]; 947 if ([object respondsToSelector:@selector(isEnabled)] && 948 ![object performSelector:@selector(isEnabled)]) { 949 // Don't perform any action if the cell isn't enabled. 950 return; 951 } 952 953 NSInteger itemType = [self.tableViewModel itemTypeForIndexPath:indexPath]; 954 955 UIViewController<SettingsRootViewControlling>* controller; 956 957 switch (itemType) { 958 case SettingsItemTypeSignInButton: 959 signin_metrics::RecordSigninUserActionForAccessPoint( 960 signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS, 961 signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO); 962 [self showSignInWithIdentity:nil 963 promoAction:signin_metrics::PromoAction:: 964 PROMO_ACTION_NO_SIGNIN_PROMO 965 completion:nil]; 966 break; 967 case SettingsItemTypeAccount: 968 base::RecordAction(base::UserMetricsAction("Settings.MyAccount")); 969 controller = [[AccountsTableViewController alloc] initWithBrowser:_browser 970 closeSettingsOnAddAccount:NO]; 971 break; 972 case SettingsItemTypeSyncAndGoogleServices: 973 case SettingsItemTypeGoogleServices: 974 base::RecordAction(base::UserMetricsAction("Settings.GoogleServices")); 975 [self showGoogleServices]; 976 break; 977 case SettingsItemTypeGoogleSync: 978 base::RecordAction(base::UserMetricsAction("Settings.Sync")); 979 [self showGoogleSync]; 980 break; 981 case SettingsItemTypeDefaultBrowser: 982 base::RecordAction( 983 base::UserMetricsAction("Settings.ShowDefaultBrowser")); 984 controller = [[DefaultBrowserSettingsTableViewController alloc] init]; 985 break; 986 case SettingsItemTypeSearchEngine: 987 base::RecordAction(base::UserMetricsAction("EditSearchEngines")); 988 controller = [[SearchEngineTableViewController alloc] 989 initWithBrowserState:_browserState]; 990 break; 991 case SettingsItemTypePasswords: 992 base::RecordAction( 993 base::UserMetricsAction("Options_ShowPasswordManager")); 994 [self showPasswords]; 995 break; 996 case SettingsItemTypeAutofillCreditCard: 997 base::RecordAction(base::UserMetricsAction("AutofillCreditCardsViewed")); 998 controller = [[AutofillCreditCardTableViewController alloc] 999 initWithBrowser:_browser]; 1000 break; 1001 case SettingsItemTypeAutofillProfile: 1002 base::RecordAction(base::UserMetricsAction("AutofillAddressesViewed")); 1003 controller = [[AutofillProfileTableViewController alloc] 1004 initWithBrowserState:_browserState]; 1005 break; 1006 case SettingsItemTypeVoiceSearch: 1007 base::RecordAction(base::UserMetricsAction("Settings.VoiceSearch")); 1008 controller = [[VoiceSearchTableViewController alloc] 1009 initWithPrefs:_browserState->GetPrefs()]; 1010 break; 1011 case SettingsItemTypeSafetyCheck: 1012 [self showSafetyCheck]; 1013 break; 1014 case SettingsItemTypePrivacy: 1015 base::RecordAction(base::UserMetricsAction("Settings.Privacy")); 1016 [self showPrivacy]; 1017 break; 1018 case SettingsItemTypeLanguageSettings: { 1019 base::RecordAction(base::UserMetricsAction("Settings.Language")); 1020 LanguageSettingsMediator* mediator = 1021 [[LanguageSettingsMediator alloc] initWithBrowserState:_browserState]; 1022 LanguageSettingsTableViewController* languageSettingsTableViewController = 1023 [[LanguageSettingsTableViewController alloc] 1024 initWithDataSource:mediator 1025 commandHandler:mediator]; 1026 mediator.consumer = languageSettingsTableViewController; 1027 controller = languageSettingsTableViewController; 1028 break; 1029 } 1030 case SettingsItemTypeContentSettings: 1031 base::RecordAction(base::UserMetricsAction("Settings.ContentSettings")); 1032 controller = [[ContentSettingsTableViewController alloc] 1033 initWithBrowserState:_browserState]; 1034 break; 1035 case SettingsItemTypeBandwidth: 1036 base::RecordAction(base::UserMetricsAction("Settings.Bandwidth")); 1037 controller = [[BandwidthManagementTableViewController alloc] 1038 initWithBrowserState:_browserState]; 1039 break; 1040 case SettingsItemTypeAboutChrome: 1041 base::RecordAction(base::UserMetricsAction("AboutChrome")); 1042 controller = [[AboutChromeTableViewController alloc] init]; 1043 break; 1044 case SettingsItemTypeMemoryDebugging: 1045 case SettingsItemTypeViewSource: 1046 // Taps on these don't do anything. They have a switch as accessory view 1047 // and only the switch is tappable. 1048 break; 1049 case SettingsItemTypeTableCellCatalog: 1050 [self.navigationController 1051 pushViewController:[[TableCellCatalogViewController alloc] init] 1052 animated:YES]; 1053 break; 1054 default: 1055 break; 1056 } 1057 1058 if (controller) { 1059 controller.dispatcher = self.dispatcher; 1060 [self.navigationController pushViewController:controller animated:YES]; 1061 } 1062} 1063 1064#pragma mark - Actions 1065 1066// Called when the user clicks on the information button of a managed 1067// settings UI cell. Shows a contextual bubble with the information of the 1068// enterprise. 1069- (void)didTapManagedUIInfoButton:(UIButton*)buttonView { 1070 EnterpriseInfoPopoverViewController* bubbleViewController = 1071 [[EnterpriseInfoPopoverViewController alloc] initWithEnterpriseName:nil]; 1072 bubbleViewController.delegate = self; 1073 1074 // Disable the button when showing the bubble. 1075 // The button will be enabled when close the bubble in 1076 // (void)popoverPresentationControllerDidDismissPopover: of 1077 // EnterpriseInfoPopoverViewController. 1078 buttonView.enabled = NO; 1079 1080 // Set the anchor and arrow direction of the bubble. 1081 bubbleViewController.popoverPresentationController.sourceView = buttonView; 1082 bubbleViewController.popoverPresentationController.sourceRect = 1083 buttonView.bounds; 1084 bubbleViewController.popoverPresentationController.permittedArrowDirections = 1085 UIPopoverArrowDirectionAny; 1086 1087 [self presentViewController:bubbleViewController animated:YES completion:nil]; 1088} 1089 1090#pragma mark Switch Actions 1091 1092- (void)memorySwitchToggled:(UISwitch*)sender { 1093 NSIndexPath* switchPath = 1094 [self.tableViewModel indexPathForItemType:SettingsItemTypeMemoryDebugging 1095 sectionIdentifier:SettingsSectionIdentifierDebug]; 1096 1097 SettingsSwitchItem* switchItem = 1098 base::mac::ObjCCastStrict<SettingsSwitchItem>( 1099 [self.tableViewModel itemAtIndexPath:switchPath]); 1100 1101 BOOL newSwitchValue = sender.isOn; 1102 switchItem.on = newSwitchValue; 1103 [_showMemoryDebugToolsEnabled setValue:newSwitchValue]; 1104} 1105 1106- (void)articlesForYouSwitchToggled:(UISwitch*)sender { 1107 NSIndexPath* switchPath = [self.tableViewModel 1108 indexPathForItemType:SettingsItemTypeArticlesForYou 1109 sectionIdentifier:SettingsSectionIdentifierAdvanced]; 1110 1111 SettingsSwitchItem* switchItem = 1112 base::mac::ObjCCastStrict<SettingsSwitchItem>( 1113 [self.tableViewModel itemAtIndexPath:switchPath]); 1114 1115 BOOL newSwitchValue = sender.isOn; 1116 switchItem.on = newSwitchValue; 1117 [_articlesEnabled setValue:newSwitchValue]; 1118} 1119 1120#if BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG) 1121- (void)viewSourceSwitchToggled:(UISwitch*)sender { 1122 NSIndexPath* switchPath = 1123 [self.tableViewModel indexPathForItemType:SettingsItemTypeViewSource 1124 sectionIdentifier:SettingsSectionIdentifierDebug]; 1125 1126 SettingsSwitchItem* switchItem = 1127 base::mac::ObjCCastStrict<SettingsSwitchItem>( 1128 [self.tableViewModel itemAtIndexPath:switchPath]); 1129 1130 BOOL newSwitchValue = sender.isOn; 1131 switchItem.on = newSwitchValue; 1132 [self setBooleanNSUserDefaultsValue:newSwitchValue forKey:kDevViewSourceKey]; 1133} 1134#endif // BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG) 1135 1136#pragma mark - Private methods 1137 1138- (void)showGoogleServices { 1139 DCHECK(!_googleServicesSettingsCoordinator); 1140 _googleServicesSettingsCoordinator = 1141 [[GoogleServicesSettingsCoordinator alloc] 1142 initWithBaseNavigationController:self.navigationController 1143 browser:_browser 1144 mode:GoogleServicesSettingsModeSettings]; 1145 _googleServicesSettingsCoordinator.handler = self.dispatcher; 1146 _googleServicesSettingsCoordinator.delegate = self; 1147 [_googleServicesSettingsCoordinator start]; 1148} 1149 1150- (void)showGoogleSync { 1151 DCHECK(!_manageSyncSettingsCoordinator); 1152 _manageSyncSettingsCoordinator = [[ManageSyncSettingsCoordinator alloc] 1153 initWithBaseNavigationController:self.navigationController 1154 browser:_browser]; 1155 _manageSyncSettingsCoordinator.delegate = self; 1156 [_manageSyncSettingsCoordinator start]; 1157} 1158 1159- (void)showPasswords { 1160 DCHECK(!_passwordsCoordinator); 1161 _passwordsCoordinator = [[PasswordsCoordinator alloc] 1162 initWithBaseNavigationController:self.navigationController 1163 browser:_browser]; 1164 _passwordsCoordinator.delegate = self; 1165 [_passwordsCoordinator start]; 1166} 1167 1168// Shows Safety Check Screen. 1169- (void)showSafetyCheck { 1170 DCHECK(!_safetyCheckCoordinator); 1171 _safetyCheckCoordinator = [[SafetyCheckCoordinator alloc] 1172 initWithBaseNavigationController:self.navigationController 1173 browser:_browser]; 1174 _safetyCheckCoordinator.delegate = self; 1175 [_safetyCheckCoordinator start]; 1176} 1177 1178// Checks if there are any remaining password issues from the last time password 1179// check was run. 1180- (BOOL)hasPasswordIssuesRemaining { 1181 return !_passwordCheckManager->GetCompromisedCredentials().empty(); 1182} 1183 1184// Displays a red issue state on |_safetyCheckItem| if there is a reamining 1185// issue for any of the checks. 1186- (void)setSafetyCheckIssueStateUnsafe:(BOOL)isUnsafe { 1187 if (isUnsafe && PreviousSafetyCheckIssueFound()) { 1188 UIImage* unSafeIconImage = [[UIImage imageNamed:@"settings_unsafe_state"] 1189 imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 1190 _safetyCheckItem.trailingImage = unSafeIconImage; 1191 _safetyCheckItem.trailingImageTintColor = [UIColor colorNamed:kRedColor]; 1192 } else { 1193 _safetyCheckItem.trailingImage = nil; 1194 _safetyCheckItem.trailingImageTintColor = nil; 1195 } 1196 [self reconfigureCellsForItems:@[ _safetyCheckItem ]]; 1197} 1198 1199// Shows Privacy screen. 1200- (void)showPrivacy { 1201 DCHECK(!_privacyCoordinator); 1202 _privacyCoordinator = [[PrivacyCoordinator alloc] 1203 initWithBaseNavigationController:self.navigationController 1204 browser:_browser]; 1205 _privacyCoordinator.delegate = self; 1206 [_privacyCoordinator start]; 1207} 1208 1209// Sets the NSUserDefaults BOOL |value| for |key|. 1210- (void)setBooleanNSUserDefaultsValue:(BOOL)value forKey:(NSString*)key { 1211 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; 1212 [defaults setBool:value forKey:key]; 1213 [defaults synchronize]; 1214} 1215 1216// Returns YES if a "Debug" section should be shown. This is always true for 1217// Chromium builds, but for official builds it is gated by an experimental flag 1218// because the "Debug" section should never be showing in stable channel. 1219- (BOOL)hasDebugSection { 1220#if BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG) 1221 return YES; 1222#else 1223 if (experimental_flags::IsMemoryDebuggingEnabled()) { 1224 return YES; 1225 } 1226 return NO; 1227#endif // BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG) 1228} 1229 1230// Updates the identity cell. 1231- (void)updateIdentityAccountItem:(TableViewAccountItem*)identityAccountItem { 1232 AuthenticationService* authService = 1233 AuthenticationServiceFactory::GetForBrowserState(_browserState); 1234 _identity = authService->GetAuthenticatedIdentity(); 1235 if (!_identity) { 1236 // This could occur during the sign out process. Just ignore as the account 1237 // cell will be replaced by the "Sign in" button. 1238 return; 1239 } 1240 identityAccountItem.image = [self userAccountImage]; 1241 identityAccountItem.text = [_identity userFullName]; 1242 identityAccountItem.detailText = _identity.userEmail; 1243} 1244 1245- (void)reloadAccountCell { 1246 if (![self.tableViewModel 1247 hasItemForItemType:SettingsItemTypeAccount 1248 sectionIdentifier:SettingsSectionIdentifierAccount]) { 1249 return; 1250 } 1251 NSIndexPath* accountCellIndexPath = [self.tableViewModel 1252 indexPathForItemType:SettingsItemTypeAccount 1253 sectionIdentifier:SettingsSectionIdentifierAccount]; 1254 TableViewAccountItem* identityAccountItem = 1255 base::mac::ObjCCast<TableViewAccountItem>( 1256 [self.tableViewModel itemAtIndexPath:accountCellIndexPath]); 1257 if (identityAccountItem) { 1258 [self updateIdentityAccountItem:identityAccountItem]; 1259 [self reconfigureCellsForItems:@[ identityAccountItem ]]; 1260 } 1261} 1262 1263// Updates the Sync & Google services item to display the right icon and status 1264// message in the detail text of the cell. 1265- (void)updateSyncAndGoogleServicesItem: 1266 (SettingsImageDetailTextItem*)googleServicesItem { 1267 googleServicesItem.detailTextColor = nil; 1268 syncer::SyncService* syncService = 1269 ProfileSyncServiceFactory::GetForBrowserState(_browserState); 1270 SyncSetupService* syncSetupService = 1271 SyncSetupServiceFactory::GetForBrowserState(_browserState); 1272 AuthenticationService* authService = 1273 AuthenticationServiceFactory::GetForBrowserState(_browserState); 1274 if (!authService->IsAuthenticated()) { 1275 // No sync status when the user is not signed-in. 1276 googleServicesItem.detailText = nil; 1277 googleServicesItem.image = 1278 [UIImage imageNamed:kSyncAndGoogleServicesImageName]; 1279 } else if (syncService->GetDisableReasons().Has( 1280 syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY)) { 1281 googleServicesItem.detailText = l10n_util::GetNSString( 1282 IDS_IOS_GOOGLE_SERVICES_SETTINGS_SYNC_DISABLBED_BY_ADMINISTRATOR_STATUS); 1283 googleServicesItem.image = 1284 [UIImage imageNamed:kSyncAndGoogleServicesSyncOffImageName]; 1285 } else if (!syncSetupService->IsFirstSetupComplete()) { 1286 googleServicesItem.detailText = 1287 l10n_util::GetNSString(IDS_IOS_SYNC_SETUP_IN_PROGRESS); 1288 googleServicesItem.image = 1289 [UIImage imageNamed:kSyncAndGoogleServicesSyncOnImageName]; 1290 } else if (!IsTransientSyncError(syncSetupService->GetSyncServiceState())) { 1291 googleServicesItem.detailTextColor = [UIColor colorNamed:kRedColor]; 1292 googleServicesItem.detailText = 1293 GetSyncErrorDescriptionForSyncSetupService(syncSetupService); 1294 googleServicesItem.image = 1295 [UIImage imageNamed:kSyncAndGoogleServicesSyncErrorImageName]; 1296 } else if (syncSetupService->IsSyncEnabled()) { 1297 googleServicesItem.detailText = 1298 l10n_util::GetNSString(IDS_IOS_SIGN_IN_TO_CHROME_SETTING_SYNC_ON); 1299 googleServicesItem.image = 1300 [UIImage imageNamed:kSyncAndGoogleServicesSyncOnImageName]; 1301 } else { 1302 googleServicesItem.detailText = 1303 l10n_util::GetNSString(IDS_IOS_SIGN_IN_TO_CHROME_SETTING_SYNC_OFF); 1304 googleServicesItem.image = 1305 [UIImage imageNamed:kSyncAndGoogleServicesSyncOffImageName]; 1306 } 1307 DCHECK(googleServicesItem.image); 1308} 1309 1310// Updates the Sync item to display the right icon and status message in the 1311// cell. 1312- (void)updateGoogleSyncCellItem:(TableViewDetailIconItem*)googleSyncItem { 1313 syncer::SyncService* syncService = 1314 ProfileSyncServiceFactory::GetForBrowserState(_browserState); 1315 SyncSetupService* syncSetupService = 1316 SyncSetupServiceFactory::GetForBrowserState(_browserState); 1317 if (syncService->GetDisableReasons().Has( 1318 syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY)) { 1319 googleSyncItem.detailText = l10n_util::GetNSString( 1320 IDS_IOS_GOOGLE_SERVICES_SETTINGS_SYNC_DISABLBED_BY_ADMINISTRATOR_STATUS); 1321 googleSyncItem.iconImageName = kSyncAndGoogleServicesSyncOffImageName; 1322 } else if (!IsTransientSyncError(syncSetupService->GetSyncServiceState())) { 1323 googleSyncItem.detailText = 1324 GetSyncErrorDescriptionForSyncSetupService(syncSetupService); 1325 googleSyncItem.iconImageName = kSyncAndGoogleServicesSyncErrorImageName; 1326 } else if (syncSetupService->IsFirstSetupComplete() && 1327 syncSetupService->IsSyncEnabled()) { 1328 googleSyncItem.detailText = l10n_util::GetNSString(IDS_IOS_SETTING_ON); 1329 googleSyncItem.iconImageName = kSyncAndGoogleServicesSyncOnImageName; 1330 } else { 1331 googleSyncItem.detailText = l10n_util::GetNSString(IDS_IOS_SETTING_OFF); 1332 googleSyncItem.iconImageName = kSyncAndGoogleServicesSyncOffImageName; 1333 } 1334} 1335 1336// Updates and reloads the Google service cell. 1337- (void)reloadSyncAndGoogleServicesCell { 1338 if (base::FeatureList::IsEnabled(signin::kMobileIdentityConsistency)) { 1339 bool googleSyncCellInitialized = [self.tableViewModel 1340 hasItemForItemType:SettingsItemTypeGoogleSync 1341 sectionIdentifier:SettingsSectionIdentifierAccount]; 1342 AuthenticationService* authService = 1343 AuthenticationServiceFactory::GetForBrowserState(_browserState); 1344 if (authService->IsAuthenticated()) { 1345 if (!googleSyncCellInitialized) { 1346 [self.tableViewModel addItem:[self googleSyncCellItem] 1347 toSectionWithIdentifier:SettingsSectionIdentifierAccount]; 1348 } else { 1349 NSIndexPath* syncCellIndexPath = [self.tableViewModel 1350 indexPathForItemType:SettingsItemTypeGoogleSync 1351 sectionIdentifier:SettingsSectionIdentifierAccount]; 1352 TableViewDetailIconItem* detailIconItem = 1353 base::mac::ObjCCast<TableViewDetailIconItem>( 1354 [self.tableViewModel itemAtIndexPath:syncCellIndexPath]); 1355 [self updateGoogleSyncCellItem:detailIconItem]; 1356 [self reconfigureCellsForItems:@[ detailIconItem ]]; 1357 } 1358 } else { 1359 if (googleSyncCellInitialized) { 1360 [self.tableViewModel 1361 removeItemWithType:SettingsItemTypeGoogleSync 1362 fromSectionWithIdentifier:SettingsSectionIdentifierAccount]; 1363 } 1364 } 1365 } else { 1366 NSIndexPath* googleServicesCellIndexPath = [self.tableViewModel 1367 indexPathForItemType:SettingsItemTypeSyncAndGoogleServices 1368 sectionIdentifier:SettingsSectionIdentifierAccount]; 1369 SettingsImageDetailTextItem* googleServicesItem = 1370 base::mac::ObjCCast<SettingsImageDetailTextItem>( 1371 [self.tableViewModel itemAtIndexPath:googleServicesCellIndexPath]); 1372 DCHECK(googleServicesItem); 1373 [self updateSyncAndGoogleServicesItem:googleServicesItem]; 1374 [self reconfigureCellsForItems:@[ googleServicesItem ]]; 1375 } 1376} 1377 1378// Check if the default search engine is managed by policy. 1379- (BOOL)isDefaultSearchEngineManagedByPolicy { 1380 const base::DictionaryValue* dict = _browserState->GetPrefs()->GetDictionary( 1381 DefaultSearchManager::kDefaultSearchProviderDataPrefName); 1382 1383 if (dict) { 1384 if (dict->FindBoolPath(DefaultSearchManager::kDisabledByPolicy) || 1385 dict->FindBoolPath(prefs::kDefaultSearchProviderEnabled)) 1386 return YES; 1387 } 1388 return NO; 1389} 1390 1391#pragma mark - SigninPresenter 1392 1393- (void)showSignin:(ShowSigninCommand*)command { 1394 [self.dispatcher showSignin:command baseViewController:self]; 1395} 1396 1397#pragma mark Sign in 1398 1399- (void)showSignInWithIdentity:(ChromeIdentity*)identity 1400 promoAction:(signin_metrics::PromoAction)promoAction 1401 completion:(ShowSigninCommandCompletionCallback)completion { 1402 DCHECK(!self.isSigninInProgress); 1403 self.isSigninInProgress = YES; 1404 __weak __typeof(self) weakSelf = self; 1405 ShowSigninCommand* command = [[ShowSigninCommand alloc] 1406 initWithOperation:AUTHENTICATION_OPERATION_SIGNIN 1407 identity:identity 1408 accessPoint:signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS 1409 promoAction:promoAction 1410 callback:^(BOOL success) { 1411 if (completion) 1412 completion(success); 1413 [weakSelf didFinishSignin:success]; 1414 }]; 1415 [self.dispatcher showSignin:command baseViewController:self]; 1416} 1417 1418- (void)didFinishSignin:(BOOL)signedIn { 1419 // The sign-in is done. The sign-in promo cell or account cell can be 1420 // reloaded. 1421 DCHECK(self.isSigninInProgress); 1422 self.isSigninInProgress = NO; 1423 DCHECK(!_settingsHasBeenDismissed); 1424 [self reloadData]; 1425} 1426 1427#pragma mark SettingsControllerProtocol 1428 1429- (void)reportDismissalUserAction { 1430 base::RecordAction(base::UserMetricsAction("MobileSettingsClose")); 1431} 1432 1433- (void)reportBackUserAction { 1434 // Not called for root settings controller. 1435 NOTREACHED(); 1436} 1437 1438- (void)settingsWillBeDismissed { 1439 DCHECK(!_settingsHasBeenDismissed); 1440 1441 [_googleServicesSettingsCoordinator stop]; 1442 _googleServicesSettingsCoordinator.delegate = nil; 1443 _googleServicesSettingsCoordinator = nil; 1444 1445 [_safetyCheckCoordinator stop]; 1446 _safetyCheckCoordinator = nil; 1447 1448 [_passwordsCoordinator stop]; 1449 _passwordsCoordinator.delegate = nil; 1450 _passwordsCoordinator = nil; 1451 1452 [_privacyCoordinator stop]; 1453 _privacyCoordinator = nil; 1454 1455 [_manageSyncSettingsCoordinator stop]; 1456 _manageSyncSettingsCoordinator = nil; 1457 1458 _settingsHasBeenDismissed = YES; 1459 DCHECK(!self.isSigninInProgress); 1460 [_signinPromoViewMediator signinPromoViewIsRemoved]; 1461 _signinPromoViewMediator = nil; 1462 [self stopBrowserStateServiceObservers]; 1463} 1464 1465#pragma mark SyncObserverModelBridge 1466 1467- (void)onSyncStateChanged { 1468 [self reloadSyncAndGoogleServicesCell]; 1469} 1470 1471#pragma mark - IdentityRefreshLogic 1472 1473// Image used for loggedin user account that supports caching. 1474- (UIImage*)userAccountImage { 1475 UIImage* image = ios::GetChromeBrowserProvider() 1476 ->GetChromeIdentityService() 1477 ->GetCachedAvatarForIdentity(_identity); 1478 if (!image) { 1479 image = ios::GetChromeBrowserProvider() 1480 ->GetSigninResourcesProvider() 1481 ->GetDefaultAvatar(); 1482 // No cached image, trigger a fetch, which will notify all observers 1483 // (including the corresponding AccountViewBase). 1484 ios::GetChromeBrowserProvider() 1485 ->GetChromeIdentityService() 1486 ->GetAvatarForIdentity(_identity, nil); 1487 } 1488 1489 // If the currently used image has already been resized, use it. 1490 if (_resizedImage && _oldImage == image) 1491 return _resizedImage; 1492 1493 _oldImage = image; 1494 1495 // Resize the profile image. 1496 CGFloat dimension = kAccountProfilePhotoDimension; 1497 if (image.size.width != dimension || image.size.height != dimension) { 1498 image = ResizeImage(image, CGSizeMake(dimension, dimension), 1499 ProjectionMode::kAspectFit); 1500 } 1501 _resizedImage = image; 1502 return _resizedImage; 1503} 1504 1505#pragma mark - SearchEngineObserverBridge 1506 1507- (void)searchEngineChanged { 1508 _defaultSearchEngineItem.detailText = 1509 base::SysUTF16ToNSString(GetDefaultSearchEngineName( 1510 ios::TemplateURLServiceFactory::GetForBrowserState(_browserState))); 1511 [self reconfigureCellsForItems:@[ _defaultSearchEngineItem ]]; 1512} 1513 1514#pragma mark ChromeIdentityServiceObserver 1515 1516- (void)profileUpdate:(ChromeIdentity*)identity { 1517 if ([_identity isEqual:identity]) { 1518 [self reloadAccountCell]; 1519 } 1520} 1521 1522- (void)chromeIdentityServiceWillBeDestroyed { 1523 _identityServiceObserver.reset(); 1524} 1525 1526#pragma mark - BooleanObserver 1527 1528- (void)booleanDidChange:(id<ObservableBoolean>)observableBoolean { 1529 if (observableBoolean == _showMemoryDebugToolsEnabled) { 1530 // Update the Item. 1531 _showMemoryDebugToolsItem.on = [_showMemoryDebugToolsEnabled value]; 1532 // Update the Cell. 1533 [self reconfigureCellsForItems:@[ _showMemoryDebugToolsItem ]]; 1534 } else if (observableBoolean == _articlesEnabled) { 1535 _articlesForYouItem.on = [_articlesEnabled value]; 1536 [self reconfigureCellsForItems:@[ _articlesForYouItem ]]; 1537 } else { 1538 NOTREACHED(); 1539 } 1540} 1541 1542#pragma mark - PasswordCheckObserver 1543 1544- (void)passwordCheckStateDidChange:(PasswordCheckState)state { 1545 [self setSafetyCheckIssueStateUnsafe:[self hasPasswordIssuesRemaining]]; 1546} 1547 1548- (void)compromisedCredentialsDidChange: 1549 (password_manager::InsecureCredentialsManager::CredentialsView)credentials { 1550 [self setSafetyCheckIssueStateUnsafe:[self hasPasswordIssuesRemaining]]; 1551} 1552 1553#pragma mark - PrefObserverDelegate 1554 1555- (void)onPreferenceChanged:(const std::string&)preferenceName { 1556 if (preferenceName == prefs::kVoiceSearchLocale) { 1557 voice::SpeechInputLocaleConfig* localeConfig = 1558 voice::SpeechInputLocaleConfig::GetInstance(); 1559 voice::SpeechInputLocale locale = 1560 _voiceLocaleCode.GetValue().length() 1561 ? localeConfig->GetLocaleForCode(_voiceLocaleCode.GetValue()) 1562 : localeConfig->GetDefaultLocale(); 1563 NSString* languageName = base::SysUTF16ToNSString(locale.display_name); 1564 _voiceSearchDetailItem.detailText = languageName; 1565 [self reconfigureCellsForItems:@[ _voiceSearchDetailItem ]]; 1566 } 1567 1568 if (preferenceName == password_manager::prefs::kCredentialsEnableService) { 1569 BOOL passwordsEnabled = 1570 _browserState->GetPrefs()->GetBoolean(preferenceName); 1571 NSString* passwordsDetail = 1572 passwordsEnabled ? l10n_util::GetNSString(IDS_IOS_SETTING_ON) 1573 : l10n_util::GetNSString(IDS_IOS_SETTING_OFF); 1574 _passwordsDetailItem.detailText = passwordsDetail; 1575 [self reconfigureCellsForItems:@[ _passwordsDetailItem ]]; 1576 } 1577 1578 if (preferenceName == autofill::prefs::kAutofillProfileEnabled) { 1579 BOOL autofillProfileEnabled = 1580 autofill::prefs::IsAutofillProfileEnabled(_browserState->GetPrefs()); 1581 NSString* detailText = autofillProfileEnabled 1582 ? l10n_util::GetNSString(IDS_IOS_SETTING_ON) 1583 : l10n_util::GetNSString(IDS_IOS_SETTING_OFF); 1584 _autoFillProfileDetailItem.detailText = detailText; 1585 [self reconfigureCellsForItems:@[ _autoFillProfileDetailItem ]]; 1586 } 1587 1588 if (preferenceName == autofill::prefs::kAutofillCreditCardEnabled) { 1589 BOOL autofillCreditCardEnabled = 1590 autofill::prefs::IsAutofillCreditCardEnabled(_browserState->GetPrefs()); 1591 NSString* detailText = autofillCreditCardEnabled 1592 ? l10n_util::GetNSString(IDS_IOS_SETTING_ON) 1593 : l10n_util::GetNSString(IDS_IOS_SETTING_OFF); 1594 _autoFillCreditCardDetailItem.detailText = detailText; 1595 [self reconfigureCellsForItems:@[ _autoFillCreditCardDetailItem ]]; 1596 } 1597} 1598 1599#pragma mark - SigninPromoViewConsumer 1600 1601- (void)configureSigninPromoWithConfigurator: 1602 (SigninPromoViewConfigurator*)configurator 1603 identityChanged:(BOOL)identityChanged { 1604 DCHECK(!self.isSigninInProgress); 1605 if (![self.tableViewModel 1606 hasItemForItemType:SettingsItemTypeSigninPromo 1607 sectionIdentifier:SettingsSectionIdentifierSignIn]) { 1608 return; 1609 } 1610 NSIndexPath* signinPromoCellIndexPath = [self.tableViewModel 1611 indexPathForItemType:SettingsItemTypeSigninPromo 1612 sectionIdentifier:SettingsSectionIdentifierSignIn]; 1613 DCHECK(signinPromoCellIndexPath.item != NSNotFound); 1614 TableViewSigninPromoItem* signinPromoItem = 1615 base::mac::ObjCCast<TableViewSigninPromoItem>( 1616 [self.tableViewModel itemAtIndexPath:signinPromoCellIndexPath]); 1617 if (signinPromoItem) { 1618 signinPromoItem.configurator = configurator; 1619 signinPromoItem.delegate = _signinPromoViewMediator; 1620 [self reconfigureCellsForItems:@[ signinPromoItem ]]; 1621 } 1622} 1623 1624- (void)signinPromoViewMediator:(SigninPromoViewMediator*)mediator 1625 shouldOpenSigninWithIdentity:(ChromeIdentity*)identity 1626 promoAction:(signin_metrics::PromoAction)promoAction 1627 completion: 1628 (ShowSigninCommandCompletionCallback)completion { 1629 [self showSignInWithIdentity:identity 1630 promoAction:promoAction 1631 completion:completion]; 1632} 1633 1634- (void)signinPromoViewMediatorCloseButtonWasTapped: 1635 (SigninPromoViewMediator*)mediator { 1636 [self reloadData]; 1637} 1638 1639#pragma mark - GoogleServicesSettingsCoordinatorDelegate 1640 1641- (void)googleServicesSettingsCoordinatorDidRemove: 1642 (GoogleServicesSettingsCoordinator*)coordinator { 1643 DCHECK_EQ(_googleServicesSettingsCoordinator, coordinator); 1644 [_googleServicesSettingsCoordinator stop]; 1645 _googleServicesSettingsCoordinator.delegate = nil; 1646 _googleServicesSettingsCoordinator = nil; 1647} 1648 1649#pragma mark - SafetyCheckCoordinatorDelegate 1650 1651- (void)safetyCheckCoordinatorDidRemove:(SafetyCheckCoordinator*)coordinator { 1652 DCHECK_EQ(_safetyCheckCoordinator, coordinator); 1653 [_safetyCheckCoordinator stop]; 1654 _safetyCheckCoordinator.delegate = nil; 1655 _safetyCheckCoordinator = nil; 1656} 1657 1658#pragma mark - SafetyCheckCoordinatorDelegate 1659 1660- (void)passwordsCoordinatorDidRemove:(PasswordsCoordinator*)coordinator { 1661 DCHECK_EQ(_passwordsCoordinator, coordinator); 1662 [_passwordsCoordinator stop]; 1663 _passwordsCoordinator.delegate = nil; 1664 _passwordsCoordinator = nil; 1665} 1666 1667#pragma mark - PrivacyCoordinatorDelegate 1668 1669- (void)privacyCoordinatorViewControllerWasRemoved: 1670 (PrivacyCoordinator*)coordinator { 1671 DCHECK_EQ(_privacyCoordinator, coordinator); 1672 [_privacyCoordinator stop]; 1673 _privacyCoordinator = nil; 1674} 1675 1676#pragma mark - IdentityManagerObserverBridgeDelegate 1677 1678// Notifies this controller that the sign in state has changed. 1679- (void)signinStateDidChange { 1680 // While the sign-in view is in progress, the TableView should not be 1681 // updated. Otherwise, it would lead to an UI glitch either while the sign 1682 // in UI is appearing or disappearing. The TableView will be reloaded once 1683 // the animation is finished. 1684 // See: -[SettingsTableViewController didFinishSignin:]. 1685 if (self.isSigninInProgress) 1686 return; 1687 // Sign in state changes are rare. Just reload the entire table when 1688 // this happens. 1689 [self reloadData]; 1690} 1691- (void)onPrimaryAccountSet:(const CoreAccountInfo&)primaryAccountInfo { 1692 [self signinStateDidChange]; 1693} 1694 1695- (void)onPrimaryAccountCleared: 1696 (const CoreAccountInfo&)previousPrimaryAccountInfo { 1697 [self signinStateDidChange]; 1698} 1699 1700#pragma mark - UIAdaptivePresentationControllerDelegate 1701 1702- (void)presentationControllerDidDismiss: 1703 (UIPresentationController*)presentationController { 1704 base::RecordAction(base::UserMetricsAction("IOSSettingsCloseWithSwipe")); 1705} 1706 1707#pragma mark - PopoverLabelViewControllerDelegate 1708 1709- (void)didTapLinkURL:(NSURL*)URL { 1710 GURL convertedURL = net::GURLWithNSURL(URL); 1711 [self view:nil didTapLinkURL:convertedURL]; 1712} 1713 1714#pragma mark - ManageSyncSettingsCoordinatorDelegate 1715 1716- (void)manageSyncSettingsCoordinatorWasRemoved: 1717 (ManageSyncSettingsCoordinator*)coordinator { 1718 DCHECK_EQ(_manageSyncSettingsCoordinator, coordinator); 1719 [_manageSyncSettingsCoordinator stop]; 1720 _manageSyncSettingsCoordinator = nil; 1721} 1722 1723@end 1724