| Index: ios/chrome/browser/ui/authentication/signin_account_selector_view_controller.mm
|
| diff --git a/ios/chrome/browser/ui/authentication/signin_account_selector_view_controller.mm b/ios/chrome/browser/ui/authentication/signin_account_selector_view_controller.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..fec861d94ba0941325f133293f254d16ae17a9ef
|
| --- /dev/null
|
| +++ b/ios/chrome/browser/ui/authentication/signin_account_selector_view_controller.mm
|
| @@ -0,0 +1,332 @@
|
| +// Copyright 2016 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.
|
| +
|
| +#include "ios/chrome/browser/ui/authentication/signin_account_selector_view_controller.h"
|
| +
|
| +#include <memory>
|
| +
|
| +#import "base/mac/foundation_util.h"
|
| +#import "base/mac/scoped_nsobject.h"
|
| +#import "ios/chrome/browser/signin/chrome_identity_service_observer_bridge.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_footer_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/settings/utils/resized_avatar_cache.h"
|
| +#import "ios/chrome/browser/ui/uikit_ui_util.h"
|
| +#include "ios/chrome/grit/ios_chromium_strings.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"
|
| +#include "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
|
| +#import "ios/third_party/material_components_ios/src/components/AppBar/src/MaterialAppBar.h"
|
| +#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
|
| +#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
|
| +#import "ui/base/l10n/l10n_util.h"
|
| +
|
| +namespace {
|
| +const CGFloat kHeaderViewMinHeight = 100.;
|
| +const CGFloat kHeaderViewHeightMultiplier = 0.33;
|
| +const CGFloat kContentViewBottomInset = 40.;
|
| +
|
| +typedef NS_ENUM(NSInteger, SectionIdentifier) {
|
| + SectionIdentifierTitle = kSectionIdentifierEnumZero,
|
| + SectionIdentifierAccounts,
|
| + SectionIdentifierAddAccount,
|
| +};
|
| +
|
| +typedef NS_ENUM(NSInteger, ItemType) {
|
| + ItemTypeTitle = kItemTypeEnumZero,
|
| + ItemTypeAccount,
|
| + ItemTypeAddAccount,
|
| +};
|
| +} // namespace
|
| +
|
| +@interface SigninAccountSelectorViewController ()<
|
| + ChromeIdentityServiceObserver> {
|
| + std::unique_ptr<ChromeIdentityServiceObserverBridge> _identityServiceObserver;
|
| + // Cache for account avatar images.
|
| + base::scoped_nsobject<ResizedAvatarCache> _avatarCache;
|
| +}
|
| +@end
|
| +
|
| +@implementation SigninAccountSelectorViewController
|
| +
|
| +@synthesize delegate = _delegate;
|
| +
|
| +- (instancetype)init {
|
| + self = [super initWithStyle:CollectionViewControllerStyleAppBar];
|
| + if (self) {
|
| + _identityServiceObserver.reset(
|
| + new ChromeIdentityServiceObserverBridge(self));
|
| + _avatarCache.reset([[ResizedAvatarCache alloc] init]);
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +#pragma mark - UIViewController
|
| +
|
| +- (void)viewDidLoad {
|
| + [super viewDidLoad];
|
| +
|
| + // Configure the header.
|
| + MDCFlexibleHeaderView* headerView =
|
| + self.appBar.headerViewController.headerView;
|
| + headerView.canOverExtend = YES;
|
| + headerView.maximumHeight = 200;
|
| + headerView.backgroundColor = [UIColor whiteColor];
|
| + headerView.shiftBehavior = MDCFlexibleHeaderShiftBehaviorEnabled;
|
| + [headerView addSubview:[self contentViewWithFrame:headerView.bounds]];
|
| + self.appBar.navigationBar.hidesBackButton = YES;
|
| + self.collectionView.backgroundColor = [UIColor clearColor];
|
| + [headerView changeContentInsets:^{
|
| + UIEdgeInsets contentInset = self.collectionView.contentInset;
|
| + contentInset.bottom += kContentViewBottomInset;
|
| + self.collectionView.contentInset = contentInset;
|
| + }];
|
| +
|
| + // Load the contents of the collection view.
|
| + [self loadModel];
|
| +}
|
| +
|
| +- (UIView*)contentViewWithFrame:(CGRect)frame {
|
| + base::scoped_nsobject<UIView> contentView(
|
| + [[UIView alloc] initWithFrame:frame]);
|
| + contentView.get().autoresizingMask =
|
| + (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
|
| + contentView.get().clipsToBounds = YES;
|
| +
|
| + base::scoped_nsobject<UILabel> titleLabel(
|
| + [[UILabel alloc] initWithFrame:CGRectZero]);
|
| + titleLabel.get().text =
|
| + l10n_util::GetNSString(IDS_IOS_ACCOUNT_CONSISTENCY_SETUP_TITLE);
|
| + titleLabel.get().textColor = [[MDCPalette greyPalette] tint900];
|
| + titleLabel.get().font = [MDCTypography headlineFont];
|
| + titleLabel.get().translatesAutoresizingMaskIntoConstraints = NO;
|
| +
|
| + base::scoped_nsobject<UIView> divider(
|
| + [[UIView alloc] initWithFrame:CGRectZero]);
|
| + divider.get().backgroundColor = [[MDCPalette greyPalette] tint300];
|
| + divider.get().translatesAutoresizingMaskIntoConstraints = NO;
|
| +
|
| + base::scoped_nsobject<UILayoutGuide> layoutGuide1(
|
| + [[UILayoutGuide alloc] init]);
|
| + base::scoped_nsobject<UILayoutGuide> layoutGuide2(
|
| + [[UILayoutGuide alloc] init]);
|
| +
|
| + [contentView addSubview:titleLabel];
|
| + [contentView addSubview:divider];
|
| + [contentView addLayoutGuide:layoutGuide1];
|
| + [contentView addLayoutGuide:layoutGuide2];
|
| +
|
| + NSDictionary* views = @{
|
| + @"title" : titleLabel,
|
| + @"divider" : divider,
|
| + @"v1" : layoutGuide1,
|
| + @"v2" : layoutGuide2
|
| + };
|
| + NSArray* constraints = @[
|
| + @"V:[title]-(16)-[divider(==1)]|",
|
| + @"H:|[v1(16)][title(<=440)][v2(>=v1)]|",
|
| + @"H:|[divider]|",
|
| + ];
|
| + ApplyVisualConstraints(constraints, views);
|
| + return contentView.autorelease();
|
| +}
|
| +
|
| +- (void)viewWillLayoutSubviews {
|
| + CGSize viewSize = self.view.bounds.size;
|
| + MDCFlexibleHeaderView* headerView =
|
| + self.appBar.headerViewController.headerView;
|
| + headerView.maximumHeight =
|
| + MAX(kHeaderViewMinHeight, kHeaderViewHeightMultiplier * viewSize.height);
|
| +}
|
| +
|
| +#pragma mark - Model
|
| +
|
| +- (void)loadModel {
|
| + [super loadModel];
|
| + CollectionViewModel* model = self.collectionViewModel;
|
| + [model addSectionWithIdentifier:SectionIdentifierTitle];
|
| + [model addItem:[self titleItem]
|
| + toSectionWithIdentifier:SectionIdentifierTitle];
|
| +
|
| + [model addSectionWithIdentifier:SectionIdentifierAccounts];
|
| + NSArray* identities = ios::GetChromeBrowserProvider()
|
| + ->GetChromeIdentityService()
|
| + ->GetAllIdentitiesSortedForDisplay();
|
| + if ([identities count]) {
|
| + for (NSUInteger i = 0; i < [identities count]; ++i) {
|
| + [model addItem:[self accountItemForIdentity:identities[i]
|
| + checked:(i == 0)]
|
| + toSectionWithIdentifier:SectionIdentifierAccounts];
|
| + }
|
| + [model addSectionWithIdentifier:SectionIdentifierAddAccount];
|
| + [model addItem:[self addAccountItem]
|
| + toSectionWithIdentifier:SectionIdentifierAddAccount];
|
| + }
|
| +}
|
| +
|
| +- (CollectionViewItem*)titleItem {
|
| + // TODO(crbug.com/662549) : Rename FooterItem to be used as regular item.
|
| + CollectionViewFooterItem* item = [[[CollectionViewFooterItem alloc]
|
| + initWithType:ItemTypeTitle] autorelease];
|
| + item.text =
|
| + l10n_util::GetNSString(IDS_IOS_ACCOUNT_CONSISTENCY_SETUP_DESCRIPTION);
|
| + return item;
|
| +}
|
| +
|
| +- (CollectionViewItem*)accountItemForIdentity:(ChromeIdentity*)identity
|
| + checked:(BOOL)isChecked {
|
| + CollectionViewAccountItem* item = [[[CollectionViewAccountItem alloc]
|
| + initWithType:ItemTypeAccount] autorelease];
|
| + [self updateAccountItem:item withIdentity:identity];
|
| + if (isChecked) {
|
| + item.accessoryType = MDCCollectionViewCellAccessoryCheckmark;
|
| + }
|
| + return item;
|
| +}
|
| +
|
| +- (void)updateAccountItem:(CollectionViewAccountItem*)item
|
| + withIdentity:(ChromeIdentity*)identity {
|
| + item.image = [_avatarCache resizedAvatarForIdentity:identity];
|
| + item.text = identity.userEmail;
|
| + item.chromeIdentity = identity;
|
| +}
|
| +
|
| +- (CollectionViewItem*)addAccountItem {
|
| + CollectionViewAccountItem* item = [[[CollectionViewAccountItem alloc]
|
| + initWithType:ItemTypeAddAccount] autorelease];
|
| + item.text = l10n_util::GetNSString(
|
| + IDS_IOS_ACCOUNT_CONSISTENCY_SETUP_ADD_ACCOUNT_BUTTON);
|
| + item.image = [UIImage imageNamed:@"settings_accounts_add_account"];
|
| + return item;
|
| +}
|
| +
|
| +- (ChromeIdentity*)selectedIdentity {
|
| + NSArray* accountItems = [self.collectionViewModel
|
| + itemsInSectionWithIdentifier:SectionIdentifierAccounts];
|
| + for (CollectionViewAccountItem* accountItem in accountItems) {
|
| + if (accountItem.accessoryType == MDCCollectionViewCellAccessoryCheckmark) {
|
| + return accountItem.chromeIdentity;
|
| + }
|
| + }
|
| + return nil;
|
| +}
|
| +
|
| +#pragma mark - UICollectionViewDelegate
|
| +
|
| +- (void)collectionView:(UICollectionView*)collectionView
|
| + didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
|
| + [super collectionView:collectionView didSelectItemAtIndexPath:indexPath];
|
| + CollectionViewItem* item =
|
| + [self.collectionViewModel itemAtIndexPath:indexPath];
|
| + if (item.type == ItemTypeAccount) {
|
| + CollectionViewAccountItem* selectedAccountItem =
|
| + base::mac::ObjCCastStrict<CollectionViewAccountItem>(item);
|
| + // TODO(crbug.com/631486) : Checkmark animation.
|
| + selectedAccountItem.accessoryType = MDCCollectionViewCellAccessoryCheckmark;
|
| +
|
| + base::scoped_nsobject<NSMutableArray<CollectionViewItem*>> reloadItems(
|
| + [[NSMutableArray alloc] init]);
|
| + [reloadItems addObject:selectedAccountItem];
|
| +
|
| + // Uncheck all the other account items.
|
| + NSArray* accountItems = [self.collectionViewModel
|
| + itemsInSectionWithIdentifier:SectionIdentifierAccounts];
|
| + for (CollectionViewAccountItem* accountItem in accountItems) {
|
| + if (accountItem != selectedAccountItem &&
|
| + accountItem.accessoryType != MDCCollectionViewCellAccessoryNone) {
|
| + // TODO(crbug.com/631486) : Checkmark animation.
|
| + accountItem.accessoryType = MDCCollectionViewCellAccessoryNone;
|
| + [reloadItems addObject:accountItem];
|
| + }
|
| + }
|
| + [self reconfigureCellsForItems:reloadItems
|
| + inSectionWithIdentifier:SectionIdentifierAccounts];
|
| + } else if (item.type == ItemTypeAddAccount) {
|
| + [self.delegate accountSelectorControllerDidSelectAddAccount:self];
|
| + }
|
| +}
|
| +
|
| +#pragma mark - ChromeIdentityServiceObserver
|
| +
|
| +- (void)onIdentityListChanged {
|
| + ChromeIdentity* selectedIdentity = [self selectedIdentity];
|
| + [self loadModel];
|
| +
|
| + // Reselect the identity.
|
| + if (!selectedIdentity) {
|
| + return;
|
| + }
|
| + NSArray* accountItems = [self.collectionViewModel
|
| + itemsInSectionWithIdentifier:SectionIdentifierAccounts];
|
| + for (CollectionViewAccountItem* accountItem in accountItems) {
|
| + if ([accountItem.chromeIdentity.gaiaID
|
| + isEqualToString:selectedIdentity.gaiaID]) {
|
| + accountItem.accessoryType = MDCCollectionViewCellAccessoryCheckmark;
|
| + } else {
|
| + accountItem.accessoryType = MDCCollectionViewCellAccessoryNone;
|
| + }
|
| + }
|
| +}
|
| +
|
| +- (void)onChromeIdentityServiceWillBeDestroyed {
|
| + _identityServiceObserver.reset();
|
| +}
|
| +
|
| +#pragma mark UICollectionViewDataSource
|
| +
|
| +- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
|
| + cellForItemAtIndexPath:(NSIndexPath*)indexPath {
|
| + MDCCollectionViewCell* cell =
|
| + [super collectionView:collectionView cellForItemAtIndexPath:indexPath];
|
| + CollectionViewItem* item =
|
| + [self.collectionViewModel itemAtIndexPath:indexPath];
|
| +
|
| + if (item.type == ItemTypeAccount || item.type == ItemTypeAddAccount) {
|
| + CollectionViewAccountCell* accountCell =
|
| + base::mac::ObjCCastStrict<CollectionViewAccountCell>(cell);
|
| + accountCell.textLabel.font = [MDCTypography body1Font];
|
| + accountCell.textLabel.textColor = [[MDCPalette greyPalette] tint900];
|
| + } else if (item.type == ItemTypeTitle) {
|
| + CollectionViewFooterCell* titleCell =
|
| + base::mac::ObjCCastStrict<CollectionViewFooterCell>(cell);
|
| + titleCell.textLabel.font = [MDCTypography body1Font];
|
| + titleCell.textLabel.textColor = [[MDCPalette greyPalette] tint900];
|
| + titleCell.horizontalPadding = 16;
|
| + }
|
| + cell.shouldHideSeparator = YES;
|
| + return cell;
|
| +}
|
| +
|
| +#pragma mark - MDCCollectionViewStylingDelegate
|
| +
|
| +- (CGFloat)collectionView:(UICollectionView*)collectionView
|
| + cellHeightAtIndexPath:(NSIndexPath*)indexPath {
|
| + CollectionViewItem* item =
|
| + [self.collectionViewModel itemAtIndexPath:indexPath];
|
| +
|
| + if (item.type == ItemTypeTitle) {
|
| + return [MDCCollectionViewCell
|
| + cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds)
|
| + forItem:item];
|
| + }
|
| + return MDCCellDefaultTwoLineHeight;
|
| +}
|
| +
|
| +- (BOOL)collectionView:(UICollectionView*)collectionView
|
| + hidesInkViewAtIndexPath:(NSIndexPath*)indexPath {
|
| + NSInteger itemType =
|
| + [self.collectionViewModel itemTypeForIndexPath:indexPath];
|
| + return (itemType == ItemTypeTitle);
|
| +}
|
| +
|
| +- (BOOL)collectionView:(nonnull UICollectionView*)collectionView
|
| + shouldHideItemBackgroundAtIndexPath:(nonnull NSIndexPath*)indexPath {
|
| + return YES;
|
| +}
|
| +
|
| +@end
|
|
|