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

Side by Side Diff: ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.mm

Issue 2586993002: Upstream Chrome on iOS source code [3/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 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 #import "ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller .h"
6
7 #import "base/mac/foundation_util.h"
8 #include "components/signin/core/browser/account_tracker_service.h"
9 #include "components/signin/core/browser/profile_oauth2_token_service.h"
10 #include "components/signin/ios/browser/oauth2_token_service_observer_bridge.h"
11 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
12 #include "ios/chrome/browser/signin/account_tracker_service_factory.h"
13 #include "ios/chrome/browser/signin/authentication_service.h"
14 #include "ios/chrome/browser/signin/authentication_service_factory.h"
15 #include "ios/chrome/browser/signin/chrome_identity_service_observer_bridge.h"
16 #include "ios/chrome/browser/signin/oauth2_token_service_factory.h"
17 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_account_ite m.h"
18 #import "ios/chrome/browser/ui/collection_view/collection_view_controller.h"
19 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
20 #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
21 #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
22 #import "ios/chrome/browser/ui/commands/generic_chrome_command.h"
23 #include "ios/chrome/browser/ui/commands/ios_command_ids.h"
24 #import "ios/chrome/browser/ui/settings/utils/resized_avatar_cache.h"
25 #import "ios/chrome/browser/ui/uikit_ui_util.h"
26 #include "ios/chrome/grit/ios_chromium_strings.h"
27 #include "ios/chrome/grit/ios_strings.h"
28 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
29 #import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
30 #import "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
31 #include "ios/public/provider/chrome/browser/signin/signin_resources_provider.h"
32 #import "ios/third_party/material_components_ios/src/components/Buttons/src/Mate rialButtons.h"
33 #import "ios/third_party/material_components_ios/src/components/Dialogs/src/Mate rialDialogs.h"
34 #import "ios/third_party/material_components_ios/src/components/Palettes/src/Mat erialPalettes.h"
35 #import "ios/third_party/material_components_ios/src/components/Typography/src/M aterialTypography.h"
36 #include "ui/base/l10n/l10n_util_mac.h"
37
38 namespace {
39
40 const int kMaxShownAccounts = 3;
41 const CGFloat kAccountsExtraBottomInset = 16;
42 const CGFloat kVerticalPadding = 24;
43 const CGFloat kButtonVerticalPadding = 16;
44 const CGFloat kHorizontalPadding = 24;
45 const CGFloat kAccountsHorizontalPadding = 8;
46 const CGFloat kButtonHorizontalPadding = 16;
47 const CGFloat kBetweenButtonsPadding = 8;
48 const CGFloat kMDCMinHorizontalPadding = 20;
49 const CGFloat kDialogMaxWidth = 328;
50
51 typedef NS_ENUM(NSInteger, SectionIdentifier) {
52 SectionIdentifierAccounts = kSectionIdentifierEnumZero,
53 };
54
55 typedef NS_ENUM(NSInteger, ItemType) {
56 ItemTypeAccount = kItemTypeEnumZero,
57 };
58
59 // Whether the Signed In Accounts view is currently being shown.
60 BOOL gSignedInAccountsViewControllerIsShown = NO;
61
62 } // namespace
63
64 @interface SignedInAccountsCollectionViewController
65 : CollectionViewController<ChromeIdentityServiceObserver> {
66 ios::ChromeBrowserState* _browserState; // Weak.
67 std::unique_ptr<ChromeIdentityServiceObserverBridge> _identityServiceObserver;
68 base::scoped_nsobject<ResizedAvatarCache> _avatarCache;
69
70 // Enable lookup of item corresponding to a given identity GAIA ID string.
71 base::scoped_nsobject<NSDictionary<NSString*, CollectionViewItem*>>
72 _identityMap;
73 }
74 @end
75
76 @implementation SignedInAccountsCollectionViewController
77
78 - (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState {
79 self = [super initWithStyle:CollectionViewControllerStyleDefault];
80 if (self) {
81 _browserState = browserState;
82 _avatarCache.reset([[ResizedAvatarCache alloc] init]);
83 _identityServiceObserver.reset(
84 new ChromeIdentityServiceObserverBridge(self));
85 [self loadModel];
86 }
87 return self;
88 }
89
90 - (void)viewDidLoad {
91 [super viewDidLoad];
92
93 self.styler.shouldHideSeparators = YES;
94 self.collectionView.backgroundColor = [UIColor clearColor];
95
96 // Add an inset at the bottom so the user can see whether it is possible to
97 // scroll to see additional accounts.
98 UIEdgeInsets contentInset = self.collectionView.contentInset;
99 contentInset.bottom += kAccountsExtraBottomInset;
100 self.collectionView.contentInset = contentInset;
101 }
102
103 #pragma mark CollectionViewController
104
105 - (void)loadModel {
106 [super loadModel];
107 CollectionViewModel* model = self.collectionViewModel;
108 NSMutableDictionary<NSString*, CollectionViewItem*>* mutableIdentityMap =
109 [[[NSMutableDictionary alloc] init] autorelease];
110
111 [model addSectionWithIdentifier:SectionIdentifierAccounts];
112 ProfileOAuth2TokenService* oauth2_service =
113 OAuth2TokenServiceFactory::GetForBrowserState(_browserState);
114 AccountTrackerService* accountTracker =
115 ios::AccountTrackerServiceFactory::GetForBrowserState(_browserState);
116 for (const std::string& account_id : oauth2_service->GetAccounts()) {
117 AccountInfo account = accountTracker->GetAccountInfo(account_id);
118 ChromeIdentity* identity = ios::GetChromeBrowserProvider()
119 ->GetChromeIdentityService()
120 ->GetIdentityWithGaiaID(account.gaia);
121 CollectionViewItem* item = [self accountItem:identity];
122 [model addItem:item toSectionWithIdentifier:SectionIdentifierAccounts];
123 [mutableIdentityMap setObject:item forKey:identity.gaiaID];
124 }
125 _identityMap.reset([mutableIdentityMap retain]);
126 }
127
128 #pragma mark Model objects
129
130 - (CollectionViewItem*)accountItem:(ChromeIdentity*)identity {
131 CollectionViewAccountItem* item = [[[CollectionViewAccountItem alloc]
132 initWithType:ItemTypeAccount] autorelease];
133 [self updateAccountItem:item withIdentity:identity];
134 return item;
135 }
136
137 - (void)updateAccountItem:(CollectionViewAccountItem*)item
138 withIdentity:(ChromeIdentity*)identity {
139 item.image = [_avatarCache resizedAvatarForIdentity:identity];
140 item.text = [identity userFullName];
141 item.detailText = [identity userEmail];
142 item.chromeIdentity = identity;
143 }
144
145 #pragma mark MDCCollectionViewStylingDelegate
146
147 - (BOOL)collectionView:(UICollectionView*)collectionView
148 shouldHideItemBackgroundAtIndexPath:(NSIndexPath*)indexPath {
149 return YES;
150 }
151
152 - (CGFloat)collectionView:(UICollectionView*)collectionView
153 cellHeightAtIndexPath:(NSIndexPath*)indexPath {
154 return MDCCellDefaultOneLineWithAvatarHeight;
155 }
156
157 - (BOOL)collectionView:(UICollectionView*)collectionView
158 hidesInkViewAtIndexPath:(NSIndexPath*)indexPath {
159 return YES;
160 }
161
162 #pragma mark ChromeIdentityServiceObserver
163
164 - (void)onProfileUpdate:(ChromeIdentity*)identity {
165 CollectionViewAccountItem* item =
166 base::mac::ObjCCastStrict<CollectionViewAccountItem>(
167 [_identityMap objectForKey:identity.gaiaID]);
168 [self updateAccountItem:item withIdentity:identity];
169 NSIndexPath* indexPath =
170 [self.collectionViewModel indexPathForItem:item
171 inSectionWithIdentifier:SectionIdentifierAccounts];
172 [self.collectionView reloadItemsAtIndexPaths:@[ indexPath ]];
173 }
174
175 - (void)onChromeIdentityServiceWillBeDestroyed {
176 _identityServiceObserver.reset();
177 }
178
179 @end
180
181 @interface SignedInAccountsViewController ()<
182 OAuth2TokenServiceObserverBridgeDelegate> {
183 ios::ChromeBrowserState* _browserState; // Weak.
184 std::unique_ptr<OAuth2TokenServiceObserverBridge> _tokenServiceObserver;
185 base::scoped_nsobject<MDCDialogTransitionController> _transitionController;
186
187 base::scoped_nsobject<UILabel> _titleLabel;
188 base::scoped_nsobject<SignedInAccountsCollectionViewController>
189 _accountsCollection;
190 base::scoped_nsobject<UILabel> _infoLabel;
191 base::scoped_nsobject<MDCButton> _primaryButton;
192 base::scoped_nsobject<MDCButton> _secondaryButton;
193 }
194 @end
195
196 @implementation SignedInAccountsViewController
197
198 + (BOOL)shouldBePresentedForBrowserState:
199 (ios::ChromeBrowserState*)browserState {
200 if (!browserState || browserState->IsOffTheRecord()) {
201 return NO;
202 }
203 AuthenticationService* authService =
204 AuthenticationServiceFactory::GetForBrowserState(browserState);
205 return !gSignedInAccountsViewControllerIsShown &&
206 authService->IsAuthenticated() && authService->HaveAccountsChanged();
207 }
208
209 #pragma mark Initialization
210
211 - (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState {
212 self = [super initWithNibName:nil bundle:nil];
213 if (self) {
214 _browserState = browserState;
215 _tokenServiceObserver.reset(new OAuth2TokenServiceObserverBridge(
216 OAuth2TokenServiceFactory::GetForBrowserState(_browserState), self));
217 _transitionController.reset([[MDCDialogTransitionController alloc] init]);
218 self.modalPresentationStyle = UIModalPresentationCustom;
219 self.transitioningDelegate = _transitionController;
220 }
221 return self;
222 }
223
224 - (void)dismiss {
225 [self.presentingViewController dismissViewControllerAnimated:YES
226 completion:nil];
227 }
228
229 - (void)dealloc {
230 [_primaryButton removeTarget:self
231 action:@selector(onPrimaryButtonPressed:)
232 forControlEvents:UIControlEventTouchDown];
233 [_secondaryButton removeTarget:self
234 action:@selector(onSecondaryButtonPressed:)
235 forControlEvents:UIControlEventTouchDown];
236 [super dealloc];
237 }
238
239 #pragma mark UIViewController
240
241 - (CGSize)preferredContentSize {
242 CGFloat width = MIN(kDialogMaxWidth,
243 self.presentingViewController.view.bounds.size.width -
244 2 * kMDCMinHorizontalPadding);
245 OAuth2TokenService* token_service =
246 OAuth2TokenServiceFactory::GetForBrowserState(_browserState);
247 int shownAccounts =
248 MIN(kMaxShownAccounts, token_service->GetAccounts().size());
249 CGSize maxSize = CGSizeMake(width - 2 * kHorizontalPadding, CGFLOAT_MAX);
250 CGSize buttonSize = [_primaryButton sizeThatFits:maxSize];
251 CGSize infoSize = [_infoLabel sizeThatFits:maxSize];
252 CGSize titleSize = [_titleLabel sizeThatFits:maxSize];
253 CGFloat height = kVerticalPadding + titleSize.height + kVerticalPadding +
254 shownAccounts * MDCCellDefaultOneLineWithAvatarHeight +
255 kVerticalPadding + infoSize.height + kVerticalPadding +
256 buttonSize.height + kButtonVerticalPadding;
257 return CGSizeMake(width, height);
258 }
259
260 - (void)viewDidLoad {
261 [super viewDidLoad];
262
263 self.view.backgroundColor = [UIColor whiteColor];
264
265 _titleLabel.reset([[UILabel alloc] initWithFrame:CGRectZero]);
266 _titleLabel.get().text =
267 l10n_util::GetNSString(IDS_IOS_SIGNED_IN_ACCOUNTS_VIEW_TITLE);
268 _titleLabel.get().textColor = [[MDCPalette greyPalette] tint900];
269 _titleLabel.get().font = [MDCTypography headlineFont];
270 _titleLabel.get().translatesAutoresizingMaskIntoConstraints = NO;
271 [self.view addSubview:_titleLabel];
272
273 _accountsCollection.reset([[SignedInAccountsCollectionViewController alloc]
274 initWithBrowserState:_browserState]);
275 _accountsCollection.get().view.translatesAutoresizingMaskIntoConstraints = NO;
276 [self addChildViewController:_accountsCollection];
277 [self.view addSubview:_accountsCollection.get().view];
278 [_accountsCollection didMoveToParentViewController:self];
279
280 _infoLabel.reset([[UILabel alloc] initWithFrame:CGRectZero]);
281 _infoLabel.get().text =
282 l10n_util::GetNSString(IDS_IOS_SIGNED_IN_ACCOUNTS_VIEW_INFO);
283 _infoLabel.get().numberOfLines = 0;
284 _infoLabel.get().textColor = [[MDCPalette greyPalette] tint700];
285 _infoLabel.get().font = [MDCTypography body1Font];
286 _infoLabel.get().translatesAutoresizingMaskIntoConstraints = NO;
287 [self.view addSubview:_infoLabel];
288
289 _primaryButton.reset([[MDCFlatButton alloc] init]);
290 [_primaryButton addTarget:self
291 action:@selector(onPrimaryButtonPressed:)
292 forControlEvents:UIControlEventTouchUpInside];
293 [_primaryButton
294 setTitle:l10n_util::GetNSString(IDS_IOS_SIGNED_IN_ACCOUNTS_VIEW_OK_BUTTON)
295 forState:UIControlStateNormal];
296 [_primaryButton setBackgroundColor:[[MDCPalette cr_bluePalette] tint500]
297 forState:UIControlStateNormal];
298 _primaryButton.get().customTitleColor = [UIColor whiteColor];
299 _primaryButton.get().underlyingColorHint = [UIColor blackColor];
300 _primaryButton.get().inkColor = [UIColor colorWithWhite:1 alpha:0.2f];
301 _primaryButton.get().translatesAutoresizingMaskIntoConstraints = NO;
302 [self.view addSubview:_primaryButton];
303
304 _secondaryButton.reset([[MDCFlatButton alloc] init]);
305 [_secondaryButton addTarget:self
306 action:@selector(onSecondaryButtonPressed:)
307 forControlEvents:UIControlEventTouchUpInside];
308 [_secondaryButton
309 setTitle:l10n_util::GetNSString(
310 IDS_IOS_SIGNED_IN_ACCOUNTS_VIEW_SETTINGS_BUTTON)
311 forState:UIControlStateNormal];
312 [_secondaryButton setBackgroundColor:[UIColor whiteColor]
313 forState:UIControlStateNormal];
314 _secondaryButton.get().customTitleColor =
315 [[MDCPalette cr_bluePalette] tint500];
316 _secondaryButton.get().underlyingColorHint = [UIColor whiteColor];
317 _secondaryButton.get().inkColor = [UIColor colorWithWhite:0 alpha:0.06f];
318 _secondaryButton.get().translatesAutoresizingMaskIntoConstraints = NO;
319 [self.view addSubview:_secondaryButton];
320
321 NSDictionary* views = @{
322 @"title" : _titleLabel,
323 @"accounts" : _accountsCollection.get().view,
324 @"info" : _infoLabel,
325 @"primaryButton" : _primaryButton,
326 @"secondaryButton" : _secondaryButton,
327 };
328 NSDictionary* metrics = @{
329 @"verticalPadding" : @(kVerticalPadding),
330 @"accountsVerticalPadding" :
331 @(kVerticalPadding - kAccountsExtraBottomInset),
332 @"buttonVerticalPadding" : @(kButtonVerticalPadding),
333 @"horizontalPadding" : @(kHorizontalPadding),
334 @"accountsHorizontalPadding" : @(kAccountsHorizontalPadding),
335 @"buttonHorizontalPadding" : @(kButtonHorizontalPadding),
336 @"betweenButtonsPadding" : @(kBetweenButtonsPadding),
337 };
338 NSArray* constraints = @[
339 @"V:|-(verticalPadding)-[title]-(verticalPadding)-[accounts]",
340 @"V:[accounts]-(accountsVerticalPadding)-[info]",
341 @"V:[info]-(verticalPadding)-[primaryButton]-(buttonVerticalPadding)-|",
342 @"V:[info]-(verticalPadding)-[secondaryButton]-(buttonVerticalPadding)-|",
343 @"H:|-(horizontalPadding)-[title]-(horizontalPadding)-|",
344 @"H:|-(accountsHorizontalPadding)-[accounts]-(accountsHorizontalPadding)-|",
345 @"H:|-(horizontalPadding)-[info]-(horizontalPadding)-|",
346 @"H:[secondaryButton]-(betweenButtonsPadding)-[primaryButton]",
347 @"H:[primaryButton]-(buttonHorizontalPadding)-|",
348 ];
349
350 ApplyVisualConstraintsWithMetrics(constraints, views, metrics);
351 }
352
353 - (void)viewWillAppear:(BOOL)animated {
354 [super viewWillAppear:animated];
355 if ([self isBeingPresented] || [self isMovingToParentViewController]) {
356 gSignedInAccountsViewControllerIsShown = YES;
357 }
358 }
359
360 - (void)viewWillDisappear:(BOOL)animated {
361 [super viewWillDisappear:animated];
362 if ([self isBeingDismissed] || [self isMovingFromParentViewController]) {
363 gSignedInAccountsViewControllerIsShown = NO;
364 }
365 }
366
367 #pragma mark Events
368
369 - (void)onPrimaryButtonPressed:(id)sender {
370 [self dismiss];
371 }
372
373 - (void)onSecondaryButtonPressed:(id)sender {
374 [self dismiss];
375 base::scoped_nsobject<GenericChromeCommand> showAccountsSettingsCommand(
376 [[GenericChromeCommand alloc] initWithTag:IDC_SHOW_ACCOUNTS_SETTINGS]);
377 [self chromeExecuteCommand:showAccountsSettingsCommand];
378 }
379
380 #pragma mark OAuth2TokenServiceObserverBridgeDelegate
381
382 - (void)onEndBatchChanges {
383 ProfileOAuth2TokenService* tokenService =
384 OAuth2TokenServiceFactory::GetForBrowserState(_browserState);
385 if (tokenService->GetAccounts().empty()) {
386 [self dismiss];
387 return;
388 }
389 [_accountsCollection loadModel];
390 [_accountsCollection.get().collectionView reloadData];
391 }
392
393 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698