OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 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 #include "ios/chrome/browser/ui/authentication/signin_account_selector_view_cont
roller.h" |
| 6 |
| 7 #include <memory> |
| 8 |
| 9 #import "base/mac/foundation_util.h" |
| 10 #import "base/mac/scoped_nsobject.h" |
| 11 #import "ios/chrome/browser/signin/chrome_identity_service_observer_bridge.h" |
| 12 #import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrom
e.h" |
| 13 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_account_ite
m.h" |
| 14 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_footer_item
.h" |
| 15 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h
" |
| 16 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h" |
| 17 #import "ios/chrome/browser/ui/settings/utils/resized_avatar_cache.h" |
| 18 #import "ios/chrome/browser/ui/uikit_ui_util.h" |
| 19 #include "ios/chrome/grit/ios_chromium_strings.h" |
| 20 #include "ios/chrome/grit/ios_strings.h" |
| 21 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h" |
| 22 #import "ios/public/provider/chrome/browser/signin/chrome_identity.h" |
| 23 #include "ios/public/provider/chrome/browser/signin/chrome_identity_service.h" |
| 24 #import "ios/third_party/material_components_ios/src/components/AppBar/src/Mater
ialAppBar.h" |
| 25 #import "ios/third_party/material_components_ios/src/components/Palettes/src/Mat
erialPalettes.h" |
| 26 #import "ios/third_party/material_components_ios/src/components/Typography/src/M
aterialTypography.h" |
| 27 #import "ui/base/l10n/l10n_util.h" |
| 28 |
| 29 namespace { |
| 30 const CGFloat kHeaderViewMinHeight = 100.; |
| 31 const CGFloat kHeaderViewHeightMultiplier = 0.33; |
| 32 const CGFloat kContentViewBottomInset = 40.; |
| 33 |
| 34 typedef NS_ENUM(NSInteger, SectionIdentifier) { |
| 35 SectionIdentifierTitle = kSectionIdentifierEnumZero, |
| 36 SectionIdentifierAccounts, |
| 37 SectionIdentifierAddAccount, |
| 38 }; |
| 39 |
| 40 typedef NS_ENUM(NSInteger, ItemType) { |
| 41 ItemTypeTitle = kItemTypeEnumZero, |
| 42 ItemTypeAccount, |
| 43 ItemTypeAddAccount, |
| 44 }; |
| 45 } // namespace |
| 46 |
| 47 @interface SigninAccountSelectorViewController ()< |
| 48 ChromeIdentityServiceObserver> { |
| 49 std::unique_ptr<ChromeIdentityServiceObserverBridge> _identityServiceObserver; |
| 50 // Cache for account avatar images. |
| 51 base::scoped_nsobject<ResizedAvatarCache> _avatarCache; |
| 52 } |
| 53 @end |
| 54 |
| 55 @implementation SigninAccountSelectorViewController |
| 56 |
| 57 @synthesize delegate = _delegate; |
| 58 |
| 59 - (instancetype)init { |
| 60 self = [super initWithStyle:CollectionViewControllerStyleAppBar]; |
| 61 if (self) { |
| 62 _identityServiceObserver.reset( |
| 63 new ChromeIdentityServiceObserverBridge(self)); |
| 64 _avatarCache.reset([[ResizedAvatarCache alloc] init]); |
| 65 } |
| 66 return self; |
| 67 } |
| 68 |
| 69 #pragma mark - UIViewController |
| 70 |
| 71 - (void)viewDidLoad { |
| 72 [super viewDidLoad]; |
| 73 |
| 74 // Configure the header. |
| 75 MDCFlexibleHeaderView* headerView = |
| 76 self.appBar.headerViewController.headerView; |
| 77 headerView.canOverExtend = YES; |
| 78 headerView.maximumHeight = 200; |
| 79 headerView.backgroundColor = [UIColor whiteColor]; |
| 80 headerView.shiftBehavior = MDCFlexibleHeaderShiftBehaviorEnabled; |
| 81 [headerView addSubview:[self contentViewWithFrame:headerView.bounds]]; |
| 82 self.appBar.navigationBar.hidesBackButton = YES; |
| 83 self.collectionView.backgroundColor = [UIColor clearColor]; |
| 84 [headerView changeContentInsets:^{ |
| 85 UIEdgeInsets contentInset = self.collectionView.contentInset; |
| 86 contentInset.bottom += kContentViewBottomInset; |
| 87 self.collectionView.contentInset = contentInset; |
| 88 }]; |
| 89 |
| 90 // Load the contents of the collection view. |
| 91 [self loadModel]; |
| 92 } |
| 93 |
| 94 - (UIView*)contentViewWithFrame:(CGRect)frame { |
| 95 base::scoped_nsobject<UIView> contentView( |
| 96 [[UIView alloc] initWithFrame:frame]); |
| 97 contentView.get().autoresizingMask = |
| 98 (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); |
| 99 contentView.get().clipsToBounds = YES; |
| 100 |
| 101 base::scoped_nsobject<UILabel> titleLabel( |
| 102 [[UILabel alloc] initWithFrame:CGRectZero]); |
| 103 titleLabel.get().text = |
| 104 l10n_util::GetNSString(IDS_IOS_ACCOUNT_CONSISTENCY_SETUP_TITLE); |
| 105 titleLabel.get().textColor = [[MDCPalette greyPalette] tint900]; |
| 106 titleLabel.get().font = [MDCTypography headlineFont]; |
| 107 titleLabel.get().translatesAutoresizingMaskIntoConstraints = NO; |
| 108 |
| 109 base::scoped_nsobject<UIView> divider( |
| 110 [[UIView alloc] initWithFrame:CGRectZero]); |
| 111 divider.get().backgroundColor = [[MDCPalette greyPalette] tint300]; |
| 112 divider.get().translatesAutoresizingMaskIntoConstraints = NO; |
| 113 |
| 114 base::scoped_nsobject<UILayoutGuide> layoutGuide1( |
| 115 [[UILayoutGuide alloc] init]); |
| 116 base::scoped_nsobject<UILayoutGuide> layoutGuide2( |
| 117 [[UILayoutGuide alloc] init]); |
| 118 |
| 119 [contentView addSubview:titleLabel]; |
| 120 [contentView addSubview:divider]; |
| 121 [contentView addLayoutGuide:layoutGuide1]; |
| 122 [contentView addLayoutGuide:layoutGuide2]; |
| 123 |
| 124 NSDictionary* views = @{ |
| 125 @"title" : titleLabel, |
| 126 @"divider" : divider, |
| 127 @"v1" : layoutGuide1, |
| 128 @"v2" : layoutGuide2 |
| 129 }; |
| 130 NSArray* constraints = @[ |
| 131 @"V:[title]-(16)-[divider(==1)]|", |
| 132 @"H:|[v1(16)][title(<=440)][v2(>=v1)]|", |
| 133 @"H:|[divider]|", |
| 134 ]; |
| 135 ApplyVisualConstraints(constraints, views); |
| 136 return contentView.autorelease(); |
| 137 } |
| 138 |
| 139 - (void)viewWillLayoutSubviews { |
| 140 CGSize viewSize = self.view.bounds.size; |
| 141 MDCFlexibleHeaderView* headerView = |
| 142 self.appBar.headerViewController.headerView; |
| 143 headerView.maximumHeight = |
| 144 MAX(kHeaderViewMinHeight, kHeaderViewHeightMultiplier * viewSize.height); |
| 145 } |
| 146 |
| 147 #pragma mark - Model |
| 148 |
| 149 - (void)loadModel { |
| 150 [super loadModel]; |
| 151 CollectionViewModel* model = self.collectionViewModel; |
| 152 [model addSectionWithIdentifier:SectionIdentifierTitle]; |
| 153 [model addItem:[self titleItem] |
| 154 toSectionWithIdentifier:SectionIdentifierTitle]; |
| 155 |
| 156 [model addSectionWithIdentifier:SectionIdentifierAccounts]; |
| 157 NSArray* identities = ios::GetChromeBrowserProvider() |
| 158 ->GetChromeIdentityService() |
| 159 ->GetAllIdentitiesSortedForDisplay(); |
| 160 if ([identities count]) { |
| 161 for (NSUInteger i = 0; i < [identities count]; ++i) { |
| 162 [model addItem:[self accountItemForIdentity:identities[i] |
| 163 checked:(i == 0)] |
| 164 toSectionWithIdentifier:SectionIdentifierAccounts]; |
| 165 } |
| 166 [model addSectionWithIdentifier:SectionIdentifierAddAccount]; |
| 167 [model addItem:[self addAccountItem] |
| 168 toSectionWithIdentifier:SectionIdentifierAddAccount]; |
| 169 } |
| 170 } |
| 171 |
| 172 - (CollectionViewItem*)titleItem { |
| 173 // TODO(crbug.com/662549) : Rename FooterItem to be used as regular item. |
| 174 CollectionViewFooterItem* item = [[[CollectionViewFooterItem alloc] |
| 175 initWithType:ItemTypeTitle] autorelease]; |
| 176 item.text = |
| 177 l10n_util::GetNSString(IDS_IOS_ACCOUNT_CONSISTENCY_SETUP_DESCRIPTION); |
| 178 return item; |
| 179 } |
| 180 |
| 181 - (CollectionViewItem*)accountItemForIdentity:(ChromeIdentity*)identity |
| 182 checked:(BOOL)isChecked { |
| 183 CollectionViewAccountItem* item = [[[CollectionViewAccountItem alloc] |
| 184 initWithType:ItemTypeAccount] autorelease]; |
| 185 [self updateAccountItem:item withIdentity:identity]; |
| 186 if (isChecked) { |
| 187 item.accessoryType = MDCCollectionViewCellAccessoryCheckmark; |
| 188 } |
| 189 return item; |
| 190 } |
| 191 |
| 192 - (void)updateAccountItem:(CollectionViewAccountItem*)item |
| 193 withIdentity:(ChromeIdentity*)identity { |
| 194 item.image = [_avatarCache resizedAvatarForIdentity:identity]; |
| 195 item.text = identity.userEmail; |
| 196 item.chromeIdentity = identity; |
| 197 } |
| 198 |
| 199 - (CollectionViewItem*)addAccountItem { |
| 200 CollectionViewAccountItem* item = [[[CollectionViewAccountItem alloc] |
| 201 initWithType:ItemTypeAddAccount] autorelease]; |
| 202 item.text = l10n_util::GetNSString( |
| 203 IDS_IOS_ACCOUNT_CONSISTENCY_SETUP_ADD_ACCOUNT_BUTTON); |
| 204 item.image = [UIImage imageNamed:@"settings_accounts_add_account"]; |
| 205 return item; |
| 206 } |
| 207 |
| 208 - (ChromeIdentity*)selectedIdentity { |
| 209 NSArray* accountItems = [self.collectionViewModel |
| 210 itemsInSectionWithIdentifier:SectionIdentifierAccounts]; |
| 211 for (CollectionViewAccountItem* accountItem in accountItems) { |
| 212 if (accountItem.accessoryType == MDCCollectionViewCellAccessoryCheckmark) { |
| 213 return accountItem.chromeIdentity; |
| 214 } |
| 215 } |
| 216 return nil; |
| 217 } |
| 218 |
| 219 #pragma mark - UICollectionViewDelegate |
| 220 |
| 221 - (void)collectionView:(UICollectionView*)collectionView |
| 222 didSelectItemAtIndexPath:(NSIndexPath*)indexPath { |
| 223 [super collectionView:collectionView didSelectItemAtIndexPath:indexPath]; |
| 224 CollectionViewItem* item = |
| 225 [self.collectionViewModel itemAtIndexPath:indexPath]; |
| 226 if (item.type == ItemTypeAccount) { |
| 227 CollectionViewAccountItem* selectedAccountItem = |
| 228 base::mac::ObjCCastStrict<CollectionViewAccountItem>(item); |
| 229 // TODO(crbug.com/631486) : Checkmark animation. |
| 230 selectedAccountItem.accessoryType = MDCCollectionViewCellAccessoryCheckmark; |
| 231 |
| 232 base::scoped_nsobject<NSMutableArray<CollectionViewItem*>> reloadItems( |
| 233 [[NSMutableArray alloc] init]); |
| 234 [reloadItems addObject:selectedAccountItem]; |
| 235 |
| 236 // Uncheck all the other account items. |
| 237 NSArray* accountItems = [self.collectionViewModel |
| 238 itemsInSectionWithIdentifier:SectionIdentifierAccounts]; |
| 239 for (CollectionViewAccountItem* accountItem in accountItems) { |
| 240 if (accountItem != selectedAccountItem && |
| 241 accountItem.accessoryType != MDCCollectionViewCellAccessoryNone) { |
| 242 // TODO(crbug.com/631486) : Checkmark animation. |
| 243 accountItem.accessoryType = MDCCollectionViewCellAccessoryNone; |
| 244 [reloadItems addObject:accountItem]; |
| 245 } |
| 246 } |
| 247 [self reconfigureCellsForItems:reloadItems |
| 248 inSectionWithIdentifier:SectionIdentifierAccounts]; |
| 249 } else if (item.type == ItemTypeAddAccount) { |
| 250 [self.delegate accountSelectorControllerDidSelectAddAccount:self]; |
| 251 } |
| 252 } |
| 253 |
| 254 #pragma mark - ChromeIdentityServiceObserver |
| 255 |
| 256 - (void)onIdentityListChanged { |
| 257 ChromeIdentity* selectedIdentity = [self selectedIdentity]; |
| 258 [self loadModel]; |
| 259 |
| 260 // Reselect the identity. |
| 261 if (!selectedIdentity) { |
| 262 return; |
| 263 } |
| 264 NSArray* accountItems = [self.collectionViewModel |
| 265 itemsInSectionWithIdentifier:SectionIdentifierAccounts]; |
| 266 for (CollectionViewAccountItem* accountItem in accountItems) { |
| 267 if ([accountItem.chromeIdentity.gaiaID |
| 268 isEqualToString:selectedIdentity.gaiaID]) { |
| 269 accountItem.accessoryType = MDCCollectionViewCellAccessoryCheckmark; |
| 270 } else { |
| 271 accountItem.accessoryType = MDCCollectionViewCellAccessoryNone; |
| 272 } |
| 273 } |
| 274 } |
| 275 |
| 276 - (void)onChromeIdentityServiceWillBeDestroyed { |
| 277 _identityServiceObserver.reset(); |
| 278 } |
| 279 |
| 280 #pragma mark UICollectionViewDataSource |
| 281 |
| 282 - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView |
| 283 cellForItemAtIndexPath:(NSIndexPath*)indexPath { |
| 284 MDCCollectionViewCell* cell = |
| 285 [super collectionView:collectionView cellForItemAtIndexPath:indexPath]; |
| 286 CollectionViewItem* item = |
| 287 [self.collectionViewModel itemAtIndexPath:indexPath]; |
| 288 |
| 289 if (item.type == ItemTypeAccount || item.type == ItemTypeAddAccount) { |
| 290 CollectionViewAccountCell* accountCell = |
| 291 base::mac::ObjCCastStrict<CollectionViewAccountCell>(cell); |
| 292 accountCell.textLabel.font = [MDCTypography body1Font]; |
| 293 accountCell.textLabel.textColor = [[MDCPalette greyPalette] tint900]; |
| 294 } else if (item.type == ItemTypeTitle) { |
| 295 CollectionViewFooterCell* titleCell = |
| 296 base::mac::ObjCCastStrict<CollectionViewFooterCell>(cell); |
| 297 titleCell.textLabel.font = [MDCTypography body1Font]; |
| 298 titleCell.textLabel.textColor = [[MDCPalette greyPalette] tint900]; |
| 299 titleCell.horizontalPadding = 16; |
| 300 } |
| 301 cell.shouldHideSeparator = YES; |
| 302 return cell; |
| 303 } |
| 304 |
| 305 #pragma mark - MDCCollectionViewStylingDelegate |
| 306 |
| 307 - (CGFloat)collectionView:(UICollectionView*)collectionView |
| 308 cellHeightAtIndexPath:(NSIndexPath*)indexPath { |
| 309 CollectionViewItem* item = |
| 310 [self.collectionViewModel itemAtIndexPath:indexPath]; |
| 311 |
| 312 if (item.type == ItemTypeTitle) { |
| 313 return [MDCCollectionViewCell |
| 314 cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds) |
| 315 forItem:item]; |
| 316 } |
| 317 return MDCCellDefaultTwoLineHeight; |
| 318 } |
| 319 |
| 320 - (BOOL)collectionView:(UICollectionView*)collectionView |
| 321 hidesInkViewAtIndexPath:(NSIndexPath*)indexPath { |
| 322 NSInteger itemType = |
| 323 [self.collectionViewModel itemTypeForIndexPath:indexPath]; |
| 324 return (itemType == ItemTypeTitle); |
| 325 } |
| 326 |
| 327 - (BOOL)collectionView:(nonnull UICollectionView*)collectionView |
| 328 shouldHideItemBackgroundAtIndexPath:(nonnull NSIndexPath*)indexPath { |
| 329 return YES; |
| 330 } |
| 331 |
| 332 @end |
OLD | NEW |