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

Unified Diff: ios/chrome/browser/ui/settings/sync_encryption_passphrase_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_encryption_passphrase_collection_view_controller.mm
diff --git a/ios/chrome/browser/ui/settings/sync_encryption_passphrase_collection_view_controller.mm b/ios/chrome/browser/ui/settings/sync_encryption_passphrase_collection_view_controller.mm
new file mode 100644
index 0000000000000000000000000000000000000000..ca6a4f0a5c9e468324c4df87dd8d42e70b9153e0
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/sync_encryption_passphrase_collection_view_controller.mm
@@ -0,0 +1,580 @@
+// 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_encryption_passphrase_collection_view_controller.h"
+
+#include <memory>
+
+#include "base/i18n/time_formatting.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/objc_property_releaser.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/strings/sys_string_conversions.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/google/core/browser/google_util.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 "ios/chrome/browser/application_context.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
+#import "ios/chrome/browser/signin/authentication_service.h"
+#import "ios/chrome/browser/signin/authentication_service_factory.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/collection_view/cells/MDCCollectionViewCell+Chrome.h"
+#import "ios/chrome/browser/ui/collection_view/cells/collection_view_footer_item.h"
+#import "ios/chrome/browser/ui/collection_view/cells/collection_view_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/settings/cells/byo_textfield_item.h"
+#import "ios/chrome/browser/ui/settings/cells/card_multiline_item.h"
+#import "ios/chrome/browser/ui/settings/cells/passphrase_error_item.h"
+#import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
+#import "ios/chrome/browser/ui/settings/settings_utils.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"
+#import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
+#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
+#import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoFontLoader.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+#include "url/gurl.h"
+
+using namespace ios_internal::sync_encryption_passphrase;
+
+namespace {
+
+const CGFloat kSpinnerButtonCustomViewSize = 48;
+const CGFloat kSpinnerButtonPadding = 18;
+
+} // namespace
+
+@interface SyncEncryptionPassphraseCollectionViewController ()<
+ OAuth2TokenServiceObserverBridgeDelegate,
+ SettingsControllerProtocol> {
+ ios::ChromeBrowserState* browserState_;
+ // Whether the decryption progress is currently being shown.
+ BOOL isDecryptionProgressShown_;
+ base::scoped_nsobject<NSString> savedTitle_;
+ base::scoped_nsobject<UIBarButtonItem> savedLeftButton_;
+ std::unique_ptr<SyncObserverBridge> syncObserver_;
+ std::unique_ptr<OAuth2TokenServiceObserverBridge> tokenServiceObserver_;
+ base::scoped_nsobject<UITextField> passphrase_;
+ base::mac::ObjCPropertyReleaser
+ propertyReleaser_SyncEncryptionPassphraseCollectionViewController_;
+}
+
+// Sets up the navigation bar's right button. The button will be enabled iff
+// |-areAllFieldsFilled| returns YES.
+- (void)setRightNavBarItem;
+
+// Returns a passphrase message item.
+- (CollectionViewItem*)passphraseMessageItem;
+
+// Returns a passphrase item.
+- (CollectionViewItem*)passphraseItem;
+
+// Returns a passphrase error item having |errorMessage| as title.
+- (CollectionViewItem*)passphraseErrorItemWithMessage:(NSString*)errorMessage;
+
+// Shows the UI to indicate the decryption is being attempted.
+- (void)showDecryptionProgress;
+
+// Hides the UI to indicate decryption is in process.
+- (void)hideDecryptionProgress;
+
+// Returns a transparent content view object to be used as a footer, or nil
+// for no footer.
+- (CollectionViewItem*)footerItem;
+
+// Creates a new UIBarButtonItem with a spinner.
+- (UIBarButtonItem*)spinnerButton;
+
+@end
+
+@implementation SyncEncryptionPassphraseCollectionViewController
+
+@synthesize headerMessage = headerMessage_;
+@synthesize footerMessage = footerMessage_;
+@synthesize processingMessage = processingMessage_;
+@synthesize syncErrorMessage = syncErrorMessage_;
+
+- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState {
+ DCHECK(browserState);
+ self = [super initWithStyle:CollectionViewControllerStyleAppBar];
+ if (self) {
+ self.title = l10n_util::GetNSString(IDS_IOS_SYNC_ENTER_PASSPHRASE_TITLE);
+ self.shouldHideDoneButton = YES;
+ browserState_ = browserState;
+ NSString* userEmail =
+ AuthenticationServiceFactory::GetForBrowserState(browserState_)
+ ->GetAuthenticatedUserEmail();
+ DCHECK(userEmail);
+ browser_sync::ProfileSyncService* service =
+ IOSChromeProfileSyncServiceFactory::GetForBrowserState(browserState_);
+ if (service->IsEngineInitialized() &&
+ service->IsUsingSecondaryPassphrase()) {
+ base::Time passphrase_time = service->GetExplicitPassphraseTime();
+ if (!passphrase_time.is_null()) {
+ base::string16 passphrase_time_str =
+ base::TimeFormatShortDate(passphrase_time);
+ self.headerMessage = l10n_util::GetNSStringF(
+ IDS_IOS_SYNC_ENTER_PASSPHRASE_BODY_WITH_EMAIL_AND_DATE,
+ base::SysNSStringToUTF16(userEmail), passphrase_time_str);
+ } else {
+ self.headerMessage = l10n_util::GetNSStringF(
+ IDS_IOS_SYNC_ENTER_PASSPHRASE_BODY_WITH_EMAIL,
+ base::SysNSStringToUTF16(userEmail));
+ }
+ } else {
+ self.headerMessage =
+ l10n_util::GetNSString(IDS_SYNC_ENTER_GOOGLE_PASSPHRASE_BODY);
+ }
+ self.processingMessage = l10n_util::GetNSString(IDS_SYNC_LOGIN_SETTING_UP);
+ footerMessage_ =
+ [l10n_util::GetNSString(IDS_IOS_SYNC_PASSPHRASE_RECOVER) retain];
+
+ tokenServiceObserver_.reset(new OAuth2TokenServiceObserverBridge(
+ OAuth2TokenServiceFactory::GetForBrowserState(browserState_), self));
+
+ propertyReleaser_SyncEncryptionPassphraseCollectionViewController_.Init(
+ self, [SyncEncryptionPassphraseCollectionViewController class]);
+
+ [self loadModel];
+ }
+ return self;
+}
+
+- (UITextField*)passphrase {
+ return passphrase_;
+}
+
+- (NSString*)syncErrorMessage {
+ if (syncErrorMessage_)
+ return syncErrorMessage_;
+ SyncSetupService* service =
+ SyncSetupServiceFactory::GetForBrowserState(browserState_);
+ DCHECK(service);
+ SyncSetupService::SyncServiceState syncServiceState =
+ service->GetSyncServiceState();
+
+ // Passphrase error directly set |syncErrorMessage_|.
+ if (syncServiceState == SyncSetupService::kSyncServiceNeedsPassphrase)
+ return nil;
+
+ return ios_internal::sync::GetSyncErrorMessageForBrowserState(browserState_);
+}
+
+#pragma mark - View lifecycle
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ [self setRightNavBarItem];
+}
+
+- (void)didReceiveMemoryWarning {
+ [super didReceiveMemoryWarning];
+ if (![self isViewLoaded]) {
+ passphrase_.reset();
+ }
+}
+
+- (void)viewWillDisappear:(BOOL)animated {
+ [super viewWillDisappear:animated];
+ [self.passphrase resignFirstResponder];
+}
+
+- (void)viewDidDisappear:(BOOL)animated {
+ [super viewDidDisappear:animated];
+ if ([self isMovingFromParentViewController]) {
+ [self unregisterTextField:self.passphrase];
+ }
+}
+
+#pragma mark - SettingsRootCollectionViewController
+
+- (void)loadModel {
+ [super loadModel];
+ CollectionViewModel* model = self.collectionViewModel;
+
+ [model addSectionWithIdentifier:SectionIdentifierPassphrase];
+ if (self.headerMessage) {
+ [model addItem:[self passphraseMessageItem]
+ toSectionWithIdentifier:SectionIdentifierPassphrase];
+ }
+ [model addItem:[self passphraseItem]
+ toSectionWithIdentifier:SectionIdentifierPassphrase];
+
+ NSString* errorMessage = [self syncErrorMessage];
+ if (errorMessage) {
+ [model addItem:[self passphraseErrorItemWithMessage:errorMessage]
+ toSectionWithIdentifier:SectionIdentifierPassphrase];
+ }
+ // TODO(crbug.com/650424): Footer items must currently go into a separate
+ // section, to work around a drawing bug in MDC.
+ [model addSectionWithIdentifier:SectionIdentifierFooter];
+ [model addItem:[self footerItem]
+ toSectionWithIdentifier:SectionIdentifierFooter];
+}
+
+#pragma mark - Items
+
+- (CollectionViewItem*)passphraseMessageItem {
+ CardMultilineItem* item =
+ [[[CardMultilineItem alloc] initWithType:ItemTypeMessage] autorelease];
+ item.text = headerMessage_;
+ return item;
+}
+
+- (CollectionViewItem*)passphraseItem {
+ if (passphrase_) {
+ [self unregisterTextField:passphrase_];
+ }
+ passphrase_.reset([[UITextField alloc] init]);
+ [passphrase_ setFont:[MDCTypography body1Font]];
+ [passphrase_ setSecureTextEntry:YES];
+ [passphrase_ setBackgroundColor:[UIColor clearColor]];
+ [passphrase_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
+ [passphrase_ setAutocorrectionType:UITextAutocorrectionTypeNo];
+ [passphrase_
+ setPlaceholder:l10n_util::GetNSString(IDS_SYNC_PASSPHRASE_LABEL)];
+ [self registerTextField:passphrase_];
+
+ BYOTextFieldItem* item = [[[BYOTextFieldItem alloc]
+ initWithType:ItemTypeEnterPassphrase] autorelease];
+ item.textField = passphrase_;
+ return item;
+}
+
+- (CollectionViewItem*)passphraseErrorItemWithMessage:(NSString*)errorMessage {
+ PassphraseErrorItem* item =
+ [[[PassphraseErrorItem alloc] initWithType:ItemTypeError] autorelease];
+ item.text = errorMessage;
+ return item;
+}
+
+- (CollectionViewItem*)footerItem {
+ CollectionViewFooterItem* footerItem = [[[CollectionViewFooterItem alloc]
+ initWithType:ItemTypeFooter] autorelease];
+ footerItem.text = self.footerMessage;
+ footerItem.linkURL = google_util::AppendGoogleLocaleParam(
+ GURL(kSyncGoogleDashboardURL),
+ GetApplicationContext()->GetApplicationLocale());
+ footerItem.linkDelegate = self;
+ return footerItem;
+}
+
+#pragma mark - MDCCollectionViewStylingDelegate
+
+- (MDCCollectionViewCellStyle)collectionView:(UICollectionView*)collectionView
+ cellStyleForSection:(NSInteger)section {
+ NSInteger sectionIdentifier =
+ [self.collectionViewModel sectionIdentifierForSection:section];
+ switch (sectionIdentifier) {
+ case SectionIdentifierFooter:
+ // Display the Learn More footer in the default style with no "card" UI
+ // and no section padding.
+ return MDCCollectionViewCellStyleDefault;
+ default:
+ return self.styler.cellStyle;
+ }
+}
+
+- (BOOL)collectionView:(UICollectionView*)collectionView
+ shouldHideItemBackgroundAtIndexPath:(NSIndexPath*)indexPath {
+ NSInteger sectionIdentifier =
+ [self.collectionViewModel sectionIdentifierForSection:indexPath.section];
+ switch (sectionIdentifier) {
+ case SectionIdentifierFooter:
+ // Display the Learn More footer without any background image or
+ // shadowing.
+ return YES;
+ default:
+ return NO;
+ }
+}
+
+- (CGFloat)collectionView:(UICollectionView*)collectionView
+ cellHeightAtIndexPath:(NSIndexPath*)indexPath {
+ CollectionViewItem* item =
+ [self.collectionViewModel itemAtIndexPath:indexPath];
+ if (item.type == ItemTypeMessage || item.type == ItemTypeFooter) {
+ return [MDCCollectionViewCell
+ cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds)
+ forItem:item];
+ }
+ return MDCCellDefaultOneLineHeight;
+}
+
+#pragma mark - UICollectionViewDataSource
+
+- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
+ cellForItemAtIndexPath:(NSIndexPath*)indexPath {
+ UICollectionViewCell* cell =
+ [super collectionView:collectionView cellForItemAtIndexPath:indexPath];
+ CollectionViewItem* item =
+ [self.collectionViewModel itemAtIndexPath:indexPath];
+ if (item.type == ItemTypeMessage) {
+ CardMultilineCell* messageCell =
+ base::mac::ObjCCastStrict<CardMultilineCell>(cell);
+ messageCell.textLabel.font =
+ [[MDFRobotoFontLoader sharedInstance] mediumFontOfSize:14];
+ }
+ return cell;
+}
+
+#pragma mark - UICollectionViewDelegate
+
+- (void)collectionView:(UICollectionView*)collectionView
+ didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
+ [super collectionView:collectionView didSelectItemAtIndexPath:indexPath];
+ NSInteger itemType =
+ [self.collectionViewModel itemTypeForIndexPath:indexPath];
+ if (itemType == ItemTypeEnterPassphrase) {
+ [passphrase_ becomeFirstResponder];
+ }
+}
+
+#pragma mark - Behavior
+
+- (BOOL)forDecryption {
+ return YES;
+}
+
+- (void)signInPressed {
+ DCHECK([passphrase_ text].length);
+
+ if (!syncObserver_.get()) {
+ syncObserver_.reset(new SyncObserverBridge(
+ self,
+ IOSChromeProfileSyncServiceFactory::GetForBrowserState(browserState_)));
+ }
+
+ // Clear out the error message.
+ self.syncErrorMessage = nil;
+
+ browser_sync::ProfileSyncService* service =
+ IOSChromeProfileSyncServiceFactory::GetForBrowserState(browserState_);
+ DCHECK(service);
+ // It is possible for a race condition to happen where a user is allowed
+ // to call the backend with the passphrase before the backend is
+ // initialized.
+ // See crbug/276714. As a temporary measure, ignore the tap on sign-in
+ // button. A better fix may be to disable the rightBarButtonItem (submit)
+ // until backend is initialized.
+ if (!service->IsEngineInitialized())
+ return;
+
+ [self showDecryptionProgress];
+ std::string passphrase = base::SysNSStringToUTF8([passphrase_ text]);
+ if ([self forDecryption]) {
+ if (!service->SetDecryptionPassphrase(passphrase)) {
+ syncObserver_.reset();
+ [self clearFieldsOnError:l10n_util::GetNSString(
+ IDS_IOS_SYNC_INCORRECT_PASSPHRASE)];
+ [self hideDecryptionProgress];
+ }
+ } else {
+ service->EnableEncryptEverything();
+ service->SetEncryptionPassphrase(
+ passphrase, browser_sync::ProfileSyncService::EXPLICIT);
+ }
+ [self reloadData];
+}
+
+- (void)setRightNavBarItem {
+ UIBarButtonItem* submitButtonItem = self.navigationItem.rightBarButtonItem;
+ if (!submitButtonItem) {
+ submitButtonItem = [[[UIBarButtonItem alloc]
+ initWithTitle:l10n_util::GetNSString(IDS_IOS_SYNC_DECRYPT_BUTTON)
+ style:UIBarButtonItemStylePlain
+ target:self
+ action:@selector(signInPressed)] autorelease];
+ }
+ submitButtonItem.enabled = [self areAllFieldsFilled];
+
+ // Only setting the enabled state doesn't make the item redraw. As a
+ // workaround, set it again.
+ self.navigationItem.rightBarButtonItem = submitButtonItem;
+}
+
+- (BOOL)areAllFieldsFilled {
+ return [self.passphrase text].length > 0;
+}
+
+- (void)clearFieldsOnError:(NSString*)errorMessage {
+ self.syncErrorMessage = errorMessage;
+ [self.passphrase setText:@""];
+}
+
+- (void)showDecryptionProgress {
+ if (isDecryptionProgressShown_)
+ return;
+ isDecryptionProgressShown_ = YES;
+
+ // Hide the button.
+ self.navigationItem.rightBarButtonItem = nil;
+
+ // Custom title view with spinner.
+ DCHECK(!savedTitle_);
+ DCHECK(!savedLeftButton_);
+ savedLeftButton_.reset([self.navigationItem.leftBarButtonItem retain]);
+ self.navigationItem.leftBarButtonItem = [self spinnerButton];
+ savedTitle_.reset([self.title copy]);
+ self.title = processingMessage_;
+}
+
+- (void)hideDecryptionProgress {
+ if (!isDecryptionProgressShown_)
+ return;
+ isDecryptionProgressShown_ = NO;
+
+ self.navigationItem.leftBarButtonItem = savedLeftButton_.autorelease();
+ self.title = savedTitle_.autorelease();
+ [self setRightNavBarItem];
+}
+
+- (void)registerTextField:(UITextField*)textField {
+ [textField addTarget:self
+ action:@selector(textFieldDidBeginEditing:)
+ forControlEvents:UIControlEventEditingDidBegin];
+ [textField addTarget:self
+ action:@selector(textFieldDidChange:)
+ forControlEvents:UIControlEventEditingChanged];
+ [textField addTarget:self
+ action:@selector(textFieldDidEndEditing:)
+ forControlEvents:UIControlEventEditingDidEndOnExit];
+}
+
+- (void)unregisterTextField:(UITextField*)textField {
+ [textField removeTarget:self
+ action:@selector(textFieldDidBeginEditing:)
+ forControlEvents:UIControlEventEditingDidBegin];
+ [textField removeTarget:self
+ action:@selector(textFieldDidChange:)
+ forControlEvents:UIControlEventEditingChanged];
+ [textField removeTarget:self
+ action:@selector(textFieldDidEndEditing:)
+ forControlEvents:UIControlEventEditingDidEndOnExit];
+}
+
+- (UIBarButtonItem*)spinnerButton {
+ CGRect customViewFrame = CGRectMake(0, 0, kSpinnerButtonCustomViewSize,
+ kSpinnerButtonCustomViewSize);
+ base::scoped_nsobject<UIView> customView(
+ [[UIView alloc] initWithFrame:customViewFrame]);
+
+ base::scoped_nsobject<UIActivityIndicatorView> spinner(
+ [[UIActivityIndicatorView alloc]
+ initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]);
+
+ CGRect spinnerFrame = [spinner bounds];
+ spinnerFrame.origin.x = kSpinnerButtonPadding;
+ spinnerFrame.origin.y = kSpinnerButtonPadding;
+ [spinner setFrame:spinnerFrame];
+ [customView addSubview:spinner];
+
+ base::scoped_nsobject<UIBarButtonItem> leftBarButtonItem(
+ [[UIBarButtonItem alloc] initWithCustomView:customView]);
+
+ [spinner setHidesWhenStopped:NO];
+ [spinner startAnimating];
+
+ return leftBarButtonItem.autorelease();
+}
+
+- (void)stopObserving {
+ // Stops observing the sync service. This is required during the shutdown
+ // phase to avoid observing sync events for a browser state that is being
+ // killed.
+ syncObserver_.reset();
+ tokenServiceObserver_.reset();
+}
+
+#pragma mark - UIControl events listener
+
+- (void)textFieldDidBeginEditing:(id)sender {
+ // Remove the error cell if there is one.
+ CollectionViewModel* model = self.collectionViewModel;
+ NSInteger section =
+ [model sectionForSectionIdentifier:SectionIdentifierPassphrase];
+ NSIndexPath* errorIndexPath =
+ [NSIndexPath indexPathForItem:ItemTypeError inSection:section];
+ if ([model hasItemAtIndexPath:errorIndexPath] &&
+ [model itemTypeForIndexPath:errorIndexPath] == ItemTypeError) {
+ DCHECK(self.syncErrorMessage);
+ [model removeItemWithType:ItemTypeError
+ fromSectionWithIdentifier:SectionIdentifierPassphrase];
+ [self.collectionView deleteItemsAtIndexPaths:@[ errorIndexPath ]];
+ self.syncErrorMessage = nil;
+ }
+}
+
+- (void)textFieldDidChange:(id)sender {
+ [self setRightNavBarItem];
+}
+
+- (void)textFieldDidEndEditing:(id)sender {
+ if (sender == self.passphrase) {
+ if ([self areAllFieldsFilled]) {
+ [self signInPressed];
+ } else {
+ [self clearFieldsOnError:l10n_util::GetNSString(
+ IDS_SYNC_EMPTY_PASSPHRASE_ERROR)];
+ [self reloadData];
+ }
+ }
+}
+
+#pragma mark - SyncObserverModelBridge
+
+- (void)onSyncStateChanged {
+ browser_sync::ProfileSyncService* service =
+ IOSChromeProfileSyncServiceFactory::GetForBrowserState(browserState_);
+
+ if (!service->IsEngineInitialized()) {
+ return;
+ }
+
+ // Checking if the operation succeeded.
+ if (!service->IsPassphraseRequired() &&
+ (service->IsUsingSecondaryPassphrase() || [self forDecryption])) {
+ syncObserver_.reset();
+ [base::mac::ObjCCastStrict<SettingsNavigationController>(
+ self.navigationController)
+ popViewControllerOrCloseSettingsAnimated:YES];
+ return;
+ }
+
+ // Handling passphrase error case.
+ if (service->IsPassphraseRequired()) {
+ self.syncErrorMessage =
+ l10n_util::GetNSString(IDS_IOS_SYNC_INCORRECT_PASSPHRASE);
+ }
+ [self hideDecryptionProgress];
+ [self reloadData];
+}
+
+#pragma mark - OAuth2TokenServiceObserverBridgeDelegate
+
+- (void)onEndBatchChanges {
+ if (AuthenticationServiceFactory::GetForBrowserState(browserState_)
+ ->IsAuthenticated()) {
+ return;
+ }
+ [base::mac::ObjCCastStrict<SettingsNavigationController>(
+ self.navigationController) popViewControllerOrCloseSettingsAnimated:NO];
+}
+
+#pragma mark - SettingsControllerProtocol callbacks
+
+- (void)settingsWillBeDismissed {
+ [self stopObserving];
+}
+
+@end

Powered by Google App Engine
This is Rietveld 408576698