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

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

Issue 2589583003: Upstream Chrome on iOS source code [7/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/accounts_collection_view_controller.h"
6
7 #import "base/ios/weak_nsobject.h"
8 #import "base/mac/foundation_util.h"
9 #include "base/mac/scoped_nsobject.h"
10 #include "base/metrics/user_metrics.h"
11 #include "base/strings/sys_string_conversions.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "components/browser_sync/profile_sync_service.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/core/browser/signin_manager.h"
17 #include "components/signin/ios/browser/oauth2_token_service_observer_bridge.h"
18 #include "components/strings/grit/components_strings.h"
19 #import "ios/chrome/browser/browser_state/chrome_browser_state.h"
20 #include "ios/chrome/browser/signin/account_tracker_service_factory.h"
21 #import "ios/chrome/browser/signin/authentication_service.h"
22 #import "ios/chrome/browser/signin/authentication_service_factory.h"
23 #include "ios/chrome/browser/signin/chrome_identity_service_observer_bridge.h"
24 #include "ios/chrome/browser/signin/oauth2_token_service_factory.h"
25 #include "ios/chrome/browser/signin/signin_manager_factory.h"
26 #include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h"
27 #include "ios/chrome/browser/sync/sync_observer_bridge.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/alert_coordinator/alert_coordinator.h"
31 #import "ios/chrome/browser/ui/authentication/signin_interaction_controller.h"
32 #import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrom e.h"
33 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_account_ite m.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 #import "ios/chrome/browser/ui/commands/generic_chrome_command.h"
39 #include "ios/chrome/browser/ui/commands/ios_command_ids.h"
40 #import "ios/chrome/browser/ui/commands/open_url_command.h"
41 #import "ios/chrome/browser/ui/icons/chrome_icon.h"
42 #import "ios/chrome/browser/ui/settings/cells/account_control_item.h"
43 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
44 #import "ios/chrome/browser/ui/settings/sync_settings_collection_view_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 #include "ios/chrome/grit/ios_chromium_strings.h"
48 #include "ios/chrome/grit/ios_strings.h"
49 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
50 #include "ios/public/provider/chrome/browser/images/branded_image_provider.h"
51 #import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
52 #import "ios/public/provider/chrome/browser/signin/chrome_identity_browser_opene r.h"
53 #import "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
54 #import "net/base/mac/url_conversions.h"
55 #include "ui/base/l10n/l10n_util_mac.h"
56
57 NSString* const kSettingsAccountsId = @"kSettingsAccountsId";
58 NSString* const kSettingsHeaderId = @"kSettingsHeaderId";
59 NSString* const kSettingsAccountsSignoutCellId =
60 @"kSettingsAccountsSignoutCellId";
61 NSString* const kSettingsAccountsSyncCellId = @"kSettingsAccountsSyncCellId";
62
63 namespace {
64
65 typedef NS_ENUM(NSInteger, SectionIdentifier) {
66 SectionIdentifierAccounts = kSectionIdentifierEnumZero,
67 SectionIdentifierSync,
68 SectionIdentifierSignOut,
69 };
70
71 typedef NS_ENUM(NSInteger, ItemType) {
72 ItemTypeAccount = kItemTypeEnumZero,
73 ItemTypeAddAccount,
74 ItemTypeSync,
75 ItemTypeGoogleActivityControls,
76 ItemTypeSignOut,
77 ItemTypeHeader,
78 };
79
80 } // namespace
81
82 @interface AccountsCollectionViewController ()<
83 ChromeIdentityServiceObserver,
84 ChromeIdentityBrowserOpener,
85 OAuth2TokenServiceObserverBridgeDelegate,
86 SyncObserverModelBridge> {
87 ios::ChromeBrowserState* _browserState; // weak
88 BOOL _closeSettingsOnAddAccount;
89 std::unique_ptr<SyncObserverBridge> _syncObserver;
90 std::unique_ptr<OAuth2TokenServiceObserverBridge> _tokenServiceObserver;
91 base::scoped_nsobject<SigninInteractionController>
92 _signinInteractionController;
93 // Modal alert for sign out.
94 base::scoped_nsobject<AlertCoordinator> _alertCoordinator;
95 // Whether an authentication operation is in progress (e.g switch accounts,
96 // sign out).
97 BOOL _authenticationOperationInProgress;
98 // Whether the view controller is currently being dismissed and new dismiss
99 // requests should be ignored.
100 BOOL _isBeingDismissed;
101 base::WeakNSObject<UIViewController> _settingsDetails;
102 base::scoped_nsobject<ResizedAvatarCache> _avatarCache;
103 std::unique_ptr<ChromeIdentityServiceObserverBridge> _identityServiceObserver;
104
105 // Enable lookup of item corresponding to a given identity GAIA ID string.
106 base::scoped_nsobject<NSDictionary<NSString*, CollectionViewItem*>>
107 _identityMap;
108 }
109
110 // Stops observing browser state services. This is required during the shutdown
111 // phase to avoid observing services for a browser state that is being killed.
112 - (void)stopBrowserStateServiceObservers;
113
114 @end
115
116 @implementation AccountsCollectionViewController
117
118 - (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
119 closeSettingsOnAddAccount:(BOOL)closeSettingsOnAddAccount {
120 DCHECK(browserState);
121 DCHECK(!browserState->IsOffTheRecord());
122 self = [super initWithStyle:CollectionViewControllerStyleAppBar];
123 if (self) {
124 _browserState = browserState;
125 _closeSettingsOnAddAccount = closeSettingsOnAddAccount;
126 browser_sync::ProfileSyncService* syncService =
127 IOSChromeProfileSyncServiceFactory::GetForBrowserState(_browserState);
128 _syncObserver.reset(new SyncObserverBridge(self, syncService));
129 _tokenServiceObserver.reset(new OAuth2TokenServiceObserverBridge(
130 OAuth2TokenServiceFactory::GetForBrowserState(_browserState), self));
131 [[NSNotificationCenter defaultCenter]
132 addObserver:self
133 selector:@selector(willStartSwitchAccount)
134 name:kSwitchAccountWillStartNotification
135 object:nil];
136 [[NSNotificationCenter defaultCenter]
137 addObserver:self
138 selector:@selector(didFinishSwitchAccount)
139 name:kSwitchAccountDidFinishNotification
140 object:nil];
141 self.collectionViewAccessibilityIdentifier = kSettingsAccountsId;
142 _avatarCache.reset([[ResizedAvatarCache alloc] init]);
143 _identityServiceObserver.reset(
144 new ChromeIdentityServiceObserverBridge(self));
145 [self loadModel];
146 }
147
148 return self;
149 }
150
151 - (void)dealloc {
152 [[NSNotificationCenter defaultCenter] removeObserver:self];
153 [super dealloc];
154 }
155
156 - (void)stopBrowserStateServiceObservers {
157 _tokenServiceObserver.reset();
158 _syncObserver.reset();
159 }
160
161 #pragma mark - SettingsControllerProtocol
162
163 - (void)settingsWillBeDismissed {
164 [_signinInteractionController cancel];
165 [_alertCoordinator stop];
166 [self stopBrowserStateServiceObservers];
167 }
168
169 #pragma mark - SettingsRootCollectionViewController
170
171 - (void)reloadData {
172 if (![self authService]->IsAuthenticated()) {
173 // This accounts collection view will be popped or dismissed when the user
174 // is signed out. Avoid reloading it in that case as that would lead to an
175 // empty collection view.
176 return;
177 }
178 [super reloadData];
179 }
180
181 - (void)loadModel {
182 // Update the title with the name with the currently signed-in account.
183 ChromeIdentity* authenticatedIdentity =
184 [self authService]->GetAuthenticatedIdentity();
185 NSString* title = nil;
186 if (authenticatedIdentity) {
187 title = [authenticatedIdentity userFullName];
188 if (!title) {
189 // TODO(crbug.com/656994): Figure how to handle unnamed account.
190 title = @"Unnamed account";
191 }
192 }
193 self.title = title;
194
195 [super loadModel];
196
197 if (![self authService]->IsAuthenticated())
198 return;
199
200 CollectionViewModel* model = self.collectionViewModel;
201
202 NSMutableDictionary<NSString*, CollectionViewItem*>* mutableIdentityMap =
203 [[[NSMutableDictionary alloc] init] autorelease];
204
205 // Account cells.
206 ProfileOAuth2TokenService* oauth2_service =
207 OAuth2TokenServiceFactory::GetForBrowserState(_browserState);
208 AccountTrackerService* accountTracker =
209 ios::AccountTrackerServiceFactory::GetForBrowserState(_browserState);
210 [model addSectionWithIdentifier:SectionIdentifierAccounts];
211 [model setHeader:[self header]
212 forSectionWithIdentifier:SectionIdentifierAccounts];
213 for (const std::string& account_id : oauth2_service->GetAccounts()) {
214 AccountInfo account = accountTracker->GetAccountInfo(account_id);
215 ChromeIdentity* identity = ios::GetChromeBrowserProvider()
216 ->GetChromeIdentityService()
217 ->GetIdentityWithGaiaID(account.gaia);
218 CollectionViewItem* item = [self accountItem:identity];
219 [model addItem:item toSectionWithIdentifier:SectionIdentifierAccounts];
220
221 [mutableIdentityMap setObject:item forKey:identity.gaiaID];
222 }
223 _identityMap.reset([mutableIdentityMap retain]);
224
225 [model addItem:[self addAccountItem]
226 toSectionWithIdentifier:SectionIdentifierAccounts];
227
228 // Sync and Google Activity section.
229 [model addSectionWithIdentifier:SectionIdentifierSync];
230 [model addItem:[self syncItem] toSectionWithIdentifier:SectionIdentifierSync];
231 [model addItem:[self googleActivityControlsItem]
232 toSectionWithIdentifier:SectionIdentifierSync];
233
234 // Sign out section.
235 [model addSectionWithIdentifier:SectionIdentifierSignOut];
236 [model addItem:[self signOutItem]
237 toSectionWithIdentifier:SectionIdentifierSignOut];
238 }
239
240 #pragma mark - Model objects
241
242 - (CollectionViewItem*)header {
243 CollectionViewTextItem* header = [
244 [[CollectionViewTextItem alloc] initWithType:ItemTypeHeader] autorelease];
245 header.text = l10n_util::GetNSString(IDS_IOS_OPTIONS_ACCOUNTS_DESCRIPTION);
246 header.accessibilityIdentifier = kSettingsHeaderId;
247 return header;
248 }
249
250 - (CollectionViewItem*)accountItem:(ChromeIdentity*)identity {
251 CollectionViewAccountItem* item = [[[CollectionViewAccountItem alloc]
252 initWithType:ItemTypeAccount] autorelease];
253 [self updateAccountItem:item withIdentity:identity];
254 return item;
255 }
256
257 - (void)updateAccountItem:(CollectionViewAccountItem*)item
258 withIdentity:(ChromeIdentity*)identity {
259 item.image = [_avatarCache resizedAvatarForIdentity:identity];
260 item.text = identity.userEmail;
261 item.chromeIdentity = identity;
262 item.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator;
263 }
264
265 - (CollectionViewItem*)addAccountItem {
266 CollectionViewAccountItem* item = [[[CollectionViewAccountItem alloc]
267 initWithType:ItemTypeAddAccount] autorelease];
268 item.text =
269 l10n_util::GetNSString(IDS_IOS_OPTIONS_ACCOUNTS_ADD_ACCOUNT_BUTTON);
270 item.image = [UIImage imageNamed:@"settings_accounts_add_account"];
271 return item;
272 }
273
274 - (CollectionViewItem*)syncItem {
275 AccountControlItem* item =
276 [[[AccountControlItem alloc] initWithType:ItemTypeSync] autorelease];
277 item.text = l10n_util::GetNSString(IDS_IOS_OPTIONS_ACCOUNTS_SYNC_TITLE);
278 item.accessibilityIdentifier = kSettingsAccountsSyncCellId;
279 [self updateSyncItem:item];
280 item.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator;
281 return item;
282 }
283
284 - (void)updateSyncItem:(AccountControlItem*)syncItem {
285 SyncSetupService* syncSetupService =
286 SyncSetupServiceFactory::GetForBrowserState(_browserState);
287 if (!syncSetupService->HasFinishedInitialSetup()) {
288 syncItem.image = [UIImage imageNamed:@"settings_sync"];
289 syncItem.detailText =
290 l10n_util::GetNSString(IDS_IOS_SYNC_SETUP_IN_PROGRESS);
291 syncItem.shouldDisplayError = NO;
292 return;
293 }
294
295 ChromeIdentity* identity = [self authService]->GetAuthenticatedIdentity();
296 bool hasSyncError = !ios_internal::sync::IsTransientSyncError(
297 syncSetupService->GetSyncServiceState());
298 bool hasMDMError = [self authService]->HasCachedMDMErrorForIdentity(identity);
299 if (hasSyncError || hasMDMError) {
300 syncItem.image = [UIImage imageNamed:@"settings_error"];
301 syncItem.detailText =
302 ios_internal::sync::GetSyncErrorDescriptionForBrowserState(
303 _browserState);
304 syncItem.shouldDisplayError = YES;
305 } else {
306 syncItem.image = [UIImage imageNamed:@"settings_sync"];
307 syncItem.detailText =
308 syncSetupService->IsSyncEnabled()
309 ? l10n_util::GetNSStringF(
310 IDS_IOS_SIGN_IN_TO_CHROME_SETTING_SYNCING,
311 base::SysNSStringToUTF16([identity userEmail]))
312 : l10n_util::GetNSString(IDS_IOS_OPTIONS_ACCOUNTS_SYNC_IS_OFF);
313 syncItem.shouldDisplayError = NO;
314 }
315 }
316
317 - (CollectionViewItem*)googleActivityControlsItem {
318 AccountControlItem* item = [[[AccountControlItem alloc]
319 initWithType:ItemTypeGoogleActivityControls] autorelease];
320 item.text = l10n_util::GetNSString(IDS_IOS_OPTIONS_ACCOUNTS_GOOGLE_TITLE);
321 item.detailText =
322 l10n_util::GetNSString(IDS_IOS_OPTIONS_ACCOUNTS_GOOGLE_DESCRIPTION);
323 item.image = ios::GetChromeBrowserProvider()
324 ->GetBrandedImageProvider()
325 ->GetAccountsListActivityControlsImage();
326 item.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator;
327 return item;
328 }
329
330 - (CollectionViewItem*)signOutItem {
331 CollectionViewTextItem* item = [[[CollectionViewTextItem alloc]
332 initWithType:ItemTypeSignOut] autorelease];
333 item.text = l10n_util::GetNSString(IDS_IOS_OPTIONS_ACCOUNTS_SIGNOUT);
334 item.accessibilityTraits |= UIAccessibilityTraitButton;
335 item.accessibilityIdentifier = kSettingsAccountsSignoutCellId;
336 return item;
337 }
338
339 #pragma mark - UICollectionViewDelegate
340
341 - (void)collectionView:(UICollectionView*)collectionView
342 didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
343 [super collectionView:collectionView didSelectItemAtIndexPath:indexPath];
344
345 NSInteger itemType =
346 [self.collectionViewModel itemTypeForIndexPath:indexPath];
347
348 switch (itemType) {
349 case ItemTypeAccount: {
350 CollectionViewAccountItem* item =
351 base::mac::ObjCCastStrict<CollectionViewAccountItem>(
352 [self.collectionViewModel itemAtIndexPath:indexPath]);
353 DCHECK(item.chromeIdentity);
354 [self showAccountDetails:item.chromeIdentity];
355 break;
356 }
357 case ItemTypeAddAccount:
358 [self showAddAccount];
359 break;
360 case ItemTypeSync:
361 [self showSyncSettings];
362 break;
363 case ItemTypeGoogleActivityControls:
364 [self showGoogleActivitySettings];
365 break;
366 case ItemTypeSignOut:
367 [self showDisconnect];
368 break;
369 default:
370 break;
371 }
372 }
373
374 #pragma mark - UICollectionViewDataSource
375
376 - (UICollectionReusableView*)collectionView:(UICollectionView*)collectionView
377 viewForSupplementaryElementOfKind:(NSString*)kind
378 atIndexPath:(NSIndexPath*)indexPath {
379 UICollectionReusableView* view = [super collectionView:collectionView
380 viewForSupplementaryElementOfKind:kind
381 atIndexPath:indexPath];
382 MDCCollectionViewTextCell* textCell =
383 base::mac::ObjCCast<MDCCollectionViewTextCell>(view);
384 if (textCell) {
385 textCell.textLabel.textColor = [[MDCPalette greyPalette] tint500];
386 }
387 return view;
388 }
389
390 #pragma mark - MDCCollectionViewStylingDelegate
391
392 - (CGFloat)collectionView:(UICollectionView*)collectionView
393 cellHeightAtIndexPath:(NSIndexPath*)indexPath {
394 CollectionViewItem* item =
395 [self.collectionViewModel itemAtIndexPath:indexPath];
396 if (item.type == ItemTypeGoogleActivityControls ||
397 item.type == ItemTypeSync) {
398 return [MDCCollectionViewCell
399 cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds)
400 forItem:item];
401 } else if (item.type == ItemTypeSignOut) {
402 return MDCCellDefaultOneLineHeight;
403 }
404 return MDCCellDefaultTwoLineHeight;
405 }
406
407 #pragma mark - SyncObserverModelBridge
408
409 - (void)onSyncStateChanged {
410 if (![self authService]->IsAuthenticated()) {
411 // Ignore sync state changed notification if signed out.
412 return;
413 }
414
415 NSIndexPath* index =
416 [self.collectionViewModel indexPathForItemType:ItemTypeSync
417 sectionIdentifier:SectionIdentifierSync];
418
419 CollectionViewModel* model = self.collectionViewModel;
420 if ([model numberOfSections] > index.section &&
421 [model numberOfItemsInSection:index.section] > index.row) {
422 AccountControlItem* item = base::mac::ObjCCastStrict<AccountControlItem>(
423 [model itemAtIndexPath:index]);
424 [self updateSyncItem:item];
425 [self.collectionView reloadItemsAtIndexPaths:@[ index ]];
426 }
427 }
428
429 #pragma mark - OAuth2TokenServiceObserverBridgeDelegate
430
431 - (void)onEndBatchChanges {
432 [self reloadData];
433 [self popViewIfSignedOut];
434 if (![self authService]->IsAuthenticated() && _settingsDetails) {
435 [_settingsDetails dismissViewControllerAnimated:YES completion:nil];
436 _settingsDetails.reset();
437 }
438 }
439
440 #pragma mark - Sync and Activity Controls
441
442 - (void)showSyncSettings {
443 if ([_alertCoordinator isVisible])
444 return;
445
446 if ([self authService]->ShowMDMErrorDialogForIdentity(
447 [self authService]->GetAuthenticatedIdentity())) {
448 // If there is an MDM error for the synced identity, show it instead.
449 return;
450 }
451
452 base::scoped_nsobject<UIViewController> controllerToPush(
453 [[SyncSettingsCollectionViewController alloc]
454 initWithBrowserState:_browserState
455 allowSwitchSyncAccount:YES]);
456 [self.navigationController pushViewController:controllerToPush animated:YES];
457 }
458
459 - (void)showGoogleActivitySettings {
460 if ([_alertCoordinator isVisible])
461 return;
462 base::RecordAction(base::UserMetricsAction(
463 "Signin_AccountSettings_GoogleActivityControlsClicked"));
464 base::scoped_nsobject<UINavigationController> settingsDetails(
465 ios::GetChromeBrowserProvider()
466 ->GetChromeIdentityService()
467 ->NewWebAndAppSettingDetails(
468 [self authService]->GetAuthenticatedIdentity(), self));
469 UIImage* closeIcon = [ChromeIcon closeIcon];
470 SEL action = @selector(closeGoogleActivitySettings:);
471 [settingsDetails.get().topViewController navigationItem].leftBarButtonItem =
472 [ChromeIcon templateBarButtonItemWithImage:closeIcon
473 target:self
474 action:action];
475 [self presentViewController:settingsDetails animated:YES completion:nil];
476
477 // Keep a weak reference on the settings details, to be able to dismiss it
478 // when the primary account is removed.
479 _settingsDetails.reset(settingsDetails);
480 }
481
482 - (void)closeGoogleActivitySettings:(id)sender {
483 DCHECK(_settingsDetails);
484 [self dismissViewControllerAnimated:YES completion:nil];
485 }
486
487 #pragma mark - Authentication operations
488
489 - (void)showAddAccount {
490 if ([_alertCoordinator isVisible])
491 return;
492 if (_signinInteractionController) {
493 // Ignore this user action if there is already an add account operation
494 // in-progress.
495 return;
496 }
497 _signinInteractionController.reset([[SigninInteractionController alloc]
498 initWithBrowserState:_browserState
499 presentingViewController:self.navigationController
500 isPresentedOnSettings:YES
501 signInAccessPoint:signin_metrics::AccessPoint::
502 ACCESS_POINT_SETTINGS]);
503
504 // |_authenticationOperationInProgress| is reset when the signin interaction
505 // controller is dismissed.
506 _authenticationOperationInProgress = YES;
507 base::WeakNSObject<AccountsCollectionViewController> weakSelf(self);
508 [_signinInteractionController addAccountWithCompletion:^(BOOL success) {
509 [weakSelf handleDidAddAccount:success];
510 }
511 viewController:self];
512 }
513
514 - (void)handleDidAddAccount:(BOOL)success {
515 _signinInteractionController.reset();
516 [self handleAuthenticationOperationDidFinish];
517 if (success && _closeSettingsOnAddAccount) {
518 base::scoped_nsobject<GenericChromeCommand> closeSettingsCommand(
519 [[GenericChromeCommand alloc] initWithTag:IDC_CLOSE_SETTINGS]);
520 [self chromeExecuteCommand:closeSettingsCommand];
521 }
522 }
523
524 - (void)showAccountDetails:(ChromeIdentity*)identity {
525 if ([_alertCoordinator isVisible])
526 return;
527 base::scoped_nsobject<UIViewController> accountDetails(
528 ios::GetChromeBrowserProvider()
529 ->GetChromeIdentityService()
530 ->NewAccountDetails(identity, self));
531 if (!accountDetails) {
532 // Failed to create a new account details. Ignored.
533 return;
534 }
535 [self presentViewController:accountDetails animated:YES completion:nil];
536
537 // Keep a weak reference on the account details, to be able to dismiss it
538 // when the primary account is removed.
539 _settingsDetails.reset(accountDetails);
540 }
541
542 - (void)showDisconnect {
543 if (_authenticationOperationInProgress || [_alertCoordinator isVisible] ||
544 self != [self.navigationController topViewController]) {
545 // An action is already in progress, ignore user's request.
546 return;
547 }
548
549 NSString* title;
550 NSString* message;
551 NSString* continueButtonTitle;
552 if ([self authService]->IsAuthenticatedIdentityManaged()) {
553 std::string hosted_domain =
554 ios::SigninManagerFactory::GetForBrowserState(_browserState)
555 ->GetAuthenticatedAccountInfo()
556 .hosted_domain;
557 title = l10n_util::GetNSString(IDS_IOS_MANAGED_DISCONNECT_DIALOG_TITLE);
558 message = l10n_util::GetNSStringF(IDS_IOS_MANAGED_DISCONNECT_DIALOG_INFO,
559 base::UTF8ToUTF16(hosted_domain));
560 continueButtonTitle =
561 l10n_util::GetNSString(IDS_IOS_MANAGED_DISCONNECT_DIALOG_ACCEPT);
562 } else {
563 title = l10n_util::GetNSString(IDS_IOS_DISCONNECT_DIALOG_TITLE);
564 message = l10n_util::GetNSString(IDS_IOS_DISCONNECT_DIALOG_INFO_MOBILE);
565 continueButtonTitle = l10n_util::GetNSString(
566 IDS_IOS_DISCONNECT_DIALOG_CONTINUE_BUTTON_MOBILE);
567 }
568 _alertCoordinator.reset([[AlertCoordinator alloc]
569 initWithBaseViewController:self
570 title:title
571 message:message]);
572
573 [_alertCoordinator addItemWithTitle:l10n_util::GetNSString(IDS_CANCEL)
574 action:nil
575 style:UIAlertActionStyleCancel];
576 base::WeakNSObject<AccountsCollectionViewController> weakSelf(self);
577 [_alertCoordinator addItemWithTitle:continueButtonTitle
578 action:^{
579 [weakSelf handleDisconnect];
580 }
581 style:UIAlertActionStyleDefault];
582 [_alertCoordinator start];
583 }
584
585 - (void)handleDisconnect {
586 AuthenticationService* authService = [self authService];
587 if (authService->IsAuthenticated()) {
588 _authenticationOperationInProgress = YES;
589 [self preventUserInteraction];
590 authService->SignOut(signin_metrics::USER_CLICKED_SIGNOUT_SETTINGS, ^{
591 [self allowUserInteraction];
592 _authenticationOperationInProgress = NO;
593 [base::mac::ObjCCastStrict<SettingsNavigationController>(
594 self.navigationController)
595 popViewControllerOrCloseSettingsAnimated:YES];
596 });
597 }
598 }
599
600 // Sets |_authenticationOperationInProgress| to NO and pops this accounts
601 // collection view controller if the user is signed out.
602 - (void)handleAuthenticationOperationDidFinish {
603 DCHECK(_authenticationOperationInProgress);
604 _authenticationOperationInProgress = NO;
605 [self popViewIfSignedOut];
606 }
607
608 - (void)popViewIfSignedOut {
609 if ([self authService]->IsAuthenticated()) {
610 return;
611 }
612 if (_authenticationOperationInProgress) {
613 // The signed out state might be temporary (e.g. account switch, ...).
614 // Don't pop this view based on intermediary values.
615 return;
616 }
617 [self dismissSelfAnimated:NO];
618 }
619
620 - (void)dismissSelfAnimated:(BOOL)animated {
621 if (_isBeingDismissed) {
622 return;
623 }
624 _isBeingDismissed = YES;
625 [_signinInteractionController cancelAndDismiss];
626 [_alertCoordinator stop];
627 [self.navigationController popToViewController:self animated:NO];
628 [base::mac::ObjCCastStrict<SettingsNavigationController>(
629 self.navigationController)
630 popViewControllerOrCloseSettingsAnimated:animated];
631 }
632
633 #pragma mark - Access to authentication service
634
635 - (AuthenticationService*)authService {
636 return AuthenticationServiceFactory::GetForBrowserState(_browserState);
637 }
638
639 #pragma mark - Switch accounts notifications
640
641 - (void)willStartSwitchAccount {
642 _authenticationOperationInProgress = YES;
643 }
644
645 - (void)didFinishSwitchAccount {
646 [self handleAuthenticationOperationDidFinish];
647 }
648
649 #pragma mark - ChromeIdentityBrowserOpener
650
651 - (void)openURL:(NSURL*)url
652 view:(UIView*)view
653 viewController:(UIViewController*)viewController {
654 base::scoped_nsobject<OpenUrlCommand> command(
655 [[OpenUrlCommand alloc] initWithURLFromChrome:net::GURLWithNSURL(url)]);
656 [command setTag:IDC_CLOSE_SETTINGS_AND_OPEN_URL];
657 [self chromeExecuteCommand:command];
658 }
659
660 #pragma mark - ChromeIdentityServiceObserver
661
662 - (void)onProfileUpdate:(ChromeIdentity*)identity {
663 CollectionViewAccountItem* item =
664 base::mac::ObjCCastStrict<CollectionViewAccountItem>(
665 [_identityMap objectForKey:identity.gaiaID]);
666 [self updateAccountItem:item withIdentity:identity];
667 NSIndexPath* indexPath =
668 [self.collectionViewModel indexPathForItem:item
669 inSectionWithIdentifier:SectionIdentifierAccounts];
670 [self.collectionView reloadItemsAtIndexPaths:@[ indexPath ]];
671 }
672
673 - (void)onChromeIdentityServiceWillBeDestroyed {
674 _identityServiceObserver.reset();
675 }
676
677 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698