OLD | NEW |
(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_encryption_passphrase_collection_vi
ew_controller.h" |
| 6 |
| 7 #include <memory> |
| 8 |
| 9 #include "base/i18n/time_formatting.h" |
| 10 #include "base/mac/foundation_util.h" |
| 11 #include "base/mac/objc_property_releaser.h" |
| 12 #include "base/mac/scoped_nsobject.h" |
| 13 #include "base/strings/sys_string_conversions.h" |
| 14 #include "components/browser_sync/profile_sync_service.h" |
| 15 #include "components/google/core/browser/google_util.h" |
| 16 #include "components/signin/core/browser/profile_oauth2_token_service.h" |
| 17 #include "components/signin/ios/browser/oauth2_token_service_observer_bridge.h" |
| 18 #include "components/strings/grit/components_strings.h" |
| 19 #include "ios/chrome/browser/application_context.h" |
| 20 #include "ios/chrome/browser/browser_state/chrome_browser_state.h" |
| 21 #include "ios/chrome/browser/chrome_url_constants.h" |
| 22 #import "ios/chrome/browser/signin/authentication_service.h" |
| 23 #import "ios/chrome/browser/signin/authentication_service_factory.h" |
| 24 #include "ios/chrome/browser/signin/oauth2_token_service_factory.h" |
| 25 #include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h" |
| 26 #include "ios/chrome/browser/sync/sync_setup_service.h" |
| 27 #include "ios/chrome/browser/sync/sync_setup_service_factory.h" |
| 28 #import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrom
e.h" |
| 29 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_footer_item
.h" |
| 30 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h" |
| 31 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h" |
| 32 #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h" |
| 33 #import "ios/chrome/browser/ui/settings/cells/byo_textfield_item.h" |
| 34 #import "ios/chrome/browser/ui/settings/cells/card_multiline_item.h" |
| 35 #import "ios/chrome/browser/ui/settings/cells/passphrase_error_item.h" |
| 36 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h" |
| 37 #import "ios/chrome/browser/ui/settings/settings_utils.h" |
| 38 #import "ios/chrome/browser/ui/sync/sync_util.h" |
| 39 #import "ios/chrome/browser/ui/uikit_ui_util.h" |
| 40 #include "ios/chrome/grit/ios_strings.h" |
| 41 #import "ios/public/provider/chrome/browser/signin/chrome_identity.h" |
| 42 #import "ios/third_party/material_components_ios/src/components/Typography/src/M
aterialTypography.h" |
| 43 #import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoF
ontLoader.h" |
| 44 #include "ui/base/l10n/l10n_util.h" |
| 45 #include "ui/base/l10n/l10n_util_mac.h" |
| 46 #include "url/gurl.h" |
| 47 |
| 48 using namespace ios_internal::sync_encryption_passphrase; |
| 49 |
| 50 namespace { |
| 51 |
| 52 const CGFloat kSpinnerButtonCustomViewSize = 48; |
| 53 const CGFloat kSpinnerButtonPadding = 18; |
| 54 |
| 55 } // namespace |
| 56 |
| 57 @interface SyncEncryptionPassphraseCollectionViewController ()< |
| 58 OAuth2TokenServiceObserverBridgeDelegate, |
| 59 SettingsControllerProtocol> { |
| 60 ios::ChromeBrowserState* browserState_; |
| 61 // Whether the decryption progress is currently being shown. |
| 62 BOOL isDecryptionProgressShown_; |
| 63 base::scoped_nsobject<NSString> savedTitle_; |
| 64 base::scoped_nsobject<UIBarButtonItem> savedLeftButton_; |
| 65 std::unique_ptr<SyncObserverBridge> syncObserver_; |
| 66 std::unique_ptr<OAuth2TokenServiceObserverBridge> tokenServiceObserver_; |
| 67 base::scoped_nsobject<UITextField> passphrase_; |
| 68 base::mac::ObjCPropertyReleaser |
| 69 propertyReleaser_SyncEncryptionPassphraseCollectionViewController_; |
| 70 } |
| 71 |
| 72 // Sets up the navigation bar's right button. The button will be enabled iff |
| 73 // |-areAllFieldsFilled| returns YES. |
| 74 - (void)setRightNavBarItem; |
| 75 |
| 76 // Returns a passphrase message item. |
| 77 - (CollectionViewItem*)passphraseMessageItem; |
| 78 |
| 79 // Returns a passphrase item. |
| 80 - (CollectionViewItem*)passphraseItem; |
| 81 |
| 82 // Returns a passphrase error item having |errorMessage| as title. |
| 83 - (CollectionViewItem*)passphraseErrorItemWithMessage:(NSString*)errorMessage; |
| 84 |
| 85 // Shows the UI to indicate the decryption is being attempted. |
| 86 - (void)showDecryptionProgress; |
| 87 |
| 88 // Hides the UI to indicate decryption is in process. |
| 89 - (void)hideDecryptionProgress; |
| 90 |
| 91 // Returns a transparent content view object to be used as a footer, or nil |
| 92 // for no footer. |
| 93 - (CollectionViewItem*)footerItem; |
| 94 |
| 95 // Creates a new UIBarButtonItem with a spinner. |
| 96 - (UIBarButtonItem*)spinnerButton; |
| 97 |
| 98 @end |
| 99 |
| 100 @implementation SyncEncryptionPassphraseCollectionViewController |
| 101 |
| 102 @synthesize headerMessage = headerMessage_; |
| 103 @synthesize footerMessage = footerMessage_; |
| 104 @synthesize processingMessage = processingMessage_; |
| 105 @synthesize syncErrorMessage = syncErrorMessage_; |
| 106 |
| 107 - (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState { |
| 108 DCHECK(browserState); |
| 109 self = [super initWithStyle:CollectionViewControllerStyleAppBar]; |
| 110 if (self) { |
| 111 self.title = l10n_util::GetNSString(IDS_IOS_SYNC_ENTER_PASSPHRASE_TITLE); |
| 112 self.shouldHideDoneButton = YES; |
| 113 browserState_ = browserState; |
| 114 NSString* userEmail = |
| 115 AuthenticationServiceFactory::GetForBrowserState(browserState_) |
| 116 ->GetAuthenticatedUserEmail(); |
| 117 DCHECK(userEmail); |
| 118 browser_sync::ProfileSyncService* service = |
| 119 IOSChromeProfileSyncServiceFactory::GetForBrowserState(browserState_); |
| 120 if (service->IsEngineInitialized() && |
| 121 service->IsUsingSecondaryPassphrase()) { |
| 122 base::Time passphrase_time = service->GetExplicitPassphraseTime(); |
| 123 if (!passphrase_time.is_null()) { |
| 124 base::string16 passphrase_time_str = |
| 125 base::TimeFormatShortDate(passphrase_time); |
| 126 self.headerMessage = l10n_util::GetNSStringF( |
| 127 IDS_IOS_SYNC_ENTER_PASSPHRASE_BODY_WITH_EMAIL_AND_DATE, |
| 128 base::SysNSStringToUTF16(userEmail), passphrase_time_str); |
| 129 } else { |
| 130 self.headerMessage = l10n_util::GetNSStringF( |
| 131 IDS_IOS_SYNC_ENTER_PASSPHRASE_BODY_WITH_EMAIL, |
| 132 base::SysNSStringToUTF16(userEmail)); |
| 133 } |
| 134 } else { |
| 135 self.headerMessage = |
| 136 l10n_util::GetNSString(IDS_SYNC_ENTER_GOOGLE_PASSPHRASE_BODY); |
| 137 } |
| 138 self.processingMessage = l10n_util::GetNSString(IDS_SYNC_LOGIN_SETTING_UP); |
| 139 footerMessage_ = |
| 140 [l10n_util::GetNSString(IDS_IOS_SYNC_PASSPHRASE_RECOVER) retain]; |
| 141 |
| 142 tokenServiceObserver_.reset(new OAuth2TokenServiceObserverBridge( |
| 143 OAuth2TokenServiceFactory::GetForBrowserState(browserState_), self)); |
| 144 |
| 145 propertyReleaser_SyncEncryptionPassphraseCollectionViewController_.Init( |
| 146 self, [SyncEncryptionPassphraseCollectionViewController class]); |
| 147 |
| 148 [self loadModel]; |
| 149 } |
| 150 return self; |
| 151 } |
| 152 |
| 153 - (UITextField*)passphrase { |
| 154 return passphrase_; |
| 155 } |
| 156 |
| 157 - (NSString*)syncErrorMessage { |
| 158 if (syncErrorMessage_) |
| 159 return syncErrorMessage_; |
| 160 SyncSetupService* service = |
| 161 SyncSetupServiceFactory::GetForBrowserState(browserState_); |
| 162 DCHECK(service); |
| 163 SyncSetupService::SyncServiceState syncServiceState = |
| 164 service->GetSyncServiceState(); |
| 165 |
| 166 // Passphrase error directly set |syncErrorMessage_|. |
| 167 if (syncServiceState == SyncSetupService::kSyncServiceNeedsPassphrase) |
| 168 return nil; |
| 169 |
| 170 return ios_internal::sync::GetSyncErrorMessageForBrowserState(browserState_); |
| 171 } |
| 172 |
| 173 #pragma mark - View lifecycle |
| 174 |
| 175 - (void)viewDidLoad { |
| 176 [super viewDidLoad]; |
| 177 [self setRightNavBarItem]; |
| 178 } |
| 179 |
| 180 - (void)didReceiveMemoryWarning { |
| 181 [super didReceiveMemoryWarning]; |
| 182 if (![self isViewLoaded]) { |
| 183 passphrase_.reset(); |
| 184 } |
| 185 } |
| 186 |
| 187 - (void)viewWillDisappear:(BOOL)animated { |
| 188 [super viewWillDisappear:animated]; |
| 189 [self.passphrase resignFirstResponder]; |
| 190 } |
| 191 |
| 192 - (void)viewDidDisappear:(BOOL)animated { |
| 193 [super viewDidDisappear:animated]; |
| 194 if ([self isMovingFromParentViewController]) { |
| 195 [self unregisterTextField:self.passphrase]; |
| 196 } |
| 197 } |
| 198 |
| 199 #pragma mark - SettingsRootCollectionViewController |
| 200 |
| 201 - (void)loadModel { |
| 202 [super loadModel]; |
| 203 CollectionViewModel* model = self.collectionViewModel; |
| 204 |
| 205 [model addSectionWithIdentifier:SectionIdentifierPassphrase]; |
| 206 if (self.headerMessage) { |
| 207 [model addItem:[self passphraseMessageItem] |
| 208 toSectionWithIdentifier:SectionIdentifierPassphrase]; |
| 209 } |
| 210 [model addItem:[self passphraseItem] |
| 211 toSectionWithIdentifier:SectionIdentifierPassphrase]; |
| 212 |
| 213 NSString* errorMessage = [self syncErrorMessage]; |
| 214 if (errorMessage) { |
| 215 [model addItem:[self passphraseErrorItemWithMessage:errorMessage] |
| 216 toSectionWithIdentifier:SectionIdentifierPassphrase]; |
| 217 } |
| 218 // TODO(crbug.com/650424): Footer items must currently go into a separate |
| 219 // section, to work around a drawing bug in MDC. |
| 220 [model addSectionWithIdentifier:SectionIdentifierFooter]; |
| 221 [model addItem:[self footerItem] |
| 222 toSectionWithIdentifier:SectionIdentifierFooter]; |
| 223 } |
| 224 |
| 225 #pragma mark - Items |
| 226 |
| 227 - (CollectionViewItem*)passphraseMessageItem { |
| 228 CardMultilineItem* item = |
| 229 [[[CardMultilineItem alloc] initWithType:ItemTypeMessage] autorelease]; |
| 230 item.text = headerMessage_; |
| 231 return item; |
| 232 } |
| 233 |
| 234 - (CollectionViewItem*)passphraseItem { |
| 235 if (passphrase_) { |
| 236 [self unregisterTextField:passphrase_]; |
| 237 } |
| 238 passphrase_.reset([[UITextField alloc] init]); |
| 239 [passphrase_ setFont:[MDCTypography body1Font]]; |
| 240 [passphrase_ setSecureTextEntry:YES]; |
| 241 [passphrase_ setBackgroundColor:[UIColor clearColor]]; |
| 242 [passphrase_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; |
| 243 [passphrase_ setAutocorrectionType:UITextAutocorrectionTypeNo]; |
| 244 [passphrase_ |
| 245 setPlaceholder:l10n_util::GetNSString(IDS_SYNC_PASSPHRASE_LABEL)]; |
| 246 [self registerTextField:passphrase_]; |
| 247 |
| 248 BYOTextFieldItem* item = [[[BYOTextFieldItem alloc] |
| 249 initWithType:ItemTypeEnterPassphrase] autorelease]; |
| 250 item.textField = passphrase_; |
| 251 return item; |
| 252 } |
| 253 |
| 254 - (CollectionViewItem*)passphraseErrorItemWithMessage:(NSString*)errorMessage { |
| 255 PassphraseErrorItem* item = |
| 256 [[[PassphraseErrorItem alloc] initWithType:ItemTypeError] autorelease]; |
| 257 item.text = errorMessage; |
| 258 return item; |
| 259 } |
| 260 |
| 261 - (CollectionViewItem*)footerItem { |
| 262 CollectionViewFooterItem* footerItem = [[[CollectionViewFooterItem alloc] |
| 263 initWithType:ItemTypeFooter] autorelease]; |
| 264 footerItem.text = self.footerMessage; |
| 265 footerItem.linkURL = google_util::AppendGoogleLocaleParam( |
| 266 GURL(kSyncGoogleDashboardURL), |
| 267 GetApplicationContext()->GetApplicationLocale()); |
| 268 footerItem.linkDelegate = self; |
| 269 return footerItem; |
| 270 } |
| 271 |
| 272 #pragma mark - MDCCollectionViewStylingDelegate |
| 273 |
| 274 - (MDCCollectionViewCellStyle)collectionView:(UICollectionView*)collectionView |
| 275 cellStyleForSection:(NSInteger)section { |
| 276 NSInteger sectionIdentifier = |
| 277 [self.collectionViewModel sectionIdentifierForSection:section]; |
| 278 switch (sectionIdentifier) { |
| 279 case SectionIdentifierFooter: |
| 280 // Display the Learn More footer in the default style with no "card" UI |
| 281 // and no section padding. |
| 282 return MDCCollectionViewCellStyleDefault; |
| 283 default: |
| 284 return self.styler.cellStyle; |
| 285 } |
| 286 } |
| 287 |
| 288 - (BOOL)collectionView:(UICollectionView*)collectionView |
| 289 shouldHideItemBackgroundAtIndexPath:(NSIndexPath*)indexPath { |
| 290 NSInteger sectionIdentifier = |
| 291 [self.collectionViewModel sectionIdentifierForSection:indexPath.section]; |
| 292 switch (sectionIdentifier) { |
| 293 case SectionIdentifierFooter: |
| 294 // Display the Learn More footer without any background image or |
| 295 // shadowing. |
| 296 return YES; |
| 297 default: |
| 298 return NO; |
| 299 } |
| 300 } |
| 301 |
| 302 - (CGFloat)collectionView:(UICollectionView*)collectionView |
| 303 cellHeightAtIndexPath:(NSIndexPath*)indexPath { |
| 304 CollectionViewItem* item = |
| 305 [self.collectionViewModel itemAtIndexPath:indexPath]; |
| 306 if (item.type == ItemTypeMessage || item.type == ItemTypeFooter) { |
| 307 return [MDCCollectionViewCell |
| 308 cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds) |
| 309 forItem:item]; |
| 310 } |
| 311 return MDCCellDefaultOneLineHeight; |
| 312 } |
| 313 |
| 314 #pragma mark - UICollectionViewDataSource |
| 315 |
| 316 - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView |
| 317 cellForItemAtIndexPath:(NSIndexPath*)indexPath { |
| 318 UICollectionViewCell* cell = |
| 319 [super collectionView:collectionView cellForItemAtIndexPath:indexPath]; |
| 320 CollectionViewItem* item = |
| 321 [self.collectionViewModel itemAtIndexPath:indexPath]; |
| 322 if (item.type == ItemTypeMessage) { |
| 323 CardMultilineCell* messageCell = |
| 324 base::mac::ObjCCastStrict<CardMultilineCell>(cell); |
| 325 messageCell.textLabel.font = |
| 326 [[MDFRobotoFontLoader sharedInstance] mediumFontOfSize:14]; |
| 327 } |
| 328 return cell; |
| 329 } |
| 330 |
| 331 #pragma mark - UICollectionViewDelegate |
| 332 |
| 333 - (void)collectionView:(UICollectionView*)collectionView |
| 334 didSelectItemAtIndexPath:(NSIndexPath*)indexPath { |
| 335 [super collectionView:collectionView didSelectItemAtIndexPath:indexPath]; |
| 336 NSInteger itemType = |
| 337 [self.collectionViewModel itemTypeForIndexPath:indexPath]; |
| 338 if (itemType == ItemTypeEnterPassphrase) { |
| 339 [passphrase_ becomeFirstResponder]; |
| 340 } |
| 341 } |
| 342 |
| 343 #pragma mark - Behavior |
| 344 |
| 345 - (BOOL)forDecryption { |
| 346 return YES; |
| 347 } |
| 348 |
| 349 - (void)signInPressed { |
| 350 DCHECK([passphrase_ text].length); |
| 351 |
| 352 if (!syncObserver_.get()) { |
| 353 syncObserver_.reset(new SyncObserverBridge( |
| 354 self, |
| 355 IOSChromeProfileSyncServiceFactory::GetForBrowserState(browserState_))); |
| 356 } |
| 357 |
| 358 // Clear out the error message. |
| 359 self.syncErrorMessage = nil; |
| 360 |
| 361 browser_sync::ProfileSyncService* service = |
| 362 IOSChromeProfileSyncServiceFactory::GetForBrowserState(browserState_); |
| 363 DCHECK(service); |
| 364 // It is possible for a race condition to happen where a user is allowed |
| 365 // to call the backend with the passphrase before the backend is |
| 366 // initialized. |
| 367 // See crbug/276714. As a temporary measure, ignore the tap on sign-in |
| 368 // button. A better fix may be to disable the rightBarButtonItem (submit) |
| 369 // until backend is initialized. |
| 370 if (!service->IsEngineInitialized()) |
| 371 return; |
| 372 |
| 373 [self showDecryptionProgress]; |
| 374 std::string passphrase = base::SysNSStringToUTF8([passphrase_ text]); |
| 375 if ([self forDecryption]) { |
| 376 if (!service->SetDecryptionPassphrase(passphrase)) { |
| 377 syncObserver_.reset(); |
| 378 [self clearFieldsOnError:l10n_util::GetNSString( |
| 379 IDS_IOS_SYNC_INCORRECT_PASSPHRASE)]; |
| 380 [self hideDecryptionProgress]; |
| 381 } |
| 382 } else { |
| 383 service->EnableEncryptEverything(); |
| 384 service->SetEncryptionPassphrase( |
| 385 passphrase, browser_sync::ProfileSyncService::EXPLICIT); |
| 386 } |
| 387 [self reloadData]; |
| 388 } |
| 389 |
| 390 - (void)setRightNavBarItem { |
| 391 UIBarButtonItem* submitButtonItem = self.navigationItem.rightBarButtonItem; |
| 392 if (!submitButtonItem) { |
| 393 submitButtonItem = [[[UIBarButtonItem alloc] |
| 394 initWithTitle:l10n_util::GetNSString(IDS_IOS_SYNC_DECRYPT_BUTTON) |
| 395 style:UIBarButtonItemStylePlain |
| 396 target:self |
| 397 action:@selector(signInPressed)] autorelease]; |
| 398 } |
| 399 submitButtonItem.enabled = [self areAllFieldsFilled]; |
| 400 |
| 401 // Only setting the enabled state doesn't make the item redraw. As a |
| 402 // workaround, set it again. |
| 403 self.navigationItem.rightBarButtonItem = submitButtonItem; |
| 404 } |
| 405 |
| 406 - (BOOL)areAllFieldsFilled { |
| 407 return [self.passphrase text].length > 0; |
| 408 } |
| 409 |
| 410 - (void)clearFieldsOnError:(NSString*)errorMessage { |
| 411 self.syncErrorMessage = errorMessage; |
| 412 [self.passphrase setText:@""]; |
| 413 } |
| 414 |
| 415 - (void)showDecryptionProgress { |
| 416 if (isDecryptionProgressShown_) |
| 417 return; |
| 418 isDecryptionProgressShown_ = YES; |
| 419 |
| 420 // Hide the button. |
| 421 self.navigationItem.rightBarButtonItem = nil; |
| 422 |
| 423 // Custom title view with spinner. |
| 424 DCHECK(!savedTitle_); |
| 425 DCHECK(!savedLeftButton_); |
| 426 savedLeftButton_.reset([self.navigationItem.leftBarButtonItem retain]); |
| 427 self.navigationItem.leftBarButtonItem = [self spinnerButton]; |
| 428 savedTitle_.reset([self.title copy]); |
| 429 self.title = processingMessage_; |
| 430 } |
| 431 |
| 432 - (void)hideDecryptionProgress { |
| 433 if (!isDecryptionProgressShown_) |
| 434 return; |
| 435 isDecryptionProgressShown_ = NO; |
| 436 |
| 437 self.navigationItem.leftBarButtonItem = savedLeftButton_.autorelease(); |
| 438 self.title = savedTitle_.autorelease(); |
| 439 [self setRightNavBarItem]; |
| 440 } |
| 441 |
| 442 - (void)registerTextField:(UITextField*)textField { |
| 443 [textField addTarget:self |
| 444 action:@selector(textFieldDidBeginEditing:) |
| 445 forControlEvents:UIControlEventEditingDidBegin]; |
| 446 [textField addTarget:self |
| 447 action:@selector(textFieldDidChange:) |
| 448 forControlEvents:UIControlEventEditingChanged]; |
| 449 [textField addTarget:self |
| 450 action:@selector(textFieldDidEndEditing:) |
| 451 forControlEvents:UIControlEventEditingDidEndOnExit]; |
| 452 } |
| 453 |
| 454 - (void)unregisterTextField:(UITextField*)textField { |
| 455 [textField removeTarget:self |
| 456 action:@selector(textFieldDidBeginEditing:) |
| 457 forControlEvents:UIControlEventEditingDidBegin]; |
| 458 [textField removeTarget:self |
| 459 action:@selector(textFieldDidChange:) |
| 460 forControlEvents:UIControlEventEditingChanged]; |
| 461 [textField removeTarget:self |
| 462 action:@selector(textFieldDidEndEditing:) |
| 463 forControlEvents:UIControlEventEditingDidEndOnExit]; |
| 464 } |
| 465 |
| 466 - (UIBarButtonItem*)spinnerButton { |
| 467 CGRect customViewFrame = CGRectMake(0, 0, kSpinnerButtonCustomViewSize, |
| 468 kSpinnerButtonCustomViewSize); |
| 469 base::scoped_nsobject<UIView> customView( |
| 470 [[UIView alloc] initWithFrame:customViewFrame]); |
| 471 |
| 472 base::scoped_nsobject<UIActivityIndicatorView> spinner( |
| 473 [[UIActivityIndicatorView alloc] |
| 474 initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]); |
| 475 |
| 476 CGRect spinnerFrame = [spinner bounds]; |
| 477 spinnerFrame.origin.x = kSpinnerButtonPadding; |
| 478 spinnerFrame.origin.y = kSpinnerButtonPadding; |
| 479 [spinner setFrame:spinnerFrame]; |
| 480 [customView addSubview:spinner]; |
| 481 |
| 482 base::scoped_nsobject<UIBarButtonItem> leftBarButtonItem( |
| 483 [[UIBarButtonItem alloc] initWithCustomView:customView]); |
| 484 |
| 485 [spinner setHidesWhenStopped:NO]; |
| 486 [spinner startAnimating]; |
| 487 |
| 488 return leftBarButtonItem.autorelease(); |
| 489 } |
| 490 |
| 491 - (void)stopObserving { |
| 492 // Stops observing the sync service. This is required during the shutdown |
| 493 // phase to avoid observing sync events for a browser state that is being |
| 494 // killed. |
| 495 syncObserver_.reset(); |
| 496 tokenServiceObserver_.reset(); |
| 497 } |
| 498 |
| 499 #pragma mark - UIControl events listener |
| 500 |
| 501 - (void)textFieldDidBeginEditing:(id)sender { |
| 502 // Remove the error cell if there is one. |
| 503 CollectionViewModel* model = self.collectionViewModel; |
| 504 NSInteger section = |
| 505 [model sectionForSectionIdentifier:SectionIdentifierPassphrase]; |
| 506 NSIndexPath* errorIndexPath = |
| 507 [NSIndexPath indexPathForItem:ItemTypeError inSection:section]; |
| 508 if ([model hasItemAtIndexPath:errorIndexPath] && |
| 509 [model itemTypeForIndexPath:errorIndexPath] == ItemTypeError) { |
| 510 DCHECK(self.syncErrorMessage); |
| 511 [model removeItemWithType:ItemTypeError |
| 512 fromSectionWithIdentifier:SectionIdentifierPassphrase]; |
| 513 [self.collectionView deleteItemsAtIndexPaths:@[ errorIndexPath ]]; |
| 514 self.syncErrorMessage = nil; |
| 515 } |
| 516 } |
| 517 |
| 518 - (void)textFieldDidChange:(id)sender { |
| 519 [self setRightNavBarItem]; |
| 520 } |
| 521 |
| 522 - (void)textFieldDidEndEditing:(id)sender { |
| 523 if (sender == self.passphrase) { |
| 524 if ([self areAllFieldsFilled]) { |
| 525 [self signInPressed]; |
| 526 } else { |
| 527 [self clearFieldsOnError:l10n_util::GetNSString( |
| 528 IDS_SYNC_EMPTY_PASSPHRASE_ERROR)]; |
| 529 [self reloadData]; |
| 530 } |
| 531 } |
| 532 } |
| 533 |
| 534 #pragma mark - SyncObserverModelBridge |
| 535 |
| 536 - (void)onSyncStateChanged { |
| 537 browser_sync::ProfileSyncService* service = |
| 538 IOSChromeProfileSyncServiceFactory::GetForBrowserState(browserState_); |
| 539 |
| 540 if (!service->IsEngineInitialized()) { |
| 541 return; |
| 542 } |
| 543 |
| 544 // Checking if the operation succeeded. |
| 545 if (!service->IsPassphraseRequired() && |
| 546 (service->IsUsingSecondaryPassphrase() || [self forDecryption])) { |
| 547 syncObserver_.reset(); |
| 548 [base::mac::ObjCCastStrict<SettingsNavigationController>( |
| 549 self.navigationController) |
| 550 popViewControllerOrCloseSettingsAnimated:YES]; |
| 551 return; |
| 552 } |
| 553 |
| 554 // Handling passphrase error case. |
| 555 if (service->IsPassphraseRequired()) { |
| 556 self.syncErrorMessage = |
| 557 l10n_util::GetNSString(IDS_IOS_SYNC_INCORRECT_PASSPHRASE); |
| 558 } |
| 559 [self hideDecryptionProgress]; |
| 560 [self reloadData]; |
| 561 } |
| 562 |
| 563 #pragma mark - OAuth2TokenServiceObserverBridgeDelegate |
| 564 |
| 565 - (void)onEndBatchChanges { |
| 566 if (AuthenticationServiceFactory::GetForBrowserState(browserState_) |
| 567 ->IsAuthenticated()) { |
| 568 return; |
| 569 } |
| 570 [base::mac::ObjCCastStrict<SettingsNavigationController>( |
| 571 self.navigationController) popViewControllerOrCloseSettingsAnimated:NO]; |
| 572 } |
| 573 |
| 574 #pragma mark - SettingsControllerProtocol callbacks |
| 575 |
| 576 - (void)settingsWillBeDismissed { |
| 577 [self stopObserving]; |
| 578 } |
| 579 |
| 580 @end |
OLD | NEW |