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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: ios/chrome/browser/ui/settings/sync_settings_collection_view_controller.mm
diff --git a/ios/chrome/browser/ui/settings/sync_settings_collection_view_controller.mm b/ios/chrome/browser/ui/settings/sync_settings_collection_view_controller.mm
new file mode 100644
index 0000000000000000000000000000000000000000..14905c796b625a7f0dbcf5794eb8c70a58c9c03f
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/sync_settings_collection_view_controller.mm
@@ -0,0 +1,996 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/settings/sync_settings_collection_view_controller.h"
+
+#include <memory>
+
+#include "base/auto_reset.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/scoped_nsobject.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/google/core/browser/google_util.h"
+#include "components/signin/core/browser/account_tracker_service.h"
+#include "components/signin/core/browser/profile_oauth2_token_service.h"
+#include "components/signin/ios/browser/oauth2_token_service_observer_bridge.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/sync/base/model_type.h"
+#include "ios/chrome/browser/application_context.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
+#include "ios/chrome/browser/experimental_flags.h"
+#include "ios/chrome/browser/signin/account_tracker_service_factory.h"
+#import "ios/chrome/browser/signin/authentication_service.h"
+#import "ios/chrome/browser/signin/authentication_service_factory.h"
+#include "ios/chrome/browser/signin/chrome_identity_service_observer_bridge.h"
+#include "ios/chrome/browser/signin/oauth2_token_service_factory.h"
+#include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h"
+#include "ios/chrome/browser/sync/sync_setup_service.h"
+#include "ios/chrome/browser/sync/sync_setup_service_factory.h"
+#import "ios/chrome/browser/ui/authentication/authentication_flow.h"
+#import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrome.h"
+#import "ios/chrome/browser/ui/collection_view/cells/collection_view_account_item.h"
+#import "ios/chrome/browser/ui/collection_view/cells/collection_view_detail_item.h"
+#import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h"
+#import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
+#import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
+#import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
+#include "ios/chrome/browser/ui/commands/ios_command_ids.h"
+#import "ios/chrome/browser/ui/commands/open_url_command.h"
+#import "ios/chrome/browser/ui/settings/cells/sync_switch_item.h"
+#import "ios/chrome/browser/ui/settings/cells/text_and_error_item.h"
+#import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
+#import "ios/chrome/browser/ui/settings/sync_encryption_collection_view_controller.h"
+#import "ios/chrome/browser/ui/settings/sync_encryption_passphrase_collection_view_controller.h"
+#import "ios/chrome/browser/ui/settings/utils/resized_avatar_cache.h"
+#import "ios/chrome/browser/ui/sync/sync_util.h"
+#import "ios/chrome/browser/ui/uikit_ui_util.h"
+#include "ios/chrome/grit/ios_strings.h"
+#include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
+#import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
+#import "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+#include "url/gurl.h"
+
+// The a11y identifier of the view controller's view.
+NSString* const kSettingsSyncId = @"kSettingsSyncId";
+// Notification when a switch account operation will start.
+NSString* const kSwitchAccountWillStartNotification =
+ @"kSwitchAccountWillStartNotification";
+// Notification when a switch account operation did finish.
+NSString* const kSwitchAccountDidFinishNotification =
+ @"kSwitchAccountDidFinishNotification";
+// Used to tag and retrieve ItemTypeSyncableDataType cell tags.
+const NSInteger kTagShift = 1000;
+
+namespace {
+
+typedef NS_ENUM(NSInteger, SectionIdentifier) {
+ SectionIdentifierSyncError = kSectionIdentifierEnumZero,
+ SectionIdentifierEnableSync,
+ SectionIdentifierSyncAccounts,
+ SectionIdentifierSyncServices,
+ SectionIdentifierEncryptionAndFooter,
+};
+
+typedef NS_ENUM(NSInteger, ItemType) {
+ ItemTypeSyncError = kItemTypeEnumZero,
+ ItemTypeSyncSwitch,
+ ItemTypeAccount,
+ ItemTypeSyncEverything,
+ ItemTypeSyncableDataType,
+ ItemTypeEncryption,
+ ItemTypeManageSyncedData,
+ ItemTypeHeader,
+};
+
+} // namespace
+
+@interface SyncSettingsCollectionViewController ()<
+ ChromeIdentityServiceObserver,
+ OAuth2TokenServiceObserverBridgeDelegate,
+ SettingsControllerProtocol,
+ SyncObserverModelBridge> {
+ ios::ChromeBrowserState* _browserState; // Weak.
+ SyncSetupService* _syncSetupService; // Weak.
+ std::unique_ptr<SyncObserverBridge> _syncObserver;
+ std::unique_ptr<OAuth2TokenServiceObserverBridge> _tokenServiceObserver;
+ base::scoped_nsobject<AuthenticationFlow> _authenticationFlow;
+ // Whether switching sync account is allowed on the screen.
+ BOOL _allowSwitchSyncAccount;
+ // Whether an authentication operation is in progress (e.g switch accounts).
+ BOOL _authenticationOperationInProgress;
+ // Whether Sync State changes should be currently ignored.
+ BOOL _ignoreSyncStateChanges;
+
+ // Cache for Identity items avatar images.
+ base::scoped_nsobject<ResizedAvatarCache> _avatarCache;
+ std::unique_ptr<ChromeIdentityServiceObserverBridge> _identityServiceObserver;
+ // Enable lookup of item corresponding to a given identity GAIA ID string.
+ base::scoped_nsobject<NSDictionary<NSString*, CollectionViewItem*>>
+ _identityMap;
+}
+
+// Stops observing browser state services.
+- (void)stopBrowserStateServiceObservers;
+// Pops the view if user is signed out and not authentication operation is in
+// progress. Returns YES if the view was popped.
+- (BOOL)popViewIfSignedOut;
+
+// Returns a switch item for sync, set to on if |isOn| is YES.
+- (CollectionViewItem*)syncSwitchItem:(BOOL)isOn;
+// Returns an item for sync errors other than sync encryption.
+- (CollectionViewItem*)syncErrorItem;
+// Returns a switch item for sync everything, set to on if |isOn| is YES.
+- (CollectionViewItem*)syncEverythingSwitchItem:(BOOL)isOn;
+// Returns a switch item for the syncable data type |dataType|, set to on if
+// |IsDataTypeEnabled| for that type returns true.
+- (CollectionViewItem*)switchItemForDataType:
+ (SyncSetupService::SyncableDatatype)dataType;
+// Returns an item for Encryption.
+- (CollectionViewItem*)encryptionCellItem;
+// Returns an item to open a link to manage the synced data.
+- (CollectionViewItem*)manageSyncedDataItem;
+
+// Action method for sync switch.
+- (void)changeSyncStatusToOn:(UISwitch*)sender;
+// Action method for the sync error cell.
+- (void)fixSyncErrorIfPossible;
+// Action method for the account cells.
+- (void)startSwitchAccountForIdentity:(ChromeIdentity*)identity
+ postSignInAction:(PostSignInAction)postSigninAction;
+// Callback for switch account action method.
+- (void)didSwitchAccountWithSuccess:(BOOL)success;
+// Action method for the Sync Everything switch.
+- (void)changeSyncEverythingStatusToOn:(UISwitch*)sender;
+// Action method for the data type switches.
+- (void)changeDataTypeSyncStatusToOn:(UISwitch*)sender;
+// Action method for the encryption cell.
+- (void)showEncryption;
+
+// Updates the visual status of the screen (i.e. whether cells are enabled,
+// whether errors are displayed, ...).
+- (void)updateCollectionView;
+// Ensures the Sync error cell is shown when there is an error.
+- (void)updateSyncError;
+// Ensures the encryption cell displays an error if needed.
+- (void)updateEncryptionCell;
+// Updates the account item so it can reflect the latest state of the identity.
+- (void)updateAccountItem:(CollectionViewAccountItem*)item
+ withIdentity:(ChromeIdentity*)identity;
+
+// Returns whether the Sync Settings screen has an Accounts section, allowing
+// users to choose to which account they want to sync to.
+- (BOOL)hasAccountsSection;
+// Returns whether a sync error cell should be displayed.
+- (BOOL)shouldDisplaySyncError;
+// Returns whether the Sync settings should be disabled because of a Sync error.
+- (BOOL)shouldDisableSettingsOnSyncError;
+// Returns whether an error should be displayed on the encryption cell.
+- (BOOL)shouldDisplayEncryptionError;
+// Returns whether the existing sync error is fixable by a user action.
+- (BOOL)isSyncErrorFixableByUserAction;
+// Returns the ID to use to access the l10n string for the given data type.
+- (int)titleIdForSyncableDataType:(SyncSetupService::SyncableDatatype)datatype;
+// Returns whether the encryption item should be enabled.
+- (BOOL)shouldEncryptionItemBeEnabled;
+// Returns whether the sync everything item should be enabled.
+- (BOOL)shouldSyncEverythingItemBeEnabled;
+// Returns whether the data type items should be enabled.
+- (BOOL)shouldSyncableItemsBeEnabled;
+// Returns the tag for a data type switch based on its index path.
+- (NSInteger)tagForIndexPath:(NSIndexPath*)indexPath;
+// Returns the indexPath for a data type switch based on its tag.
+- (NSIndexPath*)indexPathForTag:(NSInteger)shiftedTag;
+
+@end
+
+@implementation SyncSettingsCollectionViewController
+
+#pragma mark Initialization
+
+- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
+ allowSwitchSyncAccount:(BOOL)allowSwitchSyncAccount {
+ DCHECK(browserState);
+ self = [super initWithStyle:CollectionViewControllerStyleAppBar];
+ if (self) {
+ _allowSwitchSyncAccount = allowSwitchSyncAccount;
+ _browserState = browserState;
+ _syncSetupService =
+ SyncSetupServiceFactory::GetForBrowserState(_browserState);
+ self.title = l10n_util::GetNSString(IDS_IOS_SYNC_SETTING_TITLE);
+ browser_sync::ProfileSyncService* syncService =
+ IOSChromeProfileSyncServiceFactory::GetForBrowserState(_browserState);
+ _syncObserver.reset(new SyncObserverBridge(self, syncService));
+ _tokenServiceObserver.reset(new OAuth2TokenServiceObserverBridge(
+ OAuth2TokenServiceFactory::GetForBrowserState(_browserState), self));
+ self.collectionViewAccessibilityIdentifier = kSettingsSyncId;
+ _avatarCache.reset([[ResizedAvatarCache alloc] init]);
+ _identityServiceObserver.reset(
+ new ChromeIdentityServiceObserverBridge(self));
+ [self loadModel];
+ }
+ return self;
+}
+
+- (void)stopBrowserStateServiceObservers {
+ _syncObserver.reset();
+ _tokenServiceObserver.reset();
+ _identityServiceObserver.reset();
+}
+
+- (BOOL)popViewIfSignedOut {
+ if (AuthenticationServiceFactory::GetForBrowserState(_browserState)
+ ->IsAuthenticated()) {
+ return NO;
+ }
+ if (_authenticationOperationInProgress) {
+ // The signed out state might be temporary (e.g. account switch, ...).
+ // Don't pop this view based on intermediary values.
+ return NO;
+ }
+ [self.navigationController popToViewController:self animated:NO];
+ [base::mac::ObjCCastStrict<SettingsNavigationController>(
+ self.navigationController) popViewControllerOrCloseSettingsAnimated:NO];
+ return YES;
+}
+
+#pragma mark View lifecycle
+
+- (void)viewWillAppear:(BOOL)animated {
+ [super viewWillAppear:animated];
+ [self updateEncryptionCell];
+}
+
+#pragma mark SettingsRootCollectionViewController
+
+- (void)loadModel {
+ [super loadModel];
+ CollectionViewModel* model = self.collectionViewModel;
+
+ // SyncError section.
+ if ([self shouldDisplaySyncError]) {
+ [model addSectionWithIdentifier:SectionIdentifierSyncError];
+ [model addItem:[self syncErrorItem]
+ toSectionWithIdentifier:SectionIdentifierSyncError];
+ }
+
+ // Sync Section.
+ BOOL syncEnabled = _syncSetupService->IsSyncEnabled();
+ [model addSectionWithIdentifier:SectionIdentifierEnableSync];
+ [model addItem:[self syncSwitchItem:syncEnabled]
+ toSectionWithIdentifier:SectionIdentifierEnableSync];
+
+ // Sync to Section.
+ if ([self hasAccountsSection]) {
+ NSMutableDictionary<NSString*, CollectionViewItem*>* mutableIdentityMap =
+ [[[NSMutableDictionary alloc] init] autorelease];
+ // Accounts section. Cells enabled if sync is on.
+ [model addSectionWithIdentifier:SectionIdentifierSyncAccounts];
+ CollectionViewTextItem* syncToHeader = [[[CollectionViewTextItem alloc]
+ initWithType:ItemTypeHeader] autorelease];
+ syncToHeader.text = l10n_util::GetNSString(IDS_IOS_SYNC_TO_TITLE);
+ [model setHeader:syncToHeader
+ forSectionWithIdentifier:SectionIdentifierSyncAccounts];
+ ProfileOAuth2TokenService* oauth2_service =
+ OAuth2TokenServiceFactory::GetForBrowserState(_browserState);
+ AccountTrackerService* accountTracker =
+ ios::AccountTrackerServiceFactory::GetForBrowserState(_browserState);
+
+ for (const std::string& account_id : oauth2_service->GetAccounts()) {
+ AccountInfo account = accountTracker->GetAccountInfo(account_id);
+ ChromeIdentity* identity = ios::GetChromeBrowserProvider()
+ ->GetChromeIdentityService()
+ ->GetIdentityWithGaiaID(account.gaia);
+ CollectionViewItem* accountItem = [self accountItem:identity];
+ [model addItem:accountItem
+ toSectionWithIdentifier:SectionIdentifierSyncAccounts];
+ [mutableIdentityMap setObject:accountItem forKey:identity.gaiaID];
+ }
+ _identityMap.reset([mutableIdentityMap retain]);
+ }
+
+ // Data Types to sync. Enabled if sync is on.
+ [model addSectionWithIdentifier:SectionIdentifierSyncServices];
+ CollectionViewTextItem* syncServicesHeader = [
+ [[CollectionViewTextItem alloc] initWithType:ItemTypeHeader] autorelease];
+ syncServicesHeader.text =
+ l10n_util::GetNSString(IDS_IOS_SYNC_DATA_TYPES_TITLE);
+ [model setHeader:syncServicesHeader
+ forSectionWithIdentifier:SectionIdentifierSyncServices];
+ BOOL syncEverythingEnabled = _syncSetupService->IsSyncingAllDataTypes();
+ [model addItem:[self syncEverythingSwitchItem:syncEverythingEnabled]
+ toSectionWithIdentifier:SectionIdentifierSyncServices];
+ // Specific Data Types to sync. Enabled if Sync Everything is off.
+ for (int i = 0; i < SyncSetupService::kNumberOfSyncableDatatypes; ++i) {
+ SyncSetupService::SyncableDatatype dataType =
+ static_cast<SyncSetupService::SyncableDatatype>(i);
+ if (!experimental_flags::IsReadingListEnabled() &&
+ dataType == SyncSetupService::kSyncReadingList) {
+ // Display Reading List only if it is enabled.
+ continue;
+ }
+ [model addItem:[self switchItemForDataType:dataType]
+ toSectionWithIdentifier:SectionIdentifierSyncServices];
+ }
+
+ // Encryption section. Enabled if sync is on.
+ [model addSectionWithIdentifier:SectionIdentifierEncryptionAndFooter];
+ [model addItem:[self encryptionCellItem]
+ toSectionWithIdentifier:SectionIdentifierEncryptionAndFooter];
+ [model addItem:[self manageSyncedDataItem]
+ toSectionWithIdentifier:SectionIdentifierEncryptionAndFooter];
+}
+
+#pragma mark - Model items
+
+- (CollectionViewItem*)syncSwitchItem:(BOOL)isOn {
+ SyncSwitchItem* syncSwitchItem = [self
+ switchItemWithType:ItemTypeSyncSwitch
+ title:l10n_util::GetNSString(IDS_IOS_SYNC_SETTING_TITLE)
+ subTitle:l10n_util::GetNSString(
+ IDS_IOS_SIGN_IN_TO_CHROME_SETTING_SUBTITLE)];
+ syncSwitchItem.on = isOn;
+ return syncSwitchItem;
+}
+
+- (CollectionViewItem*)syncErrorItem {
+ DCHECK([self shouldDisplaySyncError]);
+ CollectionViewAccountItem* syncErrorItem = [[[CollectionViewAccountItem alloc]
+ initWithType:ItemTypeSyncError] autorelease];
+ syncErrorItem.text = l10n_util::GetNSString(IDS_IOS_SYNC_ERROR_TITLE);
+ syncErrorItem.image = [UIImage imageNamed:@"settings_error"];
+ syncErrorItem.detailText =
+ ios_internal::sync::GetSyncErrorMessageForBrowserState(_browserState);
+ return syncErrorItem;
+}
+
+- (CollectionViewItem*)accountItem:(ChromeIdentity*)identity {
+ CollectionViewAccountItem* identityAccountItem =
+ [[[CollectionViewAccountItem alloc] initWithType:ItemTypeAccount]
+ autorelease];
+ [self updateAccountItem:identityAccountItem withIdentity:identity];
+
+ identityAccountItem.enabled = _syncSetupService->IsSyncEnabled();
+ ChromeIdentity* authenticatedIdentity =
+ AuthenticationServiceFactory::GetForBrowserState(_browserState)
+ ->GetAuthenticatedIdentity();
+ if (identity == authenticatedIdentity) {
+ identityAccountItem.accessoryType = MDCCollectionViewCellAccessoryCheckmark;
+ }
+ return identityAccountItem;
+}
+
+- (CollectionViewItem*)syncEverythingSwitchItem:(BOOL)isOn {
+ SyncSwitchItem* syncSwitchItem = [self
+ switchItemWithType:ItemTypeSyncEverything
+ title:l10n_util::GetNSString(IDS_IOS_SYNC_EVERYTHING_TITLE)
+ subTitle:nil];
+ syncSwitchItem.on = isOn;
+ syncSwitchItem.enabled = [self shouldSyncEverythingItemBeEnabled];
+ return syncSwitchItem;
+}
+
+- (CollectionViewItem*)switchItemForDataType:
+ (SyncSetupService::SyncableDatatype)dataType {
+ syncer::ModelType modelType = _syncSetupService->GetModelType(dataType);
+ BOOL isOn = _syncSetupService->IsDataTypeEnabled(modelType);
+
+ SyncSwitchItem* syncDataTypeItem =
+ [self switchItemWithType:ItemTypeSyncableDataType
+ title:l10n_util::GetNSString(
+ [self titleIdForSyncableDataType:dataType])
+ subTitle:nil];
+ syncDataTypeItem.dataType = dataType;
+ syncDataTypeItem.on = isOn;
+ syncDataTypeItem.enabled = [self shouldSyncableItemsBeEnabled];
+ return syncDataTypeItem;
+}
+
+- (CollectionViewItem*)encryptionCellItem {
+ TextAndErrorItem* encryptionCellItem =
+ [[[TextAndErrorItem alloc] initWithType:ItemTypeEncryption] autorelease];
+ encryptionCellItem.text =
+ l10n_util::GetNSString(IDS_IOS_SYNC_ENCRYPTION_TITLE);
+ encryptionCellItem.accessoryType =
+ MDCCollectionViewCellAccessoryDisclosureIndicator;
+ encryptionCellItem.shouldDisplayError = [self shouldDisplayEncryptionError];
+ encryptionCellItem.enabled = [self shouldEncryptionItemBeEnabled];
+ return encryptionCellItem;
+}
+
+- (CollectionViewItem*)manageSyncedDataItem {
+ CollectionViewTextItem* manageSyncedDataItem =
+ [[[CollectionViewTextItem alloc] initWithType:ItemTypeManageSyncedData]
+ autorelease];
+ manageSyncedDataItem.text =
+ l10n_util::GetNSString(IDS_IOS_SYNC_RESET_GOOGLE_DASHBOARD_NO_LINK);
+ manageSyncedDataItem.accessibilityTraits |= UIAccessibilityTraitButton;
+ return manageSyncedDataItem;
+}
+
+#pragma mark Item Constructors
+
+- (SyncSwitchItem*)switchItemWithType:(NSInteger)type
+ title:(NSString*)title
+ subTitle:(NSString*)detailText {
+ SyncSwitchItem* switchItem =
+ [[[SyncSwitchItem alloc] initWithType:type] autorelease];
+ switchItem.text = title;
+ switchItem.detailText = detailText;
+ return switchItem;
+}
+
+#pragma mark - UICollectionViewDataSource
+
+- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
+ cellForItemAtIndexPath:(NSIndexPath*)indexPath {
+ UICollectionViewCell* cell =
+ [super collectionView:collectionView cellForItemAtIndexPath:indexPath];
+ NSInteger itemType =
+ [self.collectionViewModel itemTypeForIndexPath:indexPath];
+
+ switch (itemType) {
+ case ItemTypeSyncError: {
+ CollectionViewAccountCell* accountCell =
+ base::mac::ObjCCastStrict<CollectionViewAccountCell>(cell);
+ accountCell.textLabel.textColor = [[MDCPalette redPalette] tint700];
+ accountCell.detailTextLabel.numberOfLines = 1;
+ break;
+ }
+ case ItemTypeSyncSwitch: {
+ SyncSwitchCell* switchCell =
+ base::mac::ObjCCastStrict<SyncSwitchCell>(cell);
+ [switchCell.switchView addTarget:self
+ action:@selector(changeSyncStatusToOn:)
+ forControlEvents:UIControlEventValueChanged];
+ break;
+ }
+ case ItemTypeSyncEverything: {
+ SyncSwitchCell* switchCell =
+ base::mac::ObjCCastStrict<SyncSwitchCell>(cell);
+ [switchCell.switchView
+ addTarget:self
+ action:@selector(changeSyncEverythingStatusToOn:)
+ forControlEvents:UIControlEventValueChanged];
+ break;
+ }
+ case ItemTypeSyncableDataType: {
+ SyncSwitchCell* switchCell =
+ base::mac::ObjCCastStrict<SyncSwitchCell>(cell);
+ [switchCell.switchView addTarget:self
+ action:@selector(changeDataTypeSyncStatusToOn:)
+ forControlEvents:UIControlEventValueChanged];
+ switchCell.switchView.tag = [self tagForIndexPath:indexPath];
+ break;
+ }
+ default:
+ break;
+ }
+ return cell;
+}
+
+// Method for overriding the header view of a section. Used to set the header
+// text color to gray.
+- (UICollectionReusableView*)collectionView:(UICollectionView*)collectionView
+ viewForSupplementaryElementOfKind:(NSString*)kind
+ atIndexPath:(NSIndexPath*)indexPath {
+ UICollectionReusableView* view = [super collectionView:collectionView
+ viewForSupplementaryElementOfKind:kind
+ atIndexPath:indexPath];
+
+ MDCCollectionViewTextCell* textCell =
+ base::mac::ObjCCast<MDCCollectionViewTextCell>(view);
+ if (textCell) {
+ textCell.textLabel.textColor = [[MDCPalette greyPalette] tint500];
+ }
+ return view;
+}
+
+#pragma mark UICollectionViewDelegate
+
+- (void)collectionView:(UICollectionView*)collectionView
+ didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
+ [super collectionView:collectionView didSelectItemAtIndexPath:indexPath];
+
+ NSInteger itemType =
+ [self.collectionViewModel itemTypeForIndexPath:indexPath];
+ switch (itemType) {
+ case ItemTypeSyncError:
+ [self fixSyncErrorIfPossible];
+ break;
+ case ItemTypeAccount: {
+ CollectionViewAccountItem* accountItem =
+ base::mac::ObjCCastStrict<CollectionViewAccountItem>(
+ [self.collectionViewModel itemAtIndexPath:indexPath]);
+ if (!accountItem.accessoryType) {
+ [self startSwitchAccountForIdentity:accountItem.chromeIdentity
+ postSignInAction:POST_SIGNIN_ACTION_NONE];
+ }
+ break;
+ }
+ case ItemTypeEncryption:
+ [self showEncryption];
+ break;
+ case ItemTypeManageSyncedData: {
+ GURL learnMoreUrl = google_util::AppendGoogleLocaleParam(
+ GURL(kSyncGoogleDashboardURL),
+ GetApplicationContext()->GetApplicationLocale());
+ base::scoped_nsobject<OpenUrlCommand> command(
+ [[OpenUrlCommand alloc] initWithURLFromChrome:learnMoreUrl]);
+ [command setTag:IDC_CLOSE_SETTINGS_AND_OPEN_URL];
+ [self chromeExecuteCommand:command];
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+#pragma mark MDCCollectionViewStylingDelegate
+
+- (CGFloat)collectionView:(UICollectionView*)collectionView
+ cellHeightAtIndexPath:(NSIndexPath*)indexPath {
+ CollectionViewItem* item =
+ [self.collectionViewModel itemAtIndexPath:indexPath];
+ switch (item.type) {
+ case ItemTypeAccount:
+ return MDCCellDefaultTwoLineHeight;
+ case ItemTypeSyncSwitch:
+ return [MDCCollectionViewCell
+ cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds)
+ forItem:item];
+ case ItemTypeSyncError:
+ return MDCCellDefaultOneLineWithAvatarHeight;
+ default:
+ return MDCCellDefaultOneLineHeight;
+ }
+}
+
+- (BOOL)collectionView:(UICollectionView*)collectionView
+ hidesInkViewAtIndexPath:(NSIndexPath*)indexPath {
+ NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath];
+ switch (type) {
+ case ItemTypeSyncSwitch:
+ case ItemTypeSyncEverything:
+ case ItemTypeSyncableDataType:
+ return YES;
+ default:
+ return NO;
+ }
+}
+
+#pragma mark - Actions
+
+- (void)changeSyncStatusToOn:(UISwitch*)sender {
+ if (self.navigationController.topViewController != self) {
+ // Workaround for timing issue when taping on a switch and the error or
+ // encryption cells. See crbug.com/647678.
+ return;
+ }
+
+ BOOL isOn = sender.isOn;
+ BOOL wasOn = _syncSetupService->IsSyncEnabled();
+ if (wasOn == isOn)
+ return;
+
+ base::AutoReset<BOOL> autoReset(&_ignoreSyncStateChanges, YES);
+ _syncSetupService->SetSyncEnabled(isOn);
+
+ BOOL isNowOn = _syncSetupService->IsSyncEnabled();
+ if (isNowOn == wasOn) {
+ DLOG(WARNING) << "Call to SetSyncEnabled(" << (isOn ? "YES" : "NO")
+ << ") failed.";
+ // This shouldn't happen, but in case there was an underlying sync problem,
+ // make sure the UI reflects sync's reality.
+ NSIndexPath* indexPath = [self.collectionViewModel
+ indexPathForItemType:ItemTypeSyncSwitch
+ sectionIdentifier:SectionIdentifierEnableSync];
+
+ SyncSwitchItem* item = base::mac::ObjCCastStrict<SyncSwitchItem>(
+ [self.collectionViewModel itemAtIndexPath:indexPath]);
+ item.on = isNowOn;
+ }
+ [self updateCollectionView];
+}
+
+- (void)fixSyncErrorIfPossible {
+ if (![self isSyncErrorFixableByUserAction] || ![self shouldDisplaySyncError])
+ return;
+
+ // Unrecoverable errors are special-cased to only do the signing out and back
+ // in from the Sync settings screen (where user interaction can safely be
+ // prevented).
+ if (_syncSetupService->GetSyncServiceState() ==
+ SyncSetupService::kSyncServiceUnrecoverableError) {
+ ChromeIdentity* authenticatedIdentity =
+ AuthenticationServiceFactory::GetForBrowserState(_browserState)
+ ->GetAuthenticatedIdentity();
+ [self startSwitchAccountForIdentity:authenticatedIdentity
+ postSignInAction:POST_SIGNIN_ACTION_START_SYNC];
+ return;
+ }
+
+ base::scoped_nsobject<GenericChromeCommand> command(
+ [ios_internal::sync::GetSyncCommandForBrowserState(_browserState)
+ retain]);
+ [self chromeExecuteCommand:command];
+}
+
+- (void)startSwitchAccountForIdentity:(ChromeIdentity*)identity
+ postSignInAction:(PostSignInAction)postSignInAction {
+ if (!_syncSetupService->IsSyncEnabled())
+ return;
+
+ _authenticationOperationInProgress = YES;
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:kSwitchAccountWillStartNotification
+ object:self];
+ [self preventUserInteraction];
+ DCHECK(!_authenticationFlow);
+ _authenticationFlow.reset([[AuthenticationFlow alloc]
+ initWithBrowserState:_browserState
+ identity:identity
+ shouldClearData:SHOULD_CLEAR_DATA_USER_CHOICE
+ postSignInAction:postSignInAction
+ presentingViewController:self]);
+
+ base::WeakNSObject<SyncSettingsCollectionViewController> weakSelf(self);
+ [_authenticationFlow startSignInWithCompletion:^(BOOL success) {
+ [weakSelf didSwitchAccountWithSuccess:success];
+ }];
+}
+
+- (void)didSwitchAccountWithSuccess:(BOOL)success {
+ _authenticationFlow.reset();
+ [self allowUserInteraction];
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:kSwitchAccountDidFinishNotification
+ object:self];
+ _authenticationOperationInProgress = NO;
+ if (![self popViewIfSignedOut]) {
+ // Only reload the view if it wasn't popped.
+ [self reloadData];
+ }
+}
+
+- (void)changeSyncEverythingStatusToOn:(UISwitch*)sender {
+ if (!_syncSetupService->IsSyncEnabled() ||
+ [self shouldDisableSettingsOnSyncError])
+ return;
+ BOOL isOn = sender.isOn;
+ BOOL wasOn = _syncSetupService->IsSyncingAllDataTypes();
+ if (wasOn == isOn)
+ return;
+
+ base::AutoReset<BOOL> autoReset(&_ignoreSyncStateChanges, YES);
+ _syncSetupService->SetSyncingAllDataTypes(isOn);
+
+ // Base the UI on the actual sync value, not the toggle.
+ BOOL isNowOn = _syncSetupService->IsSyncingAllDataTypes();
+ if (isNowOn == wasOn) {
+ DLOG(WARNING) << "Call to SetSyncingAllDataTypes(" << (isOn ? "YES" : "NO")
+ << ") failed";
+ // No change - there was a sync-level problem that didn't allow the change.
+ // This really shouldn't happen, but just in case, make sure the UI reflects
+ // sync's reality.
+ NSIndexPath* indexPath = [self.collectionViewModel
+ indexPathForItemType:ItemTypeSyncEverything
+ sectionIdentifier:SectionIdentifierSyncServices];
+ SyncSwitchItem* item = base::mac::ObjCCastStrict<SyncSwitchItem>(
+ [self.collectionViewModel itemAtIndexPath:indexPath]);
+ item.on = isNowOn;
+ }
+ [self updateCollectionView];
+}
+
+- (void)changeDataTypeSyncStatusToOn:(UISwitch*)sender {
+ if (!_syncSetupService->IsSyncEnabled() ||
+ _syncSetupService->IsSyncingAllDataTypes() ||
+ [self shouldDisableSettingsOnSyncError])
+ return;
+
+ BOOL isOn = sender.isOn;
+
+ SyncSwitchItem* syncSwitchItem =
+ base::mac::ObjCCastStrict<SyncSwitchItem>([self.collectionViewModel
+ itemAtIndexPath:[self indexPathForTag:sender.tag]]);
+ SyncSetupService::SyncableDatatype dataType =
+ (SyncSetupService::SyncableDatatype)syncSwitchItem.dataType;
+ syncer::ModelType modelType = _syncSetupService->GetModelType(dataType);
+
+ base::AutoReset<BOOL> autoReset(&_ignoreSyncStateChanges, YES);
+ _syncSetupService->SetDataTypeEnabled(modelType, isOn);
+}
+
+- (void)showEncryption {
+ browser_sync::ProfileSyncService* syncService =
+ IOSChromeProfileSyncServiceFactory::GetForBrowserState(_browserState);
+ if (!syncService->IsEngineInitialized() ||
+ !_syncSetupService->IsSyncEnabled() ||
+ [self shouldDisableSettingsOnSyncError])
+ return;
+
+ base::scoped_nsobject<UIViewController> controllerToPush;
+ // If there was a sync error, prompt the user to enter the passphrase.
+ // Otherwise, show the full encryption options.
+ if (syncService->IsPassphraseRequired()) {
+ controllerToPush.reset(
+ [[SyncEncryptionPassphraseCollectionViewController alloc]
+ initWithBrowserState:_browserState]);
+ } else {
+ controllerToPush.reset([[SyncEncryptionCollectionViewController alloc]
+ initWithBrowserState:_browserState]);
+ }
+ [self.navigationController pushViewController:controllerToPush animated:YES];
+}
+
+#pragma mark Updates
+
+- (void)updateCollectionView {
+ base::WeakNSObject<SyncSettingsCollectionViewController> weakSelf(self);
+ [self.collectionView performBatchUpdates:^{
+ [weakSelf updateCollectionViewInternal];
+ }
+ completion:nil];
+}
+
+- (void)updateCollectionViewInternal {
+ NSIndexPath* indexPath = [self.collectionViewModel
+ indexPathForItemType:ItemTypeSyncSwitch
+ sectionIdentifier:SectionIdentifierEnableSync];
+
+ SyncSwitchItem* syncItem = base::mac::ObjCCastStrict<SyncSwitchItem>(
+ [self.collectionViewModel itemAtIndexPath:indexPath]);
+ syncItem.on = _syncSetupService->IsSyncEnabled();
+ [self reconfigureCellsForItems:@[ syncItem ]
+ inSectionWithIdentifier:SectionIdentifierEnableSync];
+
+ // Update Sync Accounts section.
+ if ([self hasAccountsSection]) {
+ NSInteger section = [self.collectionViewModel
+ sectionForSectionIdentifier:SectionIdentifierSyncAccounts];
+ NSInteger itemsCount =
+ [self.collectionViewModel numberOfItemsInSection:section];
+ NSMutableArray* accountsToReconfigure =
+ [[[NSMutableArray alloc] init] autorelease];
+ for (NSInteger item = 0; item < itemsCount; ++item) {
+ NSIndexPath* indexPath = [self.collectionViewModel
+ indexPathForItemType:ItemTypeAccount
+ sectionIdentifier:SectionIdentifierSyncAccounts
+ atIndex:item];
+ CollectionViewAccountItem* accountItem =
+ base::mac::ObjCCastStrict<CollectionViewAccountItem>(
+ [self.collectionViewModel itemAtIndexPath:indexPath]);
+ accountItem.enabled = _syncSetupService->IsSyncEnabled();
+ [accountsToReconfigure addObject:accountItem];
+ }
+ [self reconfigureCellsForItems:accountsToReconfigure
+ inSectionWithIdentifier:SectionIdentifierSyncAccounts];
+ }
+
+ // Update Sync Services section.
+ indexPath = [self.collectionViewModel
+ indexPathForItemType:ItemTypeSyncEverything
+ sectionIdentifier:SectionIdentifierSyncServices];
+ SyncSwitchItem* syncEverythingItem =
+ base::mac::ObjCCastStrict<SyncSwitchItem>(
+ [self.collectionViewModel itemAtIndexPath:indexPath]);
+ syncEverythingItem.on = _syncSetupService->IsSyncingAllDataTypes();
+ syncEverythingItem.enabled = [self shouldSyncEverythingItemBeEnabled];
+ [self reconfigureCellsForItems:@[ syncEverythingItem ]
+ inSectionWithIdentifier:SectionIdentifierSyncServices];
+
+ NSInteger section = [self.collectionViewModel
+ sectionForSectionIdentifier:SectionIdentifierSyncServices];
+ NSInteger itemsCount =
+ [self.collectionViewModel numberOfItemsInSection:section];
+ // Syncable data types cells are offset by the Sync Everything cell.
+ NSMutableArray* switchsToReconfigure =
+ [[[NSMutableArray alloc] init] autorelease];
+ for (NSInteger item = 1; item < itemsCount; ++item) {
+ NSUInteger index = item - 1;
+ NSIndexPath* indexPath = [self.collectionViewModel
+ indexPathForItemType:ItemTypeSyncableDataType
+ sectionIdentifier:SectionIdentifierSyncServices
+ atIndex:index];
+ SyncSwitchItem* syncSwitchItem = base::mac::ObjCCastStrict<SyncSwitchItem>(
+ [self.collectionViewModel itemAtIndexPath:indexPath]);
+ SyncSetupService::SyncableDatatype dataType =
+ (SyncSetupService::SyncableDatatype)syncSwitchItem.dataType;
+ syncer::ModelType modelType = _syncSetupService->GetModelType(dataType);
+ syncSwitchItem.on = _syncSetupService->IsDataTypeEnabled(modelType);
+ syncSwitchItem.enabled = [self shouldSyncableItemsBeEnabled];
+ [switchsToReconfigure addObject:syncSwitchItem];
+ }
+ [self reconfigureCellsForItems:switchsToReconfigure
+ inSectionWithIdentifier:SectionIdentifierSyncServices];
+
+ // Update Encryption cell.
+ [self updateEncryptionCell];
+
+ // Add/Remove the Sync Error. This is the only update that can change index
+ // paths. It is done last because self.collectionViewModel isn't aware of
+ // the performBatchUpdates:completion: order of update/remove/delete.
+ [self updateSyncError];
+}
+
+- (void)updateSyncError {
+ BOOL shouldDisplayError = [self shouldDisplaySyncError];
+ BOOL isDisplayingError =
+ [self.collectionViewModel hasItemForItemType:ItemTypeSyncError
+ sectionIdentifier:SectionIdentifierSyncError];
+ if (shouldDisplayError && !isDisplayingError) {
+ [self.collectionViewModel
+ insertSectionWithIdentifier:SectionIdentifierSyncError
+ atIndex:0];
+ [self.collectionViewModel addItem:[self syncErrorItem]
+ toSectionWithIdentifier:SectionIdentifierSyncError];
+ NSInteger section = [self.collectionViewModel
+ sectionForSectionIdentifier:SectionIdentifierSyncError];
+ [self.collectionView insertSections:[NSIndexSet indexSetWithIndex:section]];
+ } else if (!shouldDisplayError && isDisplayingError) {
+ NSInteger section = [self.collectionViewModel
+ sectionForSectionIdentifier:SectionIdentifierSyncError];
+ [self.collectionViewModel
+ removeSectionWithIdentifier:SectionIdentifierSyncError];
+ [self.collectionView deleteSections:[NSIndexSet indexSetWithIndex:section]];
+ }
+}
+
+- (void)updateEncryptionCell {
+ BOOL shouldDisplayEncryptionError = [self shouldDisplayEncryptionError];
+ NSIndexPath* indexPath = [self.collectionViewModel
+ indexPathForItemType:ItemTypeEncryption
+ sectionIdentifier:SectionIdentifierEncryptionAndFooter];
+ TextAndErrorItem* item = base::mac::ObjCCastStrict<TextAndErrorItem>(
+ [self.collectionViewModel itemAtIndexPath:indexPath]);
+ item.shouldDisplayError = shouldDisplayEncryptionError;
+ item.enabled = [self shouldEncryptionItemBeEnabled];
+ [self reconfigureCellsForItems:@[ item ]
+ inSectionWithIdentifier:SectionIdentifierEncryptionAndFooter];
+}
+
+- (void)updateAccountItem:(CollectionViewAccountItem*)item
+ withIdentity:(ChromeIdentity*)identity {
+ item.image = [_avatarCache resizedAvatarForIdentity:identity];
+ item.text = identity.userEmail;
+ item.chromeIdentity = identity;
+}
+
+#pragma mark Helpers
+
+- (BOOL)hasAccountsSection {
+ OAuth2TokenService* tokenService =
+ OAuth2TokenServiceFactory::GetForBrowserState(_browserState);
+ return _allowSwitchSyncAccount && tokenService->GetAccounts().size() > 1;
+}
+
+- (BOOL)shouldDisplaySyncError {
+ SyncSetupService::SyncServiceState state =
+ _syncSetupService->GetSyncServiceState();
+ return state != SyncSetupService::kNoSyncServiceError;
+}
+
+- (BOOL)shouldDisableSettingsOnSyncError {
+ SyncSetupService::SyncServiceState state =
+ _syncSetupService->GetSyncServiceState();
+ return state != SyncSetupService::kNoSyncServiceError &&
+ state != SyncSetupService::kSyncServiceNeedsPassphrase;
+}
+
+- (BOOL)shouldDisplayEncryptionError {
+ return _syncSetupService->GetSyncServiceState() ==
+ SyncSetupService::kSyncServiceNeedsPassphrase;
+}
+
+- (BOOL)isSyncErrorFixableByUserAction {
+ SyncSetupService::SyncServiceState state =
+ _syncSetupService->GetSyncServiceState();
+ return state == SyncSetupService::kSyncServiceNeedsPassphrase ||
+ state == SyncSetupService::kSyncServiceSignInNeedsUpdate ||
+ state == SyncSetupService::kSyncServiceUnrecoverableError;
+}
+
+- (int)titleIdForSyncableDataType:(SyncSetupService::SyncableDatatype)datatype {
+ switch (datatype) {
+ case SyncSetupService::kSyncBookmarks:
+ return IDS_SYNC_DATATYPE_BOOKMARKS;
+ case SyncSetupService::kSyncOmniboxHistory:
+ return IDS_SYNC_DATATYPE_TYPED_URLS;
+ case SyncSetupService::kSyncPasswords:
+ return IDS_SYNC_DATATYPE_PASSWORDS;
+ case SyncSetupService::kSyncOpenTabs:
+ return IDS_SYNC_DATATYPE_TABS;
+ case SyncSetupService::kSyncAutofill:
+ return IDS_SYNC_DATATYPE_AUTOFILL;
+ case SyncSetupService::kSyncPreferences:
+ return IDS_SYNC_DATATYPE_PREFERENCES;
+ case SyncSetupService::kSyncReadingList:
+ return IDS_SYNC_DATATYPE_READING_LIST;
+ case SyncSetupService::kNumberOfSyncableDatatypes:
+ NOTREACHED();
+ }
+ return 0;
+}
+
+- (BOOL)shouldEncryptionItemBeEnabled {
+ browser_sync::ProfileSyncService* syncService =
+ IOSChromeProfileSyncServiceFactory::GetForBrowserState(_browserState);
+ return (syncService->IsEngineInitialized() &&
+ _syncSetupService->IsSyncEnabled() &&
+ ![self shouldDisableSettingsOnSyncError]);
+}
+
+- (BOOL)shouldSyncEverythingItemBeEnabled {
+ return (_syncSetupService->IsSyncEnabled() &&
+ ![self shouldDisableSettingsOnSyncError]);
+}
+
+- (BOOL)shouldSyncableItemsBeEnabled {
+ return (!_syncSetupService->IsSyncingAllDataTypes() &&
+ _syncSetupService->IsSyncEnabled() &&
+ ![self shouldDisableSettingsOnSyncError]);
+}
+
+- (NSInteger)tagForIndexPath:(NSIndexPath*)indexPath {
+ DCHECK(indexPath.section ==
+ [self.collectionViewModel
+ sectionForSectionIdentifier:SectionIdentifierSyncServices]);
+ NSInteger index =
+ [self.collectionViewModel indexInItemTypeForIndexPath:indexPath];
+ return index + kTagShift;
+}
+
+- (NSIndexPath*)indexPathForTag:(NSInteger)shiftedTag {
+ NSInteger unshiftedTag = shiftedTag - kTagShift;
+ return [self.collectionViewModel
+ indexPathForItemType:ItemTypeSyncableDataType
+ sectionIdentifier:SectionIdentifierSyncServices
+ atIndex:unshiftedTag];
+}
+
+#pragma mark SyncObserverModelBridge
+
+- (void)onSyncStateChanged {
+ if (_ignoreSyncStateChanges || _authenticationOperationInProgress) {
+ return;
+ }
+ [self updateCollectionView];
+}
+
+#pragma mark OAuth2TokenServiceObserverBridgeDelegate
+
+- (void)onEndBatchChanges {
+ if (_authenticationOperationInProgress) {
+ return;
+ }
+ if (![self popViewIfSignedOut]) {
+ // Only reload the view if it wasn't popped.
+ [self reloadData];
+ }
+}
+
+#pragma mark SettingsControllerProtocol callbacks
+
+- (void)settingsWillBeDismissed {
+ [self stopBrowserStateServiceObservers];
+ [_authenticationFlow cancelAndDismiss];
+}
+
+#pragma mark - ChromeIdentityServiceObserver
+
+- (void)onProfileUpdate:(ChromeIdentity*)identity {
+ CollectionViewAccountItem* item =
+ base::mac::ObjCCastStrict<CollectionViewAccountItem>(
+ [_identityMap objectForKey:identity.gaiaID]);
+ [self updateAccountItem:item withIdentity:identity];
+ [self reconfigureCellsForItems:@[ item ]
+ inSectionWithIdentifier:SectionIdentifierSyncAccounts];
+}
+
+- (void)onChromeIdentityServiceWillBeDestroyed {
+ _identityServiceObserver.reset();
+}
+
+@end

Powered by Google App Engine
This is Rietveld 408576698