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

Side by Side Diff: ios/chrome/browser/ui/settings/sync_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/sync_settings_collection_view_controller .h"
6
7 #include <memory>
8
9 #include "base/auto_reset.h"
10 #include "base/mac/foundation_util.h"
11 #include "base/mac/scoped_nsobject.h"
12 #include "components/browser_sync/profile_sync_service.h"
13 #include "components/google/core/browser/google_util.h"
14 #include "components/signin/core/browser/account_tracker_service.h"
15 #include "components/signin/core/browser/profile_oauth2_token_service.h"
16 #include "components/signin/ios/browser/oauth2_token_service_observer_bridge.h"
17 #include "components/strings/grit/components_strings.h"
18 #include "components/sync/base/model_type.h"
19 #include "ios/chrome/browser/application_context.h"
20 #include "ios/chrome/browser/chrome_url_constants.h"
21 #include "ios/chrome/browser/experimental_flags.h"
22 #include "ios/chrome/browser/signin/account_tracker_service_factory.h"
23 #import "ios/chrome/browser/signin/authentication_service.h"
24 #import "ios/chrome/browser/signin/authentication_service_factory.h"
25 #include "ios/chrome/browser/signin/chrome_identity_service_observer_bridge.h"
26 #include "ios/chrome/browser/signin/oauth2_token_service_factory.h"
27 #include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h"
28 #include "ios/chrome/browser/sync/sync_setup_service.h"
29 #include "ios/chrome/browser/sync/sync_setup_service_factory.h"
30 #import "ios/chrome/browser/ui/authentication/authentication_flow.h"
31 #import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrom e.h"
32 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_account_ite m.h"
33 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_detail_item .h"
34 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h "
35 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
36 #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
37 #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
38 #include "ios/chrome/browser/ui/commands/ios_command_ids.h"
39 #import "ios/chrome/browser/ui/commands/open_url_command.h"
40 #import "ios/chrome/browser/ui/settings/cells/sync_switch_item.h"
41 #import "ios/chrome/browser/ui/settings/cells/text_and_error_item.h"
42 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
43 #import "ios/chrome/browser/ui/settings/sync_encryption_collection_view_controll er.h"
44 #import "ios/chrome/browser/ui/settings/sync_encryption_passphrase_collection_vi ew_controller.h"
45 #import "ios/chrome/browser/ui/settings/utils/resized_avatar_cache.h"
46 #import "ios/chrome/browser/ui/sync/sync_util.h"
47 #import "ios/chrome/browser/ui/uikit_ui_util.h"
48 #include "ios/chrome/grit/ios_strings.h"
49 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
50 #import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
51 #import "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
52 #include "ui/base/l10n/l10n_util_mac.h"
53 #include "url/gurl.h"
54
55 // The a11y identifier of the view controller's view.
56 NSString* const kSettingsSyncId = @"kSettingsSyncId";
57 // Notification when a switch account operation will start.
58 NSString* const kSwitchAccountWillStartNotification =
59 @"kSwitchAccountWillStartNotification";
60 // Notification when a switch account operation did finish.
61 NSString* const kSwitchAccountDidFinishNotification =
62 @"kSwitchAccountDidFinishNotification";
63 // Used to tag and retrieve ItemTypeSyncableDataType cell tags.
64 const NSInteger kTagShift = 1000;
65
66 namespace {
67
68 typedef NS_ENUM(NSInteger, SectionIdentifier) {
69 SectionIdentifierSyncError = kSectionIdentifierEnumZero,
70 SectionIdentifierEnableSync,
71 SectionIdentifierSyncAccounts,
72 SectionIdentifierSyncServices,
73 SectionIdentifierEncryptionAndFooter,
74 };
75
76 typedef NS_ENUM(NSInteger, ItemType) {
77 ItemTypeSyncError = kItemTypeEnumZero,
78 ItemTypeSyncSwitch,
79 ItemTypeAccount,
80 ItemTypeSyncEverything,
81 ItemTypeSyncableDataType,
82 ItemTypeEncryption,
83 ItemTypeManageSyncedData,
84 ItemTypeHeader,
85 };
86
87 } // namespace
88
89 @interface SyncSettingsCollectionViewController ()<
90 ChromeIdentityServiceObserver,
91 OAuth2TokenServiceObserverBridgeDelegate,
92 SettingsControllerProtocol,
93 SyncObserverModelBridge> {
94 ios::ChromeBrowserState* _browserState; // Weak.
95 SyncSetupService* _syncSetupService; // Weak.
96 std::unique_ptr<SyncObserverBridge> _syncObserver;
97 std::unique_ptr<OAuth2TokenServiceObserverBridge> _tokenServiceObserver;
98 base::scoped_nsobject<AuthenticationFlow> _authenticationFlow;
99 // Whether switching sync account is allowed on the screen.
100 BOOL _allowSwitchSyncAccount;
101 // Whether an authentication operation is in progress (e.g switch accounts).
102 BOOL _authenticationOperationInProgress;
103 // Whether Sync State changes should be currently ignored.
104 BOOL _ignoreSyncStateChanges;
105
106 // Cache for Identity items avatar images.
107 base::scoped_nsobject<ResizedAvatarCache> _avatarCache;
108 std::unique_ptr<ChromeIdentityServiceObserverBridge> _identityServiceObserver;
109 // Enable lookup of item corresponding to a given identity GAIA ID string.
110 base::scoped_nsobject<NSDictionary<NSString*, CollectionViewItem*>>
111 _identityMap;
112 }
113
114 // Stops observing browser state services.
115 - (void)stopBrowserStateServiceObservers;
116 // Pops the view if user is signed out and not authentication operation is in
117 // progress. Returns YES if the view was popped.
118 - (BOOL)popViewIfSignedOut;
119
120 // Returns a switch item for sync, set to on if |isOn| is YES.
121 - (CollectionViewItem*)syncSwitchItem:(BOOL)isOn;
122 // Returns an item for sync errors other than sync encryption.
123 - (CollectionViewItem*)syncErrorItem;
124 // Returns a switch item for sync everything, set to on if |isOn| is YES.
125 - (CollectionViewItem*)syncEverythingSwitchItem:(BOOL)isOn;
126 // Returns a switch item for the syncable data type |dataType|, set to on if
127 // |IsDataTypeEnabled| for that type returns true.
128 - (CollectionViewItem*)switchItemForDataType:
129 (SyncSetupService::SyncableDatatype)dataType;
130 // Returns an item for Encryption.
131 - (CollectionViewItem*)encryptionCellItem;
132 // Returns an item to open a link to manage the synced data.
133 - (CollectionViewItem*)manageSyncedDataItem;
134
135 // Action method for sync switch.
136 - (void)changeSyncStatusToOn:(UISwitch*)sender;
137 // Action method for the sync error cell.
138 - (void)fixSyncErrorIfPossible;
139 // Action method for the account cells.
140 - (void)startSwitchAccountForIdentity:(ChromeIdentity*)identity
141 postSignInAction:(PostSignInAction)postSigninAction;
142 // Callback for switch account action method.
143 - (void)didSwitchAccountWithSuccess:(BOOL)success;
144 // Action method for the Sync Everything switch.
145 - (void)changeSyncEverythingStatusToOn:(UISwitch*)sender;
146 // Action method for the data type switches.
147 - (void)changeDataTypeSyncStatusToOn:(UISwitch*)sender;
148 // Action method for the encryption cell.
149 - (void)showEncryption;
150
151 // Updates the visual status of the screen (i.e. whether cells are enabled,
152 // whether errors are displayed, ...).
153 - (void)updateCollectionView;
154 // Ensures the Sync error cell is shown when there is an error.
155 - (void)updateSyncError;
156 // Ensures the encryption cell displays an error if needed.
157 - (void)updateEncryptionCell;
158 // Updates the account item so it can reflect the latest state of the identity.
159 - (void)updateAccountItem:(CollectionViewAccountItem*)item
160 withIdentity:(ChromeIdentity*)identity;
161
162 // Returns whether the Sync Settings screen has an Accounts section, allowing
163 // users to choose to which account they want to sync to.
164 - (BOOL)hasAccountsSection;
165 // Returns whether a sync error cell should be displayed.
166 - (BOOL)shouldDisplaySyncError;
167 // Returns whether the Sync settings should be disabled because of a Sync error.
168 - (BOOL)shouldDisableSettingsOnSyncError;
169 // Returns whether an error should be displayed on the encryption cell.
170 - (BOOL)shouldDisplayEncryptionError;
171 // Returns whether the existing sync error is fixable by a user action.
172 - (BOOL)isSyncErrorFixableByUserAction;
173 // Returns the ID to use to access the l10n string for the given data type.
174 - (int)titleIdForSyncableDataType:(SyncSetupService::SyncableDatatype)datatype;
175 // Returns whether the encryption item should be enabled.
176 - (BOOL)shouldEncryptionItemBeEnabled;
177 // Returns whether the sync everything item should be enabled.
178 - (BOOL)shouldSyncEverythingItemBeEnabled;
179 // Returns whether the data type items should be enabled.
180 - (BOOL)shouldSyncableItemsBeEnabled;
181 // Returns the tag for a data type switch based on its index path.
182 - (NSInteger)tagForIndexPath:(NSIndexPath*)indexPath;
183 // Returns the indexPath for a data type switch based on its tag.
184 - (NSIndexPath*)indexPathForTag:(NSInteger)shiftedTag;
185
186 @end
187
188 @implementation SyncSettingsCollectionViewController
189
190 #pragma mark Initialization
191
192 - (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
193 allowSwitchSyncAccount:(BOOL)allowSwitchSyncAccount {
194 DCHECK(browserState);
195 self = [super initWithStyle:CollectionViewControllerStyleAppBar];
196 if (self) {
197 _allowSwitchSyncAccount = allowSwitchSyncAccount;
198 _browserState = browserState;
199 _syncSetupService =
200 SyncSetupServiceFactory::GetForBrowserState(_browserState);
201 self.title = l10n_util::GetNSString(IDS_IOS_SYNC_SETTING_TITLE);
202 browser_sync::ProfileSyncService* syncService =
203 IOSChromeProfileSyncServiceFactory::GetForBrowserState(_browserState);
204 _syncObserver.reset(new SyncObserverBridge(self, syncService));
205 _tokenServiceObserver.reset(new OAuth2TokenServiceObserverBridge(
206 OAuth2TokenServiceFactory::GetForBrowserState(_browserState), self));
207 self.collectionViewAccessibilityIdentifier = kSettingsSyncId;
208 _avatarCache.reset([[ResizedAvatarCache alloc] init]);
209 _identityServiceObserver.reset(
210 new ChromeIdentityServiceObserverBridge(self));
211 [self loadModel];
212 }
213 return self;
214 }
215
216 - (void)stopBrowserStateServiceObservers {
217 _syncObserver.reset();
218 _tokenServiceObserver.reset();
219 _identityServiceObserver.reset();
220 }
221
222 - (BOOL)popViewIfSignedOut {
223 if (AuthenticationServiceFactory::GetForBrowserState(_browserState)
224 ->IsAuthenticated()) {
225 return NO;
226 }
227 if (_authenticationOperationInProgress) {
228 // The signed out state might be temporary (e.g. account switch, ...).
229 // Don't pop this view based on intermediary values.
230 return NO;
231 }
232 [self.navigationController popToViewController:self animated:NO];
233 [base::mac::ObjCCastStrict<SettingsNavigationController>(
234 self.navigationController) popViewControllerOrCloseSettingsAnimated:NO];
235 return YES;
236 }
237
238 #pragma mark View lifecycle
239
240 - (void)viewWillAppear:(BOOL)animated {
241 [super viewWillAppear:animated];
242 [self updateEncryptionCell];
243 }
244
245 #pragma mark SettingsRootCollectionViewController
246
247 - (void)loadModel {
248 [super loadModel];
249 CollectionViewModel* model = self.collectionViewModel;
250
251 // SyncError section.
252 if ([self shouldDisplaySyncError]) {
253 [model addSectionWithIdentifier:SectionIdentifierSyncError];
254 [model addItem:[self syncErrorItem]
255 toSectionWithIdentifier:SectionIdentifierSyncError];
256 }
257
258 // Sync Section.
259 BOOL syncEnabled = _syncSetupService->IsSyncEnabled();
260 [model addSectionWithIdentifier:SectionIdentifierEnableSync];
261 [model addItem:[self syncSwitchItem:syncEnabled]
262 toSectionWithIdentifier:SectionIdentifierEnableSync];
263
264 // Sync to Section.
265 if ([self hasAccountsSection]) {
266 NSMutableDictionary<NSString*, CollectionViewItem*>* mutableIdentityMap =
267 [[[NSMutableDictionary alloc] init] autorelease];
268 // Accounts section. Cells enabled if sync is on.
269 [model addSectionWithIdentifier:SectionIdentifierSyncAccounts];
270 CollectionViewTextItem* syncToHeader = [[[CollectionViewTextItem alloc]
271 initWithType:ItemTypeHeader] autorelease];
272 syncToHeader.text = l10n_util::GetNSString(IDS_IOS_SYNC_TO_TITLE);
273 [model setHeader:syncToHeader
274 forSectionWithIdentifier:SectionIdentifierSyncAccounts];
275 ProfileOAuth2TokenService* oauth2_service =
276 OAuth2TokenServiceFactory::GetForBrowserState(_browserState);
277 AccountTrackerService* accountTracker =
278 ios::AccountTrackerServiceFactory::GetForBrowserState(_browserState);
279
280 for (const std::string& account_id : oauth2_service->GetAccounts()) {
281 AccountInfo account = accountTracker->GetAccountInfo(account_id);
282 ChromeIdentity* identity = ios::GetChromeBrowserProvider()
283 ->GetChromeIdentityService()
284 ->GetIdentityWithGaiaID(account.gaia);
285 CollectionViewItem* accountItem = [self accountItem:identity];
286 [model addItem:accountItem
287 toSectionWithIdentifier:SectionIdentifierSyncAccounts];
288 [mutableIdentityMap setObject:accountItem forKey:identity.gaiaID];
289 }
290 _identityMap.reset([mutableIdentityMap retain]);
291 }
292
293 // Data Types to sync. Enabled if sync is on.
294 [model addSectionWithIdentifier:SectionIdentifierSyncServices];
295 CollectionViewTextItem* syncServicesHeader = [
296 [[CollectionViewTextItem alloc] initWithType:ItemTypeHeader] autorelease];
297 syncServicesHeader.text =
298 l10n_util::GetNSString(IDS_IOS_SYNC_DATA_TYPES_TITLE);
299 [model setHeader:syncServicesHeader
300 forSectionWithIdentifier:SectionIdentifierSyncServices];
301 BOOL syncEverythingEnabled = _syncSetupService->IsSyncingAllDataTypes();
302 [model addItem:[self syncEverythingSwitchItem:syncEverythingEnabled]
303 toSectionWithIdentifier:SectionIdentifierSyncServices];
304 // Specific Data Types to sync. Enabled if Sync Everything is off.
305 for (int i = 0; i < SyncSetupService::kNumberOfSyncableDatatypes; ++i) {
306 SyncSetupService::SyncableDatatype dataType =
307 static_cast<SyncSetupService::SyncableDatatype>(i);
308 if (!experimental_flags::IsReadingListEnabled() &&
309 dataType == SyncSetupService::kSyncReadingList) {
310 // Display Reading List only if it is enabled.
311 continue;
312 }
313 [model addItem:[self switchItemForDataType:dataType]
314 toSectionWithIdentifier:SectionIdentifierSyncServices];
315 }
316
317 // Encryption section. Enabled if sync is on.
318 [model addSectionWithIdentifier:SectionIdentifierEncryptionAndFooter];
319 [model addItem:[self encryptionCellItem]
320 toSectionWithIdentifier:SectionIdentifierEncryptionAndFooter];
321 [model addItem:[self manageSyncedDataItem]
322 toSectionWithIdentifier:SectionIdentifierEncryptionAndFooter];
323 }
324
325 #pragma mark - Model items
326
327 - (CollectionViewItem*)syncSwitchItem:(BOOL)isOn {
328 SyncSwitchItem* syncSwitchItem = [self
329 switchItemWithType:ItemTypeSyncSwitch
330 title:l10n_util::GetNSString(IDS_IOS_SYNC_SETTING_TITLE)
331 subTitle:l10n_util::GetNSString(
332 IDS_IOS_SIGN_IN_TO_CHROME_SETTING_SUBTITLE)];
333 syncSwitchItem.on = isOn;
334 return syncSwitchItem;
335 }
336
337 - (CollectionViewItem*)syncErrorItem {
338 DCHECK([self shouldDisplaySyncError]);
339 CollectionViewAccountItem* syncErrorItem = [[[CollectionViewAccountItem alloc]
340 initWithType:ItemTypeSyncError] autorelease];
341 syncErrorItem.text = l10n_util::GetNSString(IDS_IOS_SYNC_ERROR_TITLE);
342 syncErrorItem.image = [UIImage imageNamed:@"settings_error"];
343 syncErrorItem.detailText =
344 ios_internal::sync::GetSyncErrorMessageForBrowserState(_browserState);
345 return syncErrorItem;
346 }
347
348 - (CollectionViewItem*)accountItem:(ChromeIdentity*)identity {
349 CollectionViewAccountItem* identityAccountItem =
350 [[[CollectionViewAccountItem alloc] initWithType:ItemTypeAccount]
351 autorelease];
352 [self updateAccountItem:identityAccountItem withIdentity:identity];
353
354 identityAccountItem.enabled = _syncSetupService->IsSyncEnabled();
355 ChromeIdentity* authenticatedIdentity =
356 AuthenticationServiceFactory::GetForBrowserState(_browserState)
357 ->GetAuthenticatedIdentity();
358 if (identity == authenticatedIdentity) {
359 identityAccountItem.accessoryType = MDCCollectionViewCellAccessoryCheckmark;
360 }
361 return identityAccountItem;
362 }
363
364 - (CollectionViewItem*)syncEverythingSwitchItem:(BOOL)isOn {
365 SyncSwitchItem* syncSwitchItem = [self
366 switchItemWithType:ItemTypeSyncEverything
367 title:l10n_util::GetNSString(IDS_IOS_SYNC_EVERYTHING_TITLE)
368 subTitle:nil];
369 syncSwitchItem.on = isOn;
370 syncSwitchItem.enabled = [self shouldSyncEverythingItemBeEnabled];
371 return syncSwitchItem;
372 }
373
374 - (CollectionViewItem*)switchItemForDataType:
375 (SyncSetupService::SyncableDatatype)dataType {
376 syncer::ModelType modelType = _syncSetupService->GetModelType(dataType);
377 BOOL isOn = _syncSetupService->IsDataTypeEnabled(modelType);
378
379 SyncSwitchItem* syncDataTypeItem =
380 [self switchItemWithType:ItemTypeSyncableDataType
381 title:l10n_util::GetNSString(
382 [self titleIdForSyncableDataType:dataType])
383 subTitle:nil];
384 syncDataTypeItem.dataType = dataType;
385 syncDataTypeItem.on = isOn;
386 syncDataTypeItem.enabled = [self shouldSyncableItemsBeEnabled];
387 return syncDataTypeItem;
388 }
389
390 - (CollectionViewItem*)encryptionCellItem {
391 TextAndErrorItem* encryptionCellItem =
392 [[[TextAndErrorItem alloc] initWithType:ItemTypeEncryption] autorelease];
393 encryptionCellItem.text =
394 l10n_util::GetNSString(IDS_IOS_SYNC_ENCRYPTION_TITLE);
395 encryptionCellItem.accessoryType =
396 MDCCollectionViewCellAccessoryDisclosureIndicator;
397 encryptionCellItem.shouldDisplayError = [self shouldDisplayEncryptionError];
398 encryptionCellItem.enabled = [self shouldEncryptionItemBeEnabled];
399 return encryptionCellItem;
400 }
401
402 - (CollectionViewItem*)manageSyncedDataItem {
403 CollectionViewTextItem* manageSyncedDataItem =
404 [[[CollectionViewTextItem alloc] initWithType:ItemTypeManageSyncedData]
405 autorelease];
406 manageSyncedDataItem.text =
407 l10n_util::GetNSString(IDS_IOS_SYNC_RESET_GOOGLE_DASHBOARD_NO_LINK);
408 manageSyncedDataItem.accessibilityTraits |= UIAccessibilityTraitButton;
409 return manageSyncedDataItem;
410 }
411
412 #pragma mark Item Constructors
413
414 - (SyncSwitchItem*)switchItemWithType:(NSInteger)type
415 title:(NSString*)title
416 subTitle:(NSString*)detailText {
417 SyncSwitchItem* switchItem =
418 [[[SyncSwitchItem alloc] initWithType:type] autorelease];
419 switchItem.text = title;
420 switchItem.detailText = detailText;
421 return switchItem;
422 }
423
424 #pragma mark - UICollectionViewDataSource
425
426 - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
427 cellForItemAtIndexPath:(NSIndexPath*)indexPath {
428 UICollectionViewCell* cell =
429 [super collectionView:collectionView cellForItemAtIndexPath:indexPath];
430 NSInteger itemType =
431 [self.collectionViewModel itemTypeForIndexPath:indexPath];
432
433 switch (itemType) {
434 case ItemTypeSyncError: {
435 CollectionViewAccountCell* accountCell =
436 base::mac::ObjCCastStrict<CollectionViewAccountCell>(cell);
437 accountCell.textLabel.textColor = [[MDCPalette redPalette] tint700];
438 accountCell.detailTextLabel.numberOfLines = 1;
439 break;
440 }
441 case ItemTypeSyncSwitch: {
442 SyncSwitchCell* switchCell =
443 base::mac::ObjCCastStrict<SyncSwitchCell>(cell);
444 [switchCell.switchView addTarget:self
445 action:@selector(changeSyncStatusToOn:)
446 forControlEvents:UIControlEventValueChanged];
447 break;
448 }
449 case ItemTypeSyncEverything: {
450 SyncSwitchCell* switchCell =
451 base::mac::ObjCCastStrict<SyncSwitchCell>(cell);
452 [switchCell.switchView
453 addTarget:self
454 action:@selector(changeSyncEverythingStatusToOn:)
455 forControlEvents:UIControlEventValueChanged];
456 break;
457 }
458 case ItemTypeSyncableDataType: {
459 SyncSwitchCell* switchCell =
460 base::mac::ObjCCastStrict<SyncSwitchCell>(cell);
461 [switchCell.switchView addTarget:self
462 action:@selector(changeDataTypeSyncStatusToOn:)
463 forControlEvents:UIControlEventValueChanged];
464 switchCell.switchView.tag = [self tagForIndexPath:indexPath];
465 break;
466 }
467 default:
468 break;
469 }
470 return cell;
471 }
472
473 // Method for overriding the header view of a section. Used to set the header
474 // text color to gray.
475 - (UICollectionReusableView*)collectionView:(UICollectionView*)collectionView
476 viewForSupplementaryElementOfKind:(NSString*)kind
477 atIndexPath:(NSIndexPath*)indexPath {
478 UICollectionReusableView* view = [super collectionView:collectionView
479 viewForSupplementaryElementOfKind:kind
480 atIndexPath:indexPath];
481
482 MDCCollectionViewTextCell* textCell =
483 base::mac::ObjCCast<MDCCollectionViewTextCell>(view);
484 if (textCell) {
485 textCell.textLabel.textColor = [[MDCPalette greyPalette] tint500];
486 }
487 return view;
488 }
489
490 #pragma mark UICollectionViewDelegate
491
492 - (void)collectionView:(UICollectionView*)collectionView
493 didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
494 [super collectionView:collectionView didSelectItemAtIndexPath:indexPath];
495
496 NSInteger itemType =
497 [self.collectionViewModel itemTypeForIndexPath:indexPath];
498 switch (itemType) {
499 case ItemTypeSyncError:
500 [self fixSyncErrorIfPossible];
501 break;
502 case ItemTypeAccount: {
503 CollectionViewAccountItem* accountItem =
504 base::mac::ObjCCastStrict<CollectionViewAccountItem>(
505 [self.collectionViewModel itemAtIndexPath:indexPath]);
506 if (!accountItem.accessoryType) {
507 [self startSwitchAccountForIdentity:accountItem.chromeIdentity
508 postSignInAction:POST_SIGNIN_ACTION_NONE];
509 }
510 break;
511 }
512 case ItemTypeEncryption:
513 [self showEncryption];
514 break;
515 case ItemTypeManageSyncedData: {
516 GURL learnMoreUrl = google_util::AppendGoogleLocaleParam(
517 GURL(kSyncGoogleDashboardURL),
518 GetApplicationContext()->GetApplicationLocale());
519 base::scoped_nsobject<OpenUrlCommand> command(
520 [[OpenUrlCommand alloc] initWithURLFromChrome:learnMoreUrl]);
521 [command setTag:IDC_CLOSE_SETTINGS_AND_OPEN_URL];
522 [self chromeExecuteCommand:command];
523 break;
524 }
525 default:
526 break;
527 }
528 }
529
530 #pragma mark MDCCollectionViewStylingDelegate
531
532 - (CGFloat)collectionView:(UICollectionView*)collectionView
533 cellHeightAtIndexPath:(NSIndexPath*)indexPath {
534 CollectionViewItem* item =
535 [self.collectionViewModel itemAtIndexPath:indexPath];
536 switch (item.type) {
537 case ItemTypeAccount:
538 return MDCCellDefaultTwoLineHeight;
539 case ItemTypeSyncSwitch:
540 return [MDCCollectionViewCell
541 cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds)
542 forItem:item];
543 case ItemTypeSyncError:
544 return MDCCellDefaultOneLineWithAvatarHeight;
545 default:
546 return MDCCellDefaultOneLineHeight;
547 }
548 }
549
550 - (BOOL)collectionView:(UICollectionView*)collectionView
551 hidesInkViewAtIndexPath:(NSIndexPath*)indexPath {
552 NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath];
553 switch (type) {
554 case ItemTypeSyncSwitch:
555 case ItemTypeSyncEverything:
556 case ItemTypeSyncableDataType:
557 return YES;
558 default:
559 return NO;
560 }
561 }
562
563 #pragma mark - Actions
564
565 - (void)changeSyncStatusToOn:(UISwitch*)sender {
566 if (self.navigationController.topViewController != self) {
567 // Workaround for timing issue when taping on a switch and the error or
568 // encryption cells. See crbug.com/647678.
569 return;
570 }
571
572 BOOL isOn = sender.isOn;
573 BOOL wasOn = _syncSetupService->IsSyncEnabled();
574 if (wasOn == isOn)
575 return;
576
577 base::AutoReset<BOOL> autoReset(&_ignoreSyncStateChanges, YES);
578 _syncSetupService->SetSyncEnabled(isOn);
579
580 BOOL isNowOn = _syncSetupService->IsSyncEnabled();
581 if (isNowOn == wasOn) {
582 DLOG(WARNING) << "Call to SetSyncEnabled(" << (isOn ? "YES" : "NO")
583 << ") failed.";
584 // This shouldn't happen, but in case there was an underlying sync problem,
585 // make sure the UI reflects sync's reality.
586 NSIndexPath* indexPath = [self.collectionViewModel
587 indexPathForItemType:ItemTypeSyncSwitch
588 sectionIdentifier:SectionIdentifierEnableSync];
589
590 SyncSwitchItem* item = base::mac::ObjCCastStrict<SyncSwitchItem>(
591 [self.collectionViewModel itemAtIndexPath:indexPath]);
592 item.on = isNowOn;
593 }
594 [self updateCollectionView];
595 }
596
597 - (void)fixSyncErrorIfPossible {
598 if (![self isSyncErrorFixableByUserAction] || ![self shouldDisplaySyncError])
599 return;
600
601 // Unrecoverable errors are special-cased to only do the signing out and back
602 // in from the Sync settings screen (where user interaction can safely be
603 // prevented).
604 if (_syncSetupService->GetSyncServiceState() ==
605 SyncSetupService::kSyncServiceUnrecoverableError) {
606 ChromeIdentity* authenticatedIdentity =
607 AuthenticationServiceFactory::GetForBrowserState(_browserState)
608 ->GetAuthenticatedIdentity();
609 [self startSwitchAccountForIdentity:authenticatedIdentity
610 postSignInAction:POST_SIGNIN_ACTION_START_SYNC];
611 return;
612 }
613
614 base::scoped_nsobject<GenericChromeCommand> command(
615 [ios_internal::sync::GetSyncCommandForBrowserState(_browserState)
616 retain]);
617 [self chromeExecuteCommand:command];
618 }
619
620 - (void)startSwitchAccountForIdentity:(ChromeIdentity*)identity
621 postSignInAction:(PostSignInAction)postSignInAction {
622 if (!_syncSetupService->IsSyncEnabled())
623 return;
624
625 _authenticationOperationInProgress = YES;
626 [[NSNotificationCenter defaultCenter]
627 postNotificationName:kSwitchAccountWillStartNotification
628 object:self];
629 [self preventUserInteraction];
630 DCHECK(!_authenticationFlow);
631 _authenticationFlow.reset([[AuthenticationFlow alloc]
632 initWithBrowserState:_browserState
633 identity:identity
634 shouldClearData:SHOULD_CLEAR_DATA_USER_CHOICE
635 postSignInAction:postSignInAction
636 presentingViewController:self]);
637
638 base::WeakNSObject<SyncSettingsCollectionViewController> weakSelf(self);
639 [_authenticationFlow startSignInWithCompletion:^(BOOL success) {
640 [weakSelf didSwitchAccountWithSuccess:success];
641 }];
642 }
643
644 - (void)didSwitchAccountWithSuccess:(BOOL)success {
645 _authenticationFlow.reset();
646 [self allowUserInteraction];
647 [[NSNotificationCenter defaultCenter]
648 postNotificationName:kSwitchAccountDidFinishNotification
649 object:self];
650 _authenticationOperationInProgress = NO;
651 if (![self popViewIfSignedOut]) {
652 // Only reload the view if it wasn't popped.
653 [self reloadData];
654 }
655 }
656
657 - (void)changeSyncEverythingStatusToOn:(UISwitch*)sender {
658 if (!_syncSetupService->IsSyncEnabled() ||
659 [self shouldDisableSettingsOnSyncError])
660 return;
661 BOOL isOn = sender.isOn;
662 BOOL wasOn = _syncSetupService->IsSyncingAllDataTypes();
663 if (wasOn == isOn)
664 return;
665
666 base::AutoReset<BOOL> autoReset(&_ignoreSyncStateChanges, YES);
667 _syncSetupService->SetSyncingAllDataTypes(isOn);
668
669 // Base the UI on the actual sync value, not the toggle.
670 BOOL isNowOn = _syncSetupService->IsSyncingAllDataTypes();
671 if (isNowOn == wasOn) {
672 DLOG(WARNING) << "Call to SetSyncingAllDataTypes(" << (isOn ? "YES" : "NO")
673 << ") failed";
674 // No change - there was a sync-level problem that didn't allow the change.
675 // This really shouldn't happen, but just in case, make sure the UI reflects
676 // sync's reality.
677 NSIndexPath* indexPath = [self.collectionViewModel
678 indexPathForItemType:ItemTypeSyncEverything
679 sectionIdentifier:SectionIdentifierSyncServices];
680 SyncSwitchItem* item = base::mac::ObjCCastStrict<SyncSwitchItem>(
681 [self.collectionViewModel itemAtIndexPath:indexPath]);
682 item.on = isNowOn;
683 }
684 [self updateCollectionView];
685 }
686
687 - (void)changeDataTypeSyncStatusToOn:(UISwitch*)sender {
688 if (!_syncSetupService->IsSyncEnabled() ||
689 _syncSetupService->IsSyncingAllDataTypes() ||
690 [self shouldDisableSettingsOnSyncError])
691 return;
692
693 BOOL isOn = sender.isOn;
694
695 SyncSwitchItem* syncSwitchItem =
696 base::mac::ObjCCastStrict<SyncSwitchItem>([self.collectionViewModel
697 itemAtIndexPath:[self indexPathForTag:sender.tag]]);
698 SyncSetupService::SyncableDatatype dataType =
699 (SyncSetupService::SyncableDatatype)syncSwitchItem.dataType;
700 syncer::ModelType modelType = _syncSetupService->GetModelType(dataType);
701
702 base::AutoReset<BOOL> autoReset(&_ignoreSyncStateChanges, YES);
703 _syncSetupService->SetDataTypeEnabled(modelType, isOn);
704 }
705
706 - (void)showEncryption {
707 browser_sync::ProfileSyncService* syncService =
708 IOSChromeProfileSyncServiceFactory::GetForBrowserState(_browserState);
709 if (!syncService->IsEngineInitialized() ||
710 !_syncSetupService->IsSyncEnabled() ||
711 [self shouldDisableSettingsOnSyncError])
712 return;
713
714 base::scoped_nsobject<UIViewController> controllerToPush;
715 // If there was a sync error, prompt the user to enter the passphrase.
716 // Otherwise, show the full encryption options.
717 if (syncService->IsPassphraseRequired()) {
718 controllerToPush.reset(
719 [[SyncEncryptionPassphraseCollectionViewController alloc]
720 initWithBrowserState:_browserState]);
721 } else {
722 controllerToPush.reset([[SyncEncryptionCollectionViewController alloc]
723 initWithBrowserState:_browserState]);
724 }
725 [self.navigationController pushViewController:controllerToPush animated:YES];
726 }
727
728 #pragma mark Updates
729
730 - (void)updateCollectionView {
731 base::WeakNSObject<SyncSettingsCollectionViewController> weakSelf(self);
732 [self.collectionView performBatchUpdates:^{
733 [weakSelf updateCollectionViewInternal];
734 }
735 completion:nil];
736 }
737
738 - (void)updateCollectionViewInternal {
739 NSIndexPath* indexPath = [self.collectionViewModel
740 indexPathForItemType:ItemTypeSyncSwitch
741 sectionIdentifier:SectionIdentifierEnableSync];
742
743 SyncSwitchItem* syncItem = base::mac::ObjCCastStrict<SyncSwitchItem>(
744 [self.collectionViewModel itemAtIndexPath:indexPath]);
745 syncItem.on = _syncSetupService->IsSyncEnabled();
746 [self reconfigureCellsForItems:@[ syncItem ]
747 inSectionWithIdentifier:SectionIdentifierEnableSync];
748
749 // Update Sync Accounts section.
750 if ([self hasAccountsSection]) {
751 NSInteger section = [self.collectionViewModel
752 sectionForSectionIdentifier:SectionIdentifierSyncAccounts];
753 NSInteger itemsCount =
754 [self.collectionViewModel numberOfItemsInSection:section];
755 NSMutableArray* accountsToReconfigure =
756 [[[NSMutableArray alloc] init] autorelease];
757 for (NSInteger item = 0; item < itemsCount; ++item) {
758 NSIndexPath* indexPath = [self.collectionViewModel
759 indexPathForItemType:ItemTypeAccount
760 sectionIdentifier:SectionIdentifierSyncAccounts
761 atIndex:item];
762 CollectionViewAccountItem* accountItem =
763 base::mac::ObjCCastStrict<CollectionViewAccountItem>(
764 [self.collectionViewModel itemAtIndexPath:indexPath]);
765 accountItem.enabled = _syncSetupService->IsSyncEnabled();
766 [accountsToReconfigure addObject:accountItem];
767 }
768 [self reconfigureCellsForItems:accountsToReconfigure
769 inSectionWithIdentifier:SectionIdentifierSyncAccounts];
770 }
771
772 // Update Sync Services section.
773 indexPath = [self.collectionViewModel
774 indexPathForItemType:ItemTypeSyncEverything
775 sectionIdentifier:SectionIdentifierSyncServices];
776 SyncSwitchItem* syncEverythingItem =
777 base::mac::ObjCCastStrict<SyncSwitchItem>(
778 [self.collectionViewModel itemAtIndexPath:indexPath]);
779 syncEverythingItem.on = _syncSetupService->IsSyncingAllDataTypes();
780 syncEverythingItem.enabled = [self shouldSyncEverythingItemBeEnabled];
781 [self reconfigureCellsForItems:@[ syncEverythingItem ]
782 inSectionWithIdentifier:SectionIdentifierSyncServices];
783
784 NSInteger section = [self.collectionViewModel
785 sectionForSectionIdentifier:SectionIdentifierSyncServices];
786 NSInteger itemsCount =
787 [self.collectionViewModel numberOfItemsInSection:section];
788 // Syncable data types cells are offset by the Sync Everything cell.
789 NSMutableArray* switchsToReconfigure =
790 [[[NSMutableArray alloc] init] autorelease];
791 for (NSInteger item = 1; item < itemsCount; ++item) {
792 NSUInteger index = item - 1;
793 NSIndexPath* indexPath = [self.collectionViewModel
794 indexPathForItemType:ItemTypeSyncableDataType
795 sectionIdentifier:SectionIdentifierSyncServices
796 atIndex:index];
797 SyncSwitchItem* syncSwitchItem = base::mac::ObjCCastStrict<SyncSwitchItem>(
798 [self.collectionViewModel itemAtIndexPath:indexPath]);
799 SyncSetupService::SyncableDatatype dataType =
800 (SyncSetupService::SyncableDatatype)syncSwitchItem.dataType;
801 syncer::ModelType modelType = _syncSetupService->GetModelType(dataType);
802 syncSwitchItem.on = _syncSetupService->IsDataTypeEnabled(modelType);
803 syncSwitchItem.enabled = [self shouldSyncableItemsBeEnabled];
804 [switchsToReconfigure addObject:syncSwitchItem];
805 }
806 [self reconfigureCellsForItems:switchsToReconfigure
807 inSectionWithIdentifier:SectionIdentifierSyncServices];
808
809 // Update Encryption cell.
810 [self updateEncryptionCell];
811
812 // Add/Remove the Sync Error. This is the only update that can change index
813 // paths. It is done last because self.collectionViewModel isn't aware of
814 // the performBatchUpdates:completion: order of update/remove/delete.
815 [self updateSyncError];
816 }
817
818 - (void)updateSyncError {
819 BOOL shouldDisplayError = [self shouldDisplaySyncError];
820 BOOL isDisplayingError =
821 [self.collectionViewModel hasItemForItemType:ItemTypeSyncError
822 sectionIdentifier:SectionIdentifierSyncError];
823 if (shouldDisplayError && !isDisplayingError) {
824 [self.collectionViewModel
825 insertSectionWithIdentifier:SectionIdentifierSyncError
826 atIndex:0];
827 [self.collectionViewModel addItem:[self syncErrorItem]
828 toSectionWithIdentifier:SectionIdentifierSyncError];
829 NSInteger section = [self.collectionViewModel
830 sectionForSectionIdentifier:SectionIdentifierSyncError];
831 [self.collectionView insertSections:[NSIndexSet indexSetWithIndex:section]];
832 } else if (!shouldDisplayError && isDisplayingError) {
833 NSInteger section = [self.collectionViewModel
834 sectionForSectionIdentifier:SectionIdentifierSyncError];
835 [self.collectionViewModel
836 removeSectionWithIdentifier:SectionIdentifierSyncError];
837 [self.collectionView deleteSections:[NSIndexSet indexSetWithIndex:section]];
838 }
839 }
840
841 - (void)updateEncryptionCell {
842 BOOL shouldDisplayEncryptionError = [self shouldDisplayEncryptionError];
843 NSIndexPath* indexPath = [self.collectionViewModel
844 indexPathForItemType:ItemTypeEncryption
845 sectionIdentifier:SectionIdentifierEncryptionAndFooter];
846 TextAndErrorItem* item = base::mac::ObjCCastStrict<TextAndErrorItem>(
847 [self.collectionViewModel itemAtIndexPath:indexPath]);
848 item.shouldDisplayError = shouldDisplayEncryptionError;
849 item.enabled = [self shouldEncryptionItemBeEnabled];
850 [self reconfigureCellsForItems:@[ item ]
851 inSectionWithIdentifier:SectionIdentifierEncryptionAndFooter];
852 }
853
854 - (void)updateAccountItem:(CollectionViewAccountItem*)item
855 withIdentity:(ChromeIdentity*)identity {
856 item.image = [_avatarCache resizedAvatarForIdentity:identity];
857 item.text = identity.userEmail;
858 item.chromeIdentity = identity;
859 }
860
861 #pragma mark Helpers
862
863 - (BOOL)hasAccountsSection {
864 OAuth2TokenService* tokenService =
865 OAuth2TokenServiceFactory::GetForBrowserState(_browserState);
866 return _allowSwitchSyncAccount && tokenService->GetAccounts().size() > 1;
867 }
868
869 - (BOOL)shouldDisplaySyncError {
870 SyncSetupService::SyncServiceState state =
871 _syncSetupService->GetSyncServiceState();
872 return state != SyncSetupService::kNoSyncServiceError;
873 }
874
875 - (BOOL)shouldDisableSettingsOnSyncError {
876 SyncSetupService::SyncServiceState state =
877 _syncSetupService->GetSyncServiceState();
878 return state != SyncSetupService::kNoSyncServiceError &&
879 state != SyncSetupService::kSyncServiceNeedsPassphrase;
880 }
881
882 - (BOOL)shouldDisplayEncryptionError {
883 return _syncSetupService->GetSyncServiceState() ==
884 SyncSetupService::kSyncServiceNeedsPassphrase;
885 }
886
887 - (BOOL)isSyncErrorFixableByUserAction {
888 SyncSetupService::SyncServiceState state =
889 _syncSetupService->GetSyncServiceState();
890 return state == SyncSetupService::kSyncServiceNeedsPassphrase ||
891 state == SyncSetupService::kSyncServiceSignInNeedsUpdate ||
892 state == SyncSetupService::kSyncServiceUnrecoverableError;
893 }
894
895 - (int)titleIdForSyncableDataType:(SyncSetupService::SyncableDatatype)datatype {
896 switch (datatype) {
897 case SyncSetupService::kSyncBookmarks:
898 return IDS_SYNC_DATATYPE_BOOKMARKS;
899 case SyncSetupService::kSyncOmniboxHistory:
900 return IDS_SYNC_DATATYPE_TYPED_URLS;
901 case SyncSetupService::kSyncPasswords:
902 return IDS_SYNC_DATATYPE_PASSWORDS;
903 case SyncSetupService::kSyncOpenTabs:
904 return IDS_SYNC_DATATYPE_TABS;
905 case SyncSetupService::kSyncAutofill:
906 return IDS_SYNC_DATATYPE_AUTOFILL;
907 case SyncSetupService::kSyncPreferences:
908 return IDS_SYNC_DATATYPE_PREFERENCES;
909 case SyncSetupService::kSyncReadingList:
910 return IDS_SYNC_DATATYPE_READING_LIST;
911 case SyncSetupService::kNumberOfSyncableDatatypes:
912 NOTREACHED();
913 }
914 return 0;
915 }
916
917 - (BOOL)shouldEncryptionItemBeEnabled {
918 browser_sync::ProfileSyncService* syncService =
919 IOSChromeProfileSyncServiceFactory::GetForBrowserState(_browserState);
920 return (syncService->IsEngineInitialized() &&
921 _syncSetupService->IsSyncEnabled() &&
922 ![self shouldDisableSettingsOnSyncError]);
923 }
924
925 - (BOOL)shouldSyncEverythingItemBeEnabled {
926 return (_syncSetupService->IsSyncEnabled() &&
927 ![self shouldDisableSettingsOnSyncError]);
928 }
929
930 - (BOOL)shouldSyncableItemsBeEnabled {
931 return (!_syncSetupService->IsSyncingAllDataTypes() &&
932 _syncSetupService->IsSyncEnabled() &&
933 ![self shouldDisableSettingsOnSyncError]);
934 }
935
936 - (NSInteger)tagForIndexPath:(NSIndexPath*)indexPath {
937 DCHECK(indexPath.section ==
938 [self.collectionViewModel
939 sectionForSectionIdentifier:SectionIdentifierSyncServices]);
940 NSInteger index =
941 [self.collectionViewModel indexInItemTypeForIndexPath:indexPath];
942 return index + kTagShift;
943 }
944
945 - (NSIndexPath*)indexPathForTag:(NSInteger)shiftedTag {
946 NSInteger unshiftedTag = shiftedTag - kTagShift;
947 return [self.collectionViewModel
948 indexPathForItemType:ItemTypeSyncableDataType
949 sectionIdentifier:SectionIdentifierSyncServices
950 atIndex:unshiftedTag];
951 }
952
953 #pragma mark SyncObserverModelBridge
954
955 - (void)onSyncStateChanged {
956 if (_ignoreSyncStateChanges || _authenticationOperationInProgress) {
957 return;
958 }
959 [self updateCollectionView];
960 }
961
962 #pragma mark OAuth2TokenServiceObserverBridgeDelegate
963
964 - (void)onEndBatchChanges {
965 if (_authenticationOperationInProgress) {
966 return;
967 }
968 if (![self popViewIfSignedOut]) {
969 // Only reload the view if it wasn't popped.
970 [self reloadData];
971 }
972 }
973
974 #pragma mark SettingsControllerProtocol callbacks
975
976 - (void)settingsWillBeDismissed {
977 [self stopBrowserStateServiceObservers];
978 [_authenticationFlow cancelAndDismiss];
979 }
980
981 #pragma mark - ChromeIdentityServiceObserver
982
983 - (void)onProfileUpdate:(ChromeIdentity*)identity {
984 CollectionViewAccountItem* item =
985 base::mac::ObjCCastStrict<CollectionViewAccountItem>(
986 [_identityMap objectForKey:identity.gaiaID]);
987 [self updateAccountItem:item withIdentity:identity];
988 [self reconfigureCellsForItems:@[ item ]
989 inSectionWithIdentifier:SectionIdentifierSyncAccounts];
990 }
991
992 - (void)onChromeIdentityServiceWillBeDestroyed {
993 _identityServiceObserver.reset();
994 }
995
996 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698