Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(109)

Side by Side Diff: ios/chrome/browser/ui/settings/settings_collection_view_controller.mm

Issue 2587023002: Upstream Chrome on iOS source code [8/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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_collection_view_controller.h"
6
7 #include <memory>
8
9 #import "base/ios/weak_nsobject.h"
10 #import "base/mac/foundation_util.h"
11 #import "base/mac/scoped_nsobject.h"
12 #include "base/metrics/user_metrics.h"
13 #include "base/scoped_observer.h"
14 #include "base/strings/sys_string_conversions.h"
15 #include "components/autofill/core/common/autofill_pref_names.h"
16 #include "components/browser_sync/profile_sync_service.h"
17 #include "components/keyed_service/core/service_access_type.h"
18 #include "components/password_manager/core/browser/password_bubble_experiment.h"
19 #include "components/password_manager/core/browser/password_store.h"
20 #include "components/password_manager/core/common/password_manager_pref_names.h"
21 #include "components/prefs/pref_change_registrar.h"
22 #include "components/prefs/pref_service.h"
23 #include "components/search_engines/util.h"
24 #include "components/signin/core/browser/signin_manager.h"
25 #include "components/signin/core/common/signin_pref_names.h"
26 #include "components/strings/grit/components_strings.h"
27 #include "ios/chrome/browser/application_context.h"
28 #import "ios/chrome/browser/browser_state/chrome_browser_state.h"
29 #include "ios/chrome/browser/browser_state/chrome_browser_state_removal_controll er.h"
30 #include "ios/chrome/browser/experimental_flags.h"
31 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
32 #include "ios/chrome/browser/pref_names.h"
33 #import "ios/chrome/browser/prefs/pref_observer_bridge.h"
34 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
35 #import "ios/chrome/browser/signin/authentication_service.h"
36 #include "ios/chrome/browser/signin/authentication_service_factory.h"
37 #include "ios/chrome/browser/signin/chrome_identity_service_observer_bridge.h"
38 #include "ios/chrome/browser/signin/signin_manager_factory.h"
39 #include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h"
40 #include "ios/chrome/browser/sync/sync_observer_bridge.h"
41 #include "ios/chrome/browser/sync/sync_setup_service.h"
42 #include "ios/chrome/browser/sync/sync_setup_service_factory.h"
43 #import "ios/chrome/browser/ui/authentication/signin_interaction_controller.h"
44 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_account_ite m.h"
45 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_detail_item .h"
46 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_switch_item .h"
47 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h "
48 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
49 #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
50 #import "ios/chrome/browser/ui/settings/about_chrome_collection_view_controller. h"
51 #import "ios/chrome/browser/ui/settings/accounts_collection_view_controller.h"
52 #import "ios/chrome/browser/ui/settings/autofill_collection_view_controller.h"
53 #import "ios/chrome/browser/ui/settings/bandwidth_management_collection_view_con troller.h"
54 #import "ios/chrome/browser/ui/settings/cells/account_signin_item.h"
55 #import "ios/chrome/browser/ui/settings/content_settings_collection_view_control ler.h"
56 #import "ios/chrome/browser/ui/settings/material_cell_catalog_view_controller.h"
57 #import "ios/chrome/browser/ui/settings/native_apps_collection_view_controller.h "
58 #import "ios/chrome/browser/ui/settings/privacy_collection_view_controller.h"
59 #import "ios/chrome/browser/ui/settings/save_passwords_collection_view_controlle r.h"
60 #import "ios/chrome/browser/ui/settings/search_engine_settings_collection_view_c ontroller.h"
61 #import "ios/chrome/browser/ui/settings/settings_utils.h"
62 #import "ios/chrome/browser/ui/settings/utils/pref_backed_boolean.h"
63 #import "ios/chrome/browser/ui/settings/voicesearch_collection_view_controller.h "
64 #import "ios/chrome/browser/ui/sync/sync_util.h"
65 #import "ios/chrome/browser/ui/uikit_ui_util.h"
66 #include "ios/chrome/browser/voice/speech_input_locale_config.h"
67 #include "ios/chrome/grit/ios_chromium_strings.h"
68 #include "ios/chrome/grit/ios_strings.h"
69 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
70 #import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
71 #include "ios/public/provider/chrome/browser/signin/signin_resources_provider.h"
72 #include "ios/public/provider/chrome/browser/voice/voice_search_prefs.h"
73 #include "ui/base/l10n/l10n_util_mac.h"
74
75 NSString* const kSettingsCollectionViewId = @"kSettingsCollectionViewId";
76 NSString* const kSettingsSignInCellId = @"kSettingsSignInCellId";
77 NSString* const kSettingsAccountCellId = @"kSettingsAccountCellId";
78 NSString* const kSettingsSearchEngineCellId = @"Search Engine";
79 NSString* const kSettingsVoiceSearchCellId = @"Voice Search Settings";
80
81 @interface SettingsCollectionViewController (NotificationBridgeDelegate)
82 // Notifies this controller that the sign in state has changed.
83 - (void)onSignInStateChanged;
84 @end
85
86 namespace {
87
88 const CGFloat kAccountProfilePhotoDimension = 40.0f;
89
90 typedef NS_ENUM(NSInteger, SectionIdentifier) {
91 SectionIdentifierSignIn = kSectionIdentifierEnumZero,
92 SectionIdentifierBasics,
93 SectionIdentifierAdvanced,
94 SectionIdentifierInfo,
95 SectionIdentifierDebug,
96 };
97
98 typedef NS_ENUM(NSInteger, ItemType) {
99 ItemTypeSignInButton = kItemTypeEnumZero,
100 ItemTypeAccount,
101 ItemTypeHeader,
102 ItemTypeSearchEngine,
103 ItemTypeSavedPasswords,
104 ItemTypeAutofill,
105 ItemTypeNativeApps,
106 ItemTypeVoiceSearch,
107 ItemTypePrivacy,
108 ItemTypeContentSettings,
109 ItemTypeBandwidth,
110 ItemTypeAboutChrome,
111 ItemTypeMemoryDebugging,
112 ItemTypeViewSource,
113 ItemTypeLogJavascript,
114 ItemTypeShowAutofillTypePredictions,
115 ItemTypeCellCatalog,
116 };
117
118 #if CHROMIUM_BUILD && !defined(NDEBUG)
119 NSString* kDevViewSourceKey = @"DevViewSource";
120 NSString* kLogJavascriptKey = @"LogJavascript";
121 NSString* kShowAutofillTypePredictionsKey = @"ShowAutofillTypePredictions";
122 #endif // CHROMIUM_BUILD && !defined(NDEBUG)
123
124 #pragma mark - SigninObserverBridge Class
125
126 class SigninObserverBridge : public SigninManagerBase::Observer {
127 public:
128 SigninObserverBridge(ios::ChromeBrowserState* browserState,
129 SettingsCollectionViewController* owner);
130 ~SigninObserverBridge() override{};
131
132 // SigninManagerBase::Observer implementation:
133 void GoogleSigninSucceeded(const std::string& account_id,
134 const std::string& username,
135 const std::string& password) override;
136 void GoogleSignedOut(const std::string& account_id,
137 const std::string& username) override;
138
139 private:
140 base::WeakNSObject<SettingsCollectionViewController> owner_;
141 ScopedObserver<SigninManager, SigninObserverBridge> observer_;
142 };
143
144 SigninObserverBridge::SigninObserverBridge(
145 ios::ChromeBrowserState* browserState,
146 SettingsCollectionViewController* owner)
147 : owner_(owner), observer_(this) {
148 DCHECK(owner_);
149 SigninManager* sigin_manager =
150 ios::SigninManagerFactory::GetForBrowserState(browserState);
151 if (!sigin_manager)
152 return;
153 observer_.Add(sigin_manager);
154 }
155
156 void SigninObserverBridge::GoogleSigninSucceeded(const std::string& account_id,
157 const std::string& username,
158 const std::string& password) {
159 [owner_ onSignInStateChanged];
160 }
161
162 void SigninObserverBridge::GoogleSignedOut(const std::string& account_id,
163 const std::string& username) {
164 [owner_ onSignInStateChanged];
165 }
166
167 } // namespace
168
169 #pragma mark - SettingsCollectionViewController
170
171 @interface SettingsCollectionViewController ()<SettingsControllerProtocol,
172 SyncObserverModelBridge,
173 ChromeIdentityServiceObserver,
174 BooleanObserver,
175 PrefObserverDelegate> {
176 // The main browser state that hold the settings. Never off the record.
177 ios::ChromeBrowserState* _mainBrowserState; // weak
178
179 // The current browser state. It is either |_mainBrowserState|
180 // or |_mainBrowserState->GetOffTheRecordChromeBrowserState()|.
181 ios::ChromeBrowserState* _currentBrowserState; // weak
182 std::unique_ptr<SigninObserverBridge> _notificationBridge;
183 std::unique_ptr<SyncObserverBridge> _syncObserverBridge;
184 base::scoped_nsobject<SigninInteractionController>
185 _signinInteractionController;
186 // Whether the impression of the Signin button has already been recorded.
187 BOOL _hasRecordedSigninImpression;
188 // PrefBackedBoolean for ShowMemoryDebugTools switch.
189 base::scoped_nsobject<PrefBackedBoolean> _showMemoryDebugToolsEnabled;
190 // The item related to the switch for the show suggestions setting.
191 base::scoped_nsobject<CollectionViewSwitchItem> _showMemoryDebugToolsItem;
192
193 // Cached resized profile image.
194 base::scoped_nsobject<UIImage> _resizedImage;
195 base::WeakNSObject<UIImage> _oldImage;
196
197 // Identity object and observer used for Account Item refresh.
198 base::scoped_nsobject<ChromeIdentity> _identity;
199 std::unique_ptr<ChromeIdentityServiceObserverBridge> _identityServiceObserver;
200
201 // PrefMember for voice locale code.
202 StringPrefMember _voiceLocaleCode;
203 // Pref observer to track changes to prefs.
204 std::unique_ptr<PrefObserverBridge> _prefObserverBridge;
205 // TODO(crbug.com/662435): Refactor PrefObserverBridge so it owns the
206 // PrefChangeRegistrar.
207 // Registrar for pref changes notifications.
208 PrefChangeRegistrar _prefChangeRegistrar;
209
210 // Updatable Items.
211 base::scoped_nsobject<CollectionViewDetailItem> _voiceSearchDetailItem;
212 base::scoped_nsobject<CollectionViewDetailItem> _defaultSearchEngineItem;
213 base::scoped_nsobject<CollectionViewDetailItem> _savePasswordsDetailItem;
214 base::scoped_nsobject<CollectionViewDetailItem> _autoFillDetailItem;
215 }
216
217 // Stops observing browser state services. This is required during the shutdown
218 // phase to avoid observing services for a profile that is being killed.
219 - (void)stopBrowserStateServiceObservers;
220
221 @end
222
223 @implementation SettingsCollectionViewController
224
225 #pragma mark Initialization
226
227 - (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)mainBrowserState
228 currentBrowserState:
229 (ios::ChromeBrowserState*)currentBrowserState {
230 DCHECK(mainBrowserState);
231 DCHECK(currentBrowserState);
232 DCHECK_EQ(mainBrowserState,
233 currentBrowserState->GetOriginalChromeBrowserState());
234 self = [super initWithStyle:CollectionViewControllerStyleAppBar];
235 if (self) {
236 _mainBrowserState = mainBrowserState;
237 _currentBrowserState = currentBrowserState;
238 self.title = l10n_util::GetNSStringWithFixup(IDS_IOS_SETTINGS_TITLE);
239 self.collectionViewAccessibilityIdentifier = kSettingsCollectionViewId;
240 _notificationBridge.reset(
241 new SigninObserverBridge(_mainBrowserState, self));
242 syncer::SyncService* syncService =
243 IOSChromeProfileSyncServiceFactory::GetForBrowserState(
244 _mainBrowserState);
245 _syncObserverBridge.reset(new SyncObserverBridge(self, syncService));
246
247 _showMemoryDebugToolsEnabled.reset([[PrefBackedBoolean alloc]
248 initWithPrefService:GetApplicationContext()->GetLocalState()
249 prefName:prefs::kShowMemoryDebuggingTools]);
250 [_showMemoryDebugToolsEnabled setObserver:self];
251
252 AuthenticationService* authService =
253 AuthenticationServiceFactory::GetForBrowserState(_mainBrowserState);
254 _identity.reset([authService->GetAuthenticatedIdentity() retain]);
255 _identityServiceObserver.reset(
256 new ChromeIdentityServiceObserverBridge(self));
257
258 PrefService* prefService = _mainBrowserState->GetPrefs();
259
260 _voiceLocaleCode.Init(prefs::kVoiceSearchLocale, prefService);
261
262 _prefChangeRegistrar.Init(prefService);
263 _prefObserverBridge.reset(new PrefObserverBridge(self));
264 // Register to observe any changes on Perf backed values displayed by the
265 // screen.
266 _prefObserverBridge->ObserveChangesForPreference(prefs::kVoiceSearchLocale,
267 &_prefChangeRegistrar);
268 _prefObserverBridge->ObserveChangesForPreference(
269 password_manager::prefs::kPasswordManagerSavingEnabled,
270 &_prefChangeRegistrar);
271 _prefObserverBridge->ObserveChangesForPreference(
272 autofill::prefs::kAutofillEnabled, &_prefChangeRegistrar);
273
274 [self loadModel];
275 }
276 return self;
277 }
278
279 - (void)dealloc {
280 [self stopBrowserStateServiceObservers];
281 [super dealloc];
282 }
283
284 - (void)stopBrowserStateServiceObservers {
285 _syncObserverBridge.reset();
286 _notificationBridge.reset();
287 _identityServiceObserver.reset();
288 [_showMemoryDebugToolsEnabled setObserver:nil];
289 }
290
291 - (SigninInteractionController*)signinInteractionController {
292 return _signinInteractionController;
293 }
294
295 #pragma mark View lifecycle
296
297 // TODO(crbug.com/661915): Refactor TemplateURLObserver and re-implement this so
298 // it observes the default search engine name instead of reloading on
299 // ViewWillAppear.
300 - (void)viewWillAppear:(BOOL)animated {
301 [super viewWillAppear:animated];
302 [self updateSearchCell];
303 }
304
305 #pragma mark SettingsRootCollectionViewController
306
307 - (void)loadModel {
308 [super loadModel];
309
310 CollectionViewModel* model = self.collectionViewModel;
311
312 // Sign in/Account section
313 [model addSectionWithIdentifier:SectionIdentifierSignIn];
314 AuthenticationService* authService =
315 AuthenticationServiceFactory::GetForBrowserState(_mainBrowserState);
316 if (!authService->IsAuthenticated()) {
317 if (!_hasRecordedSigninImpression) {
318 // Once the Settings are open, this button impression will at most be
319 // recorded once until they are closed.
320 base::RecordAction(
321 base::UserMetricsAction("Signin_Impression_FromSettings"));
322 _hasRecordedSigninImpression = YES;
323 }
324 [model addItem:[self signInTextItem]
325 toSectionWithIdentifier:SectionIdentifierSignIn];
326 } else {
327 [model addItem:[self accountCellItem]
328 toSectionWithIdentifier:SectionIdentifierSignIn];
329 }
330
331 // Basics section
332 [model addSectionWithIdentifier:SectionIdentifierBasics];
333 CollectionViewTextItem* basicsHeader = [
334 [[CollectionViewTextItem alloc] initWithType:ItemTypeHeader] autorelease];
335 basicsHeader.text = l10n_util::GetNSString(IDS_IOS_OPTIONS_GENERAL_TAB_LABEL);
336 [model setHeader:basicsHeader
337 forSectionWithIdentifier:SectionIdentifierBasics];
338 [model addItem:[self searchEngineDetailItem]
339 toSectionWithIdentifier:SectionIdentifierBasics];
340 [model addItem:[self savePasswordsDetailItem]
341 toSectionWithIdentifier:SectionIdentifierBasics];
342 [model addItem:[self autoFillDetailItem]
343 toSectionWithIdentifier:SectionIdentifierBasics];
344 [model addItem:[self nativeAppsDetailItem]
345 toSectionWithIdentifier:SectionIdentifierBasics];
346
347 // Advanced Section
348 [model addSectionWithIdentifier:SectionIdentifierAdvanced];
349 CollectionViewTextItem* advancedHeader = [
350 [[CollectionViewTextItem alloc] initWithType:ItemTypeHeader] autorelease];
351 advancedHeader.text =
352 l10n_util::GetNSString(IDS_IOS_OPTIONS_ADVANCED_TAB_LABEL);
353 [model setHeader:advancedHeader
354 forSectionWithIdentifier:SectionIdentifierAdvanced];
355 [model addItem:[self voiceSearchDetailItem]
356 toSectionWithIdentifier:SectionIdentifierAdvanced];
357 [model addItem:[self privacyDetailItem]
358 toSectionWithIdentifier:SectionIdentifierAdvanced];
359 [model addItem:[self contentSettingsDetailItem]
360 toSectionWithIdentifier:SectionIdentifierAdvanced];
361 [model addItem:[self bandwidthManagementDetailItem]
362 toSectionWithIdentifier:SectionIdentifierAdvanced];
363
364 // Info Section
365 [model addSectionWithIdentifier:SectionIdentifierInfo];
366 [model addItem:[self aboutChromeDetailItem]
367 toSectionWithIdentifier:SectionIdentifierInfo];
368
369 // Debug Section
370 if ([self hasDebugSection]) {
371 [model addSectionWithIdentifier:SectionIdentifierDebug];
372 CollectionViewTextItem* debugHeader = [[[CollectionViewTextItem alloc]
373 initWithType:ItemTypeHeader] autorelease];
374 debugHeader.text = @"Debug";
375 [model setHeader:debugHeader
376 forSectionWithIdentifier:SectionIdentifierDebug];
377 }
378
379 if (experimental_flags::IsMemoryDebuggingEnabled()) {
380 _showMemoryDebugToolsItem.reset([[self showMemoryDebugSwitchItem] retain]);
381 [model addItem:_showMemoryDebugToolsItem
382 toSectionWithIdentifier:SectionIdentifierDebug];
383 }
384
385 #if CHROMIUM_BUILD && !defined(NDEBUG)
386 [model addItem:[self viewSourceSwitchItem]
387 toSectionWithIdentifier:SectionIdentifierDebug];
388 [model addItem:[self logJavascriptConsoleSwitchItem]
389 toSectionWithIdentifier:SectionIdentifierDebug];
390 [model addItem:[self showAutofillTypePredictionsSwitchItem]
391 toSectionWithIdentifier:SectionIdentifierDebug];
392 [model addItem:[self materialCatalogDetailItem]
393 toSectionWithIdentifier:SectionIdentifierDebug];
394 #endif // CHROMIUM_BUILD && !defined(NDEBUG)
395 }
396
397 #pragma mark - Model Items
398
399 - (CollectionViewItem*)signInTextItem {
400 AccountSignInItem* signInTextItem = [[[AccountSignInItem alloc]
401 initWithType:ItemTypeSignInButton] autorelease];
402 signInTextItem.accessibilityIdentifier = kSettingsSignInCellId;
403 UIImage* image = CircularImageFromImage(ios::GetChromeBrowserProvider()
404 ->GetSigninResourcesProvider()
405 ->GetDefaultAvatar(),
406 kAccountProfilePhotoDimension);
407 signInTextItem.image = image;
408
409 return signInTextItem;
410 }
411
412 - (CollectionViewItem*)accountCellItem {
413 CollectionViewAccountItem* identityAccountItem =
414 [[[CollectionViewAccountItem alloc] initWithType:ItemTypeAccount]
415 autorelease];
416 identityAccountItem.accessoryType =
417 MDCCollectionViewCellAccessoryDisclosureIndicator;
418 identityAccountItem.accessibilityIdentifier = kSettingsAccountCellId;
419 [self updateIdentityAccountItem:identityAccountItem];
420 return identityAccountItem;
421 }
422
423 - (CollectionViewItem*)searchEngineDetailItem {
424 NSString* defaultSearchEngineName =
425 base::SysUTF16ToNSString(GetDefaultSearchEngineName(
426 ios::TemplateURLServiceFactory::GetForBrowserState(
427 _mainBrowserState)));
428
429 _defaultSearchEngineItem.reset(
430 [[self detailItemWithType:ItemTypeSearchEngine
431 text:l10n_util::GetNSString(
432 IDS_IOS_SEARCH_ENGINE_SETTING_TITLE)
433 detailText:defaultSearchEngineName] retain]);
434 _defaultSearchEngineItem.get().accessibilityIdentifier =
435 kSettingsSearchEngineCellId;
436 return _defaultSearchEngineItem;
437 }
438
439 - (CollectionViewItem*)savePasswordsDetailItem {
440 BOOL savePasswordsEnabled = _mainBrowserState->GetPrefs()->GetBoolean(
441 password_manager::prefs::kPasswordManagerSavingEnabled);
442 NSString* passwordsDetail = savePasswordsEnabled
443 ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
444 : l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
445 int titleId = IDS_IOS_SAVE_PASSWORDS;
446 syncer::SyncService* syncService =
447 IOSChromeProfileSyncServiceFactory::GetForBrowserState(_mainBrowserState);
448 if (password_bubble_experiment::IsSmartLockBrandingEnabled(syncService)) {
449 titleId = IDS_IOS_SAVE_PASSWORDS_SMART_LOCK;
450 }
451
452 _savePasswordsDetailItem.reset(
453 [[self detailItemWithType:ItemTypeSavedPasswords
454 text:l10n_util::GetNSString(titleId)
455 detailText:passwordsDetail] retain]);
456
457 return _savePasswordsDetailItem;
458 }
459
460 - (CollectionViewItem*)autoFillDetailItem {
461 BOOL autofillEnabled = _mainBrowserState->GetPrefs()->GetBoolean(
462 autofill::prefs::kAutofillEnabled);
463 NSString* autofillDetail = autofillEnabled
464 ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
465 : l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
466 _autoFillDetailItem.reset(
467 [[self detailItemWithType:ItemTypeAutofill
468 text:l10n_util::GetNSString(IDS_IOS_AUTOFILL)
469 detailText:autofillDetail] retain]);
470
471 return _autoFillDetailItem;
472 }
473
474 - (CollectionViewItem*)nativeAppsDetailItem {
475 return [self
476 detailItemWithType:ItemTypeNativeApps
477 text:l10n_util::GetNSString(IDS_IOS_GOOGLE_APPS_SM_SETTINGS)
478 detailText:nil];
479 }
480
481 - (CollectionViewItem*)voiceSearchDetailItem {
482 voice::SpeechInputLocaleConfig* localeConfig =
483 voice::SpeechInputLocaleConfig::GetInstance();
484 voice::SpeechInputLocale locale =
485 _voiceLocaleCode.GetValue().length()
486 ? localeConfig->GetLocaleForCode(_voiceLocaleCode.GetValue())
487 : localeConfig->GetDefaultLocale();
488 NSString* languageName = base::SysUTF16ToNSString(locale.display_name);
489 _voiceSearchDetailItem.reset(
490 [[self detailItemWithType:ItemTypeVoiceSearch
491 text:l10n_util::GetNSString(
492 IDS_IOS_VOICE_SEARCH_SETTING_TITLE)
493 detailText:languageName] retain]);
494 _voiceSearchDetailItem.get().accessibilityIdentifier =
495 kSettingsVoiceSearchCellId;
496 return _voiceSearchDetailItem;
497 }
498
499 - (CollectionViewItem*)privacyDetailItem {
500 return
501 [self detailItemWithType:ItemTypePrivacy
502 text:l10n_util::GetNSString(
503 IDS_OPTIONS_ADVANCED_SECTION_TITLE_PRIVACY)
504 detailText:nil];
505 }
506
507 - (CollectionViewItem*)contentSettingsDetailItem {
508 return [self
509 detailItemWithType:ItemTypeContentSettings
510 text:l10n_util::GetNSString(IDS_IOS_CONTENT_SETTINGS_TITLE)
511 detailText:nil];
512 }
513
514 - (CollectionViewItem*)bandwidthManagementDetailItem {
515 return [self detailItemWithType:ItemTypeBandwidth
516 text:l10n_util::GetNSString(
517 IDS_IOS_BANDWIDTH_MANAGEMENT_SETTINGS)
518 detailText:nil];
519 }
520
521 - (CollectionViewItem*)aboutChromeDetailItem {
522 return [self detailItemWithType:ItemTypeAboutChrome
523 text:l10n_util::GetNSString(IDS_IOS_PRODUCT_NAME)
524 detailText:nil];
525 }
526
527 - (CollectionViewSwitchItem*)showMemoryDebugSwitchItem {
528 CollectionViewSwitchItem* showMemoryDebugSwitchItem =
529 [self switchItemWithType:ItemTypeMemoryDebugging
530 title:@"Show memory debug tools"
531 withDefaultsKey:nil];
532 showMemoryDebugSwitchItem.on = [_showMemoryDebugToolsEnabled value];
533
534 return showMemoryDebugSwitchItem;
535 }
536 #if CHROMIUM_BUILD && !defined(NDEBUG)
537
538 - (CollectionViewSwitchItem*)viewSourceSwitchItem {
539 return [self switchItemWithType:ItemTypeViewSource
540 title:@"View source menu"
541 withDefaultsKey:kDevViewSourceKey];
542 }
543
544 - (CollectionViewSwitchItem*)logJavascriptConsoleSwitchItem {
545 return [self switchItemWithType:ItemTypeLogJavascript
546 title:@"Log JS"
547 withDefaultsKey:kLogJavascriptKey];
548 }
549
550 - (CollectionViewSwitchItem*)showAutofillTypePredictionsSwitchItem {
551 return [self switchItemWithType:ItemTypeShowAutofillTypePredictions
552 title:@"Show Autofill type predictions"
553 withDefaultsKey:kShowAutofillTypePredictionsKey];
554 }
555
556 - (CollectionViewDetailItem*)materialCatalogDetailItem {
557 return [self detailItemWithType:ItemTypeCellCatalog
558 text:@"Cell Catalog"
559 detailText:nil];
560 }
561 #endif // CHROMIUM_BUILD && !defined(NDEBUG)
562
563 #pragma mark Item Updaters
564
565 - (void)updateSearchCell {
566 NSString* defaultSearchEngineName =
567 base::SysUTF16ToNSString(GetDefaultSearchEngineName(
568 ios::TemplateURLServiceFactory::GetForBrowserState(
569 _mainBrowserState)));
570
571 _defaultSearchEngineItem.get().detailText = defaultSearchEngineName;
572 [self reconfigureCellsForItems:@[ _defaultSearchEngineItem ]
573 inSectionWithIdentifier:SectionIdentifierBasics];
574 }
575
576 #pragma mark Item Constructors
577
578 - (CollectionViewDetailItem*)detailItemWithType:(NSInteger)type
579 text:(NSString*)text
580 detailText:(NSString*)detailText {
581 CollectionViewDetailItem* detailItem =
582 [[[CollectionViewDetailItem alloc] initWithType:type] autorelease];
583 detailItem.text = text;
584 detailItem.detailText = detailText;
585 detailItem.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator;
586 detailItem.accessibilityTraits |= UIAccessibilityTraitButton;
587
588 return detailItem;
589 }
590
591 - (CollectionViewSwitchItem*)switchItemWithType:(NSInteger)type
592 title:(NSString*)title
593 withDefaultsKey:(NSString*)key {
594 CollectionViewSwitchItem* switchItem =
595 [[[CollectionViewSwitchItem alloc] initWithType:type] autorelease];
596 switchItem.text = title;
597 if (key) {
598 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
599 switchItem.on = [defaults boolForKey:key];
600 }
601
602 return switchItem;
603 }
604
605 #pragma mark - UICollectionViewDataSource
606
607 - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
608 cellForItemAtIndexPath:(NSIndexPath*)indexPath {
609 UICollectionViewCell* cell =
610 [super collectionView:collectionView cellForItemAtIndexPath:indexPath];
611 NSInteger itemType =
612 [self.collectionViewModel itemTypeForIndexPath:indexPath];
613
614 if ([cell isKindOfClass:[CollectionViewDetailCell class]]) {
615 CollectionViewDetailCell* detailCell =
616 base::mac::ObjCCastStrict<CollectionViewDetailCell>(cell);
617 if (itemType == ItemTypeSavedPasswords) {
618 scoped_refptr<password_manager::PasswordStore> passwordStore =
619 IOSChromePasswordStoreFactory::GetForBrowserState(
620 _mainBrowserState, ServiceAccessType::EXPLICIT_ACCESS);
621 if (!passwordStore) {
622 // The password store factory returns a NULL password store if something
623 // goes wrong during the password store initialization. Disable the save
624 // passwords cell in this case.
625 LOG(ERROR) << "Save passwords cell was disabled as the password store"
626 " cannot be created.";
627 [detailCell setUserInteractionEnabled:NO];
628 detailCell.textLabel.textColor = [[MDCPalette greyPalette] tint500];
629 detailCell.detailTextLabel.textColor =
630 [[MDCPalette greyPalette] tint400];
631 }
632 } else {
633 [detailCell setUserInteractionEnabled:YES];
634 detailCell.textLabel.textColor = [[MDCPalette greyPalette] tint900];
635 detailCell.detailTextLabel.textColor = [[MDCPalette greyPalette] tint500];
636 }
637 }
638
639 switch (itemType) {
640 case ItemTypeMemoryDebugging: {
641 CollectionViewSwitchCell* switchCell =
642 base::mac::ObjCCastStrict<CollectionViewSwitchCell>(cell);
643 [switchCell.switchView addTarget:self
644 action:@selector(memorySwitchToggled:)
645 forControlEvents:UIControlEventValueChanged];
646 break;
647 }
648 case ItemTypeViewSource: {
649 CollectionViewSwitchCell* switchCell =
650 base::mac::ObjCCastStrict<CollectionViewSwitchCell>(cell);
651 [switchCell.switchView addTarget:self
652 action:@selector(viewSourceSwitchToggled:)
653 forControlEvents:UIControlEventValueChanged];
654 break;
655 }
656 case ItemTypeLogJavascript: {
657 CollectionViewSwitchCell* switchCell =
658 base::mac::ObjCCastStrict<CollectionViewSwitchCell>(cell);
659 [switchCell.switchView addTarget:self
660 action:@selector(logJSSwitchToggled:)
661 forControlEvents:UIControlEventValueChanged];
662 break;
663 }
664 case ItemTypeShowAutofillTypePredictions: {
665 CollectionViewSwitchCell* switchCell =
666 base::mac::ObjCCastStrict<CollectionViewSwitchCell>(cell);
667 [switchCell.switchView addTarget:self
668 action:@selector(showAutoFillSwitchToggled:)
669 forControlEvents:UIControlEventValueChanged];
670 break;
671 }
672 default:
673 break;
674 }
675
676 return cell;
677 }
678
679 - (UICollectionReusableView*)collectionView:(UICollectionView*)collectionView
680 viewForSupplementaryElementOfKind:(NSString*)kind
681 atIndexPath:(NSIndexPath*)indexPath {
682 UICollectionReusableView* view = [super collectionView:collectionView
683 viewForSupplementaryElementOfKind:kind
684 atIndexPath:indexPath];
685
686 MDCCollectionViewTextCell* textCell =
687 base::mac::ObjCCast<MDCCollectionViewTextCell>(view);
688 if (textCell) {
689 textCell.textLabel.textColor = [[MDCPalette greyPalette] tint500];
690 }
691 return view;
692 }
693
694 #pragma mark UICollectionViewDelegate
695
696 - (void)collectionView:(UICollectionView*)collectionView
697 didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
698 [super collectionView:collectionView didSelectItemAtIndexPath:indexPath];
699
700 id object = [self.collectionViewModel itemAtIndexPath:indexPath];
701 if ([object respondsToSelector:@selector(isEnabled)] &&
702 ![object performSelector:@selector(isEnabled)]) {
703 // Don't perform any action if the cell isn't enabled.
704 return;
705 }
706
707 NSInteger itemType =
708 [self.collectionViewModel itemTypeForIndexPath:indexPath];
709
710 base::scoped_nsobject<UIViewController> controller;
711
712 switch (itemType) {
713 case ItemTypeSignInButton:
714 [self showSignIn];
715 break;
716 case ItemTypeAccount:
717 controller.reset([[AccountsCollectionViewController alloc]
718 initWithBrowserState:_mainBrowserState
719 closeSettingsOnAddAccount:NO]);
720 break;
721 case ItemTypeSearchEngine:
722 controller.reset([[SearchEngineSettingsCollectionViewController alloc]
723 initWithBrowserState:_mainBrowserState]);
724 break;
725 case ItemTypeSavedPasswords: {
726 controller.reset([[SavePasswordsCollectionViewController alloc]
727 initWithBrowserState:_mainBrowserState]);
728 break;
729 }
730 case ItemTypeAutofill:
731 controller.reset([[AutofillCollectionViewController alloc]
732 initWithBrowserState:_mainBrowserState]);
733 break;
734 case ItemTypeNativeApps:
735 controller.reset([[NativeAppsCollectionViewController alloc]
736 initWithURLRequestContextGetter:_currentBrowserState
737 ->GetRequestContext()]);
738 break;
739 case ItemTypeVoiceSearch:
740 controller.reset([[VoicesearchCollectionViewController alloc]
741 initWithPrefs:_mainBrowserState->GetPrefs()]);
742 break;
743 case ItemTypePrivacy:
744 controller.reset([[PrivacyCollectionViewController alloc]
745 initWithBrowserState:_mainBrowserState]);
746 break;
747 case ItemTypeContentSettings:
748 controller.reset([[ContentSettingsCollectionViewController alloc]
749 initWithBrowserState:_mainBrowserState]);
750 break;
751 case ItemTypeBandwidth:
752 controller.reset([[BandwidthManagementCollectionViewController alloc]
753 initWithBrowserState:_mainBrowserState]);
754 break;
755 case ItemTypeAboutChrome:
756 controller.reset([[AboutChromeCollectionViewController alloc] init]);
757 break;
758 case ItemTypeMemoryDebugging:
759 case ItemTypeViewSource:
760 case ItemTypeLogJavascript:
761 case ItemTypeShowAutofillTypePredictions:
762 // Taps on these don't do anything. They have a switch as accessory view
763 // and only the switch is tappable.
764 break;
765 case ItemTypeCellCatalog:
766 controller.reset([[MaterialCellCatalogViewController alloc] init]);
767 break;
768 default:
769 break;
770 }
771
772 if (controller) {
773 [self.navigationController pushViewController:controller animated:YES];
774 }
775 }
776
777 #pragma mark MDCCollectionViewStylingDelegate
778
779 - (CGFloat)collectionView:(UICollectionView*)collectionView
780 cellHeightAtIndexPath:(NSIndexPath*)indexPath {
781 CollectionViewItem* item =
782 [self.collectionViewModel itemAtIndexPath:indexPath];
783
784 if (item.type == ItemTypeAccount) {
785 return MDCCellDefaultTwoLineHeight;
786 }
787
788 if (item.type == ItemTypeSignInButton) {
789 return MDCCellDefaultThreeLineHeight;
790 }
791
792 return MDCCellDefaultOneLineHeight;
793 }
794
795 - (BOOL)collectionView:(UICollectionView*)collectionView
796 hidesInkViewAtIndexPath:(NSIndexPath*)indexPath {
797 NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath];
798 switch (type) {
799 case ItemTypeMemoryDebugging:
800 case ItemTypeViewSource:
801 case ItemTypeLogJavascript:
802 case ItemTypeShowAutofillTypePredictions:
803 return YES;
804 default:
805 return NO;
806 }
807 }
808
809 #pragma mark Switch Actions
810
811 - (void)memorySwitchToggled:(UISwitch*)sender {
812 NSIndexPath* switchPath =
813 [self.collectionViewModel indexPathForItemType:ItemTypeMemoryDebugging
814 sectionIdentifier:SectionIdentifierDebug];
815
816 CollectionViewSwitchItem* switchItem =
817 base::mac::ObjCCastStrict<CollectionViewSwitchItem>(
818 [self.collectionViewModel itemAtIndexPath:switchPath]);
819
820 BOOL newSwitchValue = sender.isOn;
821 switchItem.on = newSwitchValue;
822 [_showMemoryDebugToolsEnabled setValue:newSwitchValue];
823 }
824
825 #if CHROMIUM_BUILD && !defined(NDEBUG)
826 - (void)viewSourceSwitchToggled:(UISwitch*)sender {
827 NSIndexPath* switchPath =
828 [self.collectionViewModel indexPathForItemType:ItemTypeViewSource
829 sectionIdentifier:SectionIdentifierDebug];
830
831 CollectionViewSwitchItem* switchItem =
832 base::mac::ObjCCastStrict<CollectionViewSwitchItem>(
833 [self.collectionViewModel itemAtIndexPath:switchPath]);
834
835 BOOL newSwitchValue = sender.isOn;
836 switchItem.on = newSwitchValue;
837 [self setBooleanNSUserDefaultsValue:newSwitchValue forKey:kDevViewSourceKey];
838 }
839
840 - (void)logJSSwitchToggled:(UISwitch*)sender {
841 NSIndexPath* switchPath =
842 [self.collectionViewModel indexPathForItemType:ItemTypeLogJavascript
843 sectionIdentifier:SectionIdentifierDebug];
844
845 CollectionViewSwitchItem* switchItem =
846 base::mac::ObjCCastStrict<CollectionViewSwitchItem>(
847 [self.collectionViewModel itemAtIndexPath:switchPath]);
848
849 BOOL newSwitchValue = sender.isOn;
850 switchItem.on = newSwitchValue;
851 [self setBooleanNSUserDefaultsValue:newSwitchValue forKey:kLogJavascriptKey];
852 }
853
854 - (void)showAutoFillSwitchToggled:(UISwitch*)sender {
855 NSIndexPath* switchPath = [self.collectionViewModel
856 indexPathForItemType:ItemTypeShowAutofillTypePredictions
857 sectionIdentifier:SectionIdentifierDebug];
858
859 CollectionViewSwitchItem* switchItem =
860 base::mac::ObjCCastStrict<CollectionViewSwitchItem>(
861 [self.collectionViewModel itemAtIndexPath:switchPath]);
862
863 BOOL newSwitchValue = sender.isOn;
864 switchItem.on = newSwitchValue;
865 [self setBooleanNSUserDefaultsValue:newSwitchValue
866 forKey:kShowAutofillTypePredictionsKey];
867 }
868 #endif // CHROMIUM_BUILD && !defined(NDEBUG)
869
870 #pragma mark Private methods
871
872 // Sets the NSUserDefaults BOOL |value| for |key|.
873 - (void)setBooleanNSUserDefaultsValue:(BOOL)value forKey:(NSString*)key {
874 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
875 [defaults setBool:value forKey:key];
876 [defaults synchronize];
877 }
878
879 // Returns YES if a "Debug" section should be shown. This is always true for
880 // Chromium builds, but for official builds it is gated by an experimental flag
881 // because the "Debug" section should never be showing in stable channel.
882 - (BOOL)hasDebugSection {
883 #if CHROMIUM_BUILD && !defined(NDEBUG)
884 return YES;
885 #else
886 if (experimental_flags::IsMemoryDebuggingEnabled()) {
887 return YES;
888 }
889 return NO;
890 #endif // CHROMIUM_BUILD && !defined(NDEBUG)
891 }
892
893 // Updates the identity cell.
894 - (void)updateIdentityAccountItem:
895 (CollectionViewAccountItem*)identityAccountItem {
896 AuthenticationService* authService =
897 AuthenticationServiceFactory::GetForBrowserState(_mainBrowserState);
898 _identity.reset([authService->GetAuthenticatedIdentity() retain]);
899 if (!_identity) {
900 // This could occur during the sign out process. Just ignore as the account
901 // cell will be replaced by the "Sign in" button.
902 return;
903 }
904 identityAccountItem.image = [self userAccountImage];
905
906 SyncSetupService* syncSetupService =
907 SyncSetupServiceFactory::GetForBrowserState(_mainBrowserState);
908
909 if (!syncSetupService->HasFinishedInitialSetup()) {
910 identityAccountItem.detailText =
911 l10n_util::GetNSString(IDS_IOS_SYNC_SETUP_IN_PROGRESS);
912 identityAccountItem.shouldDisplayError = NO;
913 return;
914 }
915 identityAccountItem.shouldDisplayError =
916 !ios_internal::sync::IsTransientSyncError(
917 syncSetupService->GetSyncServiceState());
918 identityAccountItem.text = [_identity userFullName];
919 if (identityAccountItem.shouldDisplayError) {
920 identityAccountItem.detailText =
921 ios_internal::sync::GetSyncErrorDescriptionForBrowserState(
922 _mainBrowserState);
923 } else {
924 identityAccountItem.detailText =
925 syncSetupService->IsSyncEnabled()
926 ? l10n_util::GetNSStringF(
927 IDS_IOS_SIGN_IN_TO_CHROME_SETTING_SYNCING,
928 base::SysNSStringToUTF16([_identity userEmail]))
929 : l10n_util::GetNSString(
930 IDS_IOS_SIGN_IN_TO_CHROME_SETTING_SYNC_OFF);
931 }
932 }
933
934 - (void)reloadAccountCell {
935 if (![self.collectionViewModel hasItemForItemType:ItemTypeAccount
936 sectionIdentifier:SectionIdentifierSignIn]) {
937 return;
938 }
939 NSIndexPath* accountCellIndexPath =
940 [self.collectionViewModel indexPathForItemType:ItemTypeAccount
941 sectionIdentifier:SectionIdentifierSignIn];
942 CollectionViewAccountItem* identityAccountItem =
943 base::mac::ObjCCast<CollectionViewAccountItem>(
944 [self.collectionViewModel itemAtIndexPath:accountCellIndexPath]);
945 if (identityAccountItem) {
946 [self updateIdentityAccountItem:identityAccountItem];
947 [self reconfigureCellsForItems:@[ identityAccountItem ]
948 inSectionWithIdentifier:SectionIdentifierSignIn];
949 }
950 }
951
952 #pragma mark Sign in
953
954 - (void)showSignIn {
955 base::RecordAction(base::UserMetricsAction("Signin_Signin_FromSettings"));
956 DCHECK(!_signinInteractionController);
957 _signinInteractionController.reset([[SigninInteractionController alloc]
958 initWithBrowserState:_mainBrowserState
959 presentingViewController:self.navigationController
960 isPresentedOnSettings:YES
961 signInAccessPoint:signin_metrics::AccessPoint::
962 ACCESS_POINT_SETTINGS]);
963
964 base::WeakNSObject<SettingsCollectionViewController> weakSelf(self);
965 [_signinInteractionController signInWithCompletion:^(BOOL success) {
966 [weakSelf didFinishSignin:success];
967 }
968 viewController:self];
969 }
970
971 - (void)didFinishSignin:(BOOL)signedIn {
972 _signinInteractionController.reset();
973 }
974
975 #pragma mark NotificationBridgeDelegate
976
977 - (void)onSignInStateChanged {
978 // Sign in state changes are rare. Just reload the entire collection when this
979 // happens.
980 [self reloadData];
981 }
982
983 #pragma mark SettingsControllerProtocol
984
985 - (void)settingsWillBeDismissed {
986 [_signinInteractionController cancel];
987 [self stopBrowserStateServiceObservers];
988 }
989
990 #pragma mark SyncObserverModelBridge
991
992 - (void)onSyncStateChanged {
993 [self reloadAccountCell];
994 }
995
996 #pragma mark - IdentityRefreshLogic
997
998 // Image used for loggedin user account that supports caching.
999 - (UIImage*)userAccountImage {
1000 UIImage* image = ios::GetChromeBrowserProvider()
1001 ->GetChromeIdentityService()
1002 ->GetCachedAvatarForIdentity(_identity);
1003 if (!image) {
1004 image = ios::GetChromeBrowserProvider()
1005 ->GetSigninResourcesProvider()
1006 ->GetDefaultAvatar();
1007 // No cached image, trigger a fetch, which will notify all observers
1008 // (including the corresponding AccountViewBase).
1009 ios::GetChromeBrowserProvider()
1010 ->GetChromeIdentityService()
1011 ->GetAvatarForIdentity(_identity, ^(UIImage*){
1012 });
1013 }
1014
1015 // If the currently used image has already been resized, use it.
1016 if (_resizedImage && _oldImage.get() == image)
1017 return _resizedImage;
1018
1019 _oldImage.reset(image);
1020
1021 // Resize the profile image.
1022 CGFloat dimension = kAccountProfilePhotoDimension;
1023 if (image.size.width != dimension || image.size.height != dimension) {
1024 image = ResizeImage(image, CGSizeMake(dimension, dimension),
1025 ProjectionMode::kAspectFit);
1026 }
1027 _resizedImage.reset([image retain]);
1028 return _resizedImage;
1029 }
1030
1031 #pragma mark ChromeIdentityServiceObserver
1032
1033 - (void)onProfileUpdate:(ChromeIdentity*)identity {
1034 if (identity == _identity) {
1035 [self reloadAccountCell];
1036 }
1037 }
1038
1039 - (void)onChromeIdentityServiceWillBeDestroyed {
1040 _identityServiceObserver.reset();
1041 }
1042
1043 #pragma mark - BooleanObserver
1044
1045 - (void)booleanDidChange:(id<ObservableBoolean>)observableBoolean {
1046 DCHECK_EQ(observableBoolean, _showMemoryDebugToolsEnabled.get());
1047 // Update the Item.
1048 _showMemoryDebugToolsItem.get().on = [_showMemoryDebugToolsEnabled value];
1049
1050 // Update the Cell.
1051 [self reconfigureCellsForItems:@[ _showMemoryDebugToolsItem ]
1052 inSectionWithIdentifier:SectionIdentifierDebug];
1053 }
1054
1055 #pragma mark - PrefObserverDelegate
1056
1057 - (void)onPreferenceChanged:(const std::string&)preferenceName {
1058 if (preferenceName == prefs::kVoiceSearchLocale) {
1059 voice::SpeechInputLocaleConfig* localeConfig =
1060 voice::SpeechInputLocaleConfig::GetInstance();
1061 voice::SpeechInputLocale locale =
1062 _voiceLocaleCode.GetValue().length()
1063 ? localeConfig->GetLocaleForCode(_voiceLocaleCode.GetValue())
1064 : localeConfig->GetDefaultLocale();
1065 NSString* languageName = base::SysUTF16ToNSString(locale.display_name);
1066 _voiceSearchDetailItem.get().detailText = languageName;
1067 [self reconfigureCellsForItems:@[ _voiceSearchDetailItem ]
1068 inSectionWithIdentifier:SectionIdentifierAdvanced];
1069 }
1070
1071 if (preferenceName ==
1072 password_manager::prefs::kPasswordManagerSavingEnabled) {
1073 BOOL savePasswordsEnabled =
1074 _mainBrowserState->GetPrefs()->GetBoolean(preferenceName);
1075 NSString* passwordsDetail =
1076 savePasswordsEnabled ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
1077 : l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
1078
1079 _savePasswordsDetailItem.get().detailText = passwordsDetail;
1080 [self reconfigureCellsForItems:@[ _savePasswordsDetailItem ]
1081 inSectionWithIdentifier:SectionIdentifierBasics];
1082 }
1083
1084 if (preferenceName == autofill::prefs::kAutofillEnabled) {
1085 BOOL autofillEnabled =
1086 _mainBrowserState->GetPrefs()->GetBoolean(preferenceName);
1087 NSString* autofillDetail =
1088 autofillEnabled ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
1089 : l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
1090 _autoFillDetailItem.get().detailText = autofillDetail;
1091 [self reconfigureCellsForItems:@[ _autoFillDetailItem ]
1092 inSectionWithIdentifier:SectionIdentifierBasics];
1093 }
1094 }
1095
1096 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698