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

Side by Side Diff: ios/chrome/browser/ui/authentication/signin_confirmation_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 #include "ios/chrome/browser/ui/authentication/signin_confirmation_view_controll er.h"
6
7 #import "base/ios/weak_nsobject.h"
8 #import "base/mac/foundation_util.h"
9 #import "base/mac/scoped_nsobject.h"
10 #include "base/metrics/user_metrics.h"
11 #import "base/strings/sys_string_conversions.h"
12 #include "components/google/core/browser/google_util.h"
13 #include "ios/chrome/browser/application_context.h"
14 #include "ios/chrome/browser/signin/chrome_identity_service_observer_bridge.h"
15 #import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrom e.h"
16 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_footer_item .h"
17 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
18 #import "ios/chrome/browser/ui/settings/cells/account_control_item.h"
19 #import "ios/chrome/browser/ui/uikit_ui_util.h"
20 #import "ios/chrome/common/string_util.h"
21 #include "ios/chrome/grit/ios_chromium_strings.h"
22 #include "ios/chrome/grit/ios_strings.h"
23 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
24 #include "ios/public/provider/chrome/browser/images/branded_image_provider.h"
25 #import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
26 #import "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
27 #include "ios/public/provider/chrome/browser/signin/signin_resources_provider.h"
28 #import "ios/third_party/material_components_ios/src/components/AppBar/src/Mater ialAppBar.h"
29 #import "ios/third_party/material_components_ios/src/components/Palettes/src/Mat erialPalettes.h"
30 #import "ios/third_party/material_components_ios/src/components/Typography/src/M aterialTypography.h"
31 #import "ui/base/l10n/l10n_util.h"
32 #include "url/gurl.h"
33
34 namespace {
35 const CGFloat kAccountImageDimension = 64.;
36 const CGFloat kHeaderViewMinHeight = 170.;
37 const CGFloat kHeaderViewHeightMultiplier = 0.33;
38 const CGFloat kContentViewBottomInset = 40.;
39 // Leading separator inset.
40 const CGFloat kLeadingSeparatorInset = 30.;
41 // Trailing separator inset.
42 const CGFloat kTrailingSeparatorInset = 16.;
43
44 UIImage* GetImageForIdentity(ChromeIdentity* identity) {
45 UIImage* image = ios::GetChromeBrowserProvider()
46 ->GetChromeIdentityService()
47 ->GetCachedAvatarForIdentity(identity);
48 if (!image) {
49 image = ios::GetChromeBrowserProvider()
50 ->GetSigninResourcesProvider()
51 ->GetDefaultAvatar();
52 // No cached image, trigger a fetch, which will notify all observers
53 // (including the corresponding AccountViewBase).
54 ios::GetChromeBrowserProvider()
55 ->GetChromeIdentityService()
56 ->GetAvatarForIdentity(identity, ^(UIImage*){
57 });
58 }
59 return image;
60 }
61
62 typedef NS_ENUM(NSInteger, SectionIdentifier) {
63 SectionIdentifierInfo = kSectionIdentifierEnumZero,
64 };
65
66 typedef NS_ENUM(NSInteger, ItemType) {
67 ItemTypeSync = kItemTypeEnumZero,
68 ItemTypeGoogleServices,
69 ItemTypeFooter,
70 };
71 }
72
73 #pragma mark - SigninConfirmationViewController
74
75 @interface SigninConfirmationViewController ()<
76 ChromeIdentityServiceObserver,
77 CollectionViewFooterLinkDelegate> {
78 base::scoped_nsobject<ChromeIdentity> _identity;
79 std::unique_ptr<ChromeIdentityServiceObserverBridge> _identityServiceObserver;
80 base::WeakNSObject<UIImage> _oldImage;
81 base::scoped_nsobject<UIImageView> _imageView;
82 base::scoped_nsobject<UILabel> _titleLabel;
83 base::scoped_nsobject<UILabel> _emailLabel;
84 }
85 @end
86
87 @implementation SigninConfirmationViewController
88
89 @synthesize delegate;
90
91 - (instancetype)initWithIdentity:(ChromeIdentity*)identity {
92 self = [super initWithStyle:CollectionViewControllerStyleAppBar];
93 if (self) {
94 _identity.reset([identity retain]);
95 _identityServiceObserver.reset(
96 new ChromeIdentityServiceObserverBridge(self));
97 }
98 return self;
99 }
100
101 - (void)scrollToBottom {
102 CGPoint bottomOffset = CGPointMake(
103 0, self.collectionView.contentSize.height -
104 self.collectionView.bounds.size.height + kContentViewBottomInset);
105 [self.collectionView setContentOffset:bottomOffset animated:YES];
106 }
107
108 #pragma mark - UIViewController
109
110 - (void)viewDidLoad {
111 [super viewDidLoad];
112
113 // Configure the header.
114 MDCFlexibleHeaderView* headerView =
115 self.appBar.headerViewController.headerView;
116 headerView.canOverExtend = YES;
117 headerView.maximumHeight = 200;
118 headerView.shiftBehavior = MDCFlexibleHeaderShiftBehaviorEnabled;
119 headerView.backgroundColor = [UIColor whiteColor];
120 [headerView addSubview:[self contentViewWithFrame:headerView.bounds]];
121 self.appBar.navigationBar.hidesBackButton = YES;
122 self.collectionView.backgroundColor = [UIColor clearColor];
123 [headerView changeContentInsets:^{
124 UIEdgeInsets contentInset = self.collectionView.contentInset;
125 contentInset.bottom += kContentViewBottomInset;
126 self.collectionView.contentInset = contentInset;
127 }];
128
129 // Load the contents of the collection view.
130 [self loadModel];
131 }
132
133 - (void)viewDidAppear:(BOOL)animated {
134 [super viewDidAppear:animated];
135
136 BOOL didSend = [self
137 sendDidReachBottomIfNecessary:self.collectionView.collectionViewLayout
138 .collectionViewContentSize.height];
139 if (!didSend && [self isMovingToParentViewController]) {
140 // The confirmation screen just appeared and there wasn't enough space to
141 // show the full screen (since the scroll hasn't reach the botton). This
142 // means the "More" button is actually necessary.
143 base::RecordAction(base::UserMetricsAction("Signin_MoreButton_Shown"));
144 }
145 }
146
147 - (UIView*)contentViewWithFrame:(CGRect)frame {
148 base::scoped_nsobject<UIView> contentView(
149 [[UIView alloc] initWithFrame:frame]);
150 contentView.get().autoresizingMask =
151 (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
152 contentView.get().clipsToBounds = YES;
153 _imageView.reset([[UIImageView alloc] init]);
154 _imageView.get().translatesAutoresizingMaskIntoConstraints = NO;
155
156 _titleLabel.reset([[UILabel alloc] initWithFrame:CGRectZero]);
157 _titleLabel.get().textColor = [[MDCPalette greyPalette] tint900];
158 _titleLabel.get().font = [MDCTypography headlineFont];
159 _titleLabel.get().translatesAutoresizingMaskIntoConstraints = NO;
160
161 _emailLabel.reset([[UILabel alloc] initWithFrame:CGRectZero]);
162 _emailLabel.get().textColor = [[MDCPalette greyPalette] tint700];
163 _emailLabel.get().font = [MDCTypography body1Font];
164 _emailLabel.get().translatesAutoresizingMaskIntoConstraints = NO;
165
166 [self updateViewWithIdentity:_identity];
167
168 base::scoped_nsobject<UIView> divider(
169 [[UIView alloc] initWithFrame:CGRectZero]);
170 divider.get().backgroundColor = [[MDCPalette greyPalette] tint300];
171 divider.get().translatesAutoresizingMaskIntoConstraints = NO;
172
173 base::scoped_nsobject<UILayoutGuide> layoutGuide1(
174 [[UILayoutGuide alloc] init]);
175 base::scoped_nsobject<UILayoutGuide> layoutGuide2(
176 [[UILayoutGuide alloc] init]);
177
178 [contentView addSubview:_imageView];
179 [contentView addSubview:_titleLabel];
180 [contentView addSubview:_emailLabel];
181 [contentView addSubview:divider];
182 [contentView addLayoutGuide:layoutGuide1];
183 [contentView addLayoutGuide:layoutGuide2];
184
185 NSDictionary* views = @{
186 @"image" : _imageView,
187 @"title" : _titleLabel,
188 @"email" : _emailLabel,
189 @"divider" : divider,
190 @"v1" : layoutGuide1,
191 @"v2" : layoutGuide2
192 };
193 NSArray* constraints = @[
194 @"V:[image]-(24)-[title]-(8)-[email]-(16)-[divider(==1)]|",
195 @"H:|[v1][image]",
196 @"H:|[v1(16)][title(<=440)][v2(>=v1)]|",
197 @"H:|[v1][email]",
198 @"H:|[divider]|",
199 ];
200 ApplyVisualConstraints(constraints, views);
201 return contentView.autorelease();
202 }
203
204 - (void)viewWillLayoutSubviews {
205 CGSize viewSize = self.view.bounds.size;
206 MDCFlexibleHeaderView* headerView =
207 self.appBar.headerViewController.headerView;
208 headerView.maximumHeight =
209 MAX(kHeaderViewMinHeight, kHeaderViewHeightMultiplier * viewSize.height);
210 }
211
212 - (void)updateViewWithIdentity:(ChromeIdentity*)identity {
213 UIImage* identityImage = GetImageForIdentity(identity);
214 if (_oldImage.get() != identityImage) {
215 _oldImage.reset(identityImage);
216 identityImage =
217 ResizeImage(identityImage,
218 CGSizeMake(kAccountImageDimension, kAccountImageDimension),
219 ProjectionMode::kAspectFit);
220 identityImage =
221 CircularImageFromImage(identityImage, kAccountImageDimension);
222 _imageView.get().image = identityImage;
223 }
224
225 _titleLabel.get().text = l10n_util::GetNSStringF(
226 IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_TITLE,
227 base::SysNSStringToUTF16([identity userFullName]));
228
229 _emailLabel.get().text = [identity userEmail];
230 }
231
232 #pragma mark - Model
233
234 - (void)loadModel {
235 [super loadModel];
236 CollectionViewModel* model = self.collectionViewModel;
237
238 [model addSectionWithIdentifier:SectionIdentifierInfo];
239 [model addItem:[self syncItem] toSectionWithIdentifier:SectionIdentifierInfo];
240 [model addItem:[self googleServicesItem]
241 toSectionWithIdentifier:SectionIdentifierInfo];
242 [model addItem:[self openSettingsItem]
243 toSectionWithIdentifier:SectionIdentifierInfo];
244 }
245
246 #pragma mark - Model items
247
248 - (CollectionViewItem*)syncItem {
249 AccountControlItem* item =
250 [[[AccountControlItem alloc] initWithType:ItemTypeSync] autorelease];
251 item.text = l10n_util::GetNSString(
252 IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_SYNC_TITLE);
253 item.detailText = l10n_util::GetNSString(
254 IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_SYNC_DESCRIPTION);
255 item.image = ios::GetChromeBrowserProvider()
256 ->GetBrandedImageProvider()
257 ->GetSigninConfirmationSyncSettingsImage();
258 return item;
259 }
260
261 - (CollectionViewItem*)googleServicesItem {
262 AccountControlItem* item = [[[AccountControlItem alloc]
263 initWithType:ItemTypeGoogleServices] autorelease];
264 item.text = l10n_util::GetNSString(
265 IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_SERVICES_TITLE);
266 item.detailText = l10n_util::GetNSString(
267 IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_SERVICES_DESCRIPTION);
268 item.image = ios::GetChromeBrowserProvider()
269 ->GetBrandedImageProvider()
270 ->GetSigninConfirmationPersonalizeServicesImage();
271 return item;
272 }
273
274 - (CollectionViewItem*)openSettingsItem {
275 CollectionViewFooterItem* item = [[[CollectionViewFooterItem alloc]
276 initWithType:ItemTypeFooter] autorelease];
277 item.text = l10n_util::GetNSString(
278 IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_OPEN_SETTINGS);
279 item.linkURL = google_util::AppendGoogleLocaleParam(
280 GURL("internal://settings-sync"),
281 GetApplicationContext()->GetApplicationLocale());
282 item.linkDelegate = self;
283 return item;
284 }
285
286 #pragma mark - Helpers
287
288 // Calls |signinConfirmationControllerDidReachBottom:| on |delegate| if
289 // the collection view has reached its bottom based on a content size height of
290 // |contentSizeHeight|. Returns whether the delegate was notified.
291 - (BOOL)sendDidReachBottomIfNecessary:(CGFloat)contentSizeHeight {
292 if (contentSizeHeight &&
293 self.collectionView.contentOffset.y +
294 self.collectionView.frame.size.height >=
295 contentSizeHeight) {
296 [self.delegate signinConfirmationControllerDidReachBottom:self];
297 return YES;
298 }
299 return NO;
300 }
301
302 #pragma mark - ChromeIdentityServiceObserver
303
304 - (void)onProfileUpdate:(ChromeIdentity*)identity {
305 if (identity == _identity) {
306 [self updateViewWithIdentity:identity];
307 }
308 }
309
310 - (void)onChromeIdentityServiceWillBeDestroyed {
311 _identityServiceObserver.reset();
312 }
313
314 #pragma mark - UIScrollViewDelegate
315
316 - (void)scrollViewDidScroll:(UIScrollView*)scrollView {
317 [super scrollViewDidScroll:scrollView];
318
319 if (scrollView == self.collectionView) {
320 [self sendDidReachBottomIfNecessary:scrollView.contentSize.height];
321 }
322 }
323
324 #pragma mark UICollectionViewDataSource
325
326 - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
327 cellForItemAtIndexPath:(NSIndexPath*)indexPath {
328 MDCCollectionViewCell* cell =
329 [super collectionView:collectionView cellForItemAtIndexPath:indexPath];
330
331 NSInteger itemType =
332 [self.collectionViewModel itemTypeForIndexPath:indexPath];
333 switch (itemType) {
334 case ItemTypeSync:
335 cell.separatorInset = UIEdgeInsetsMake(0, kLeadingSeparatorInset, 0,
336 kTrailingSeparatorInset);
337 break;
338 case ItemTypeGoogleServices:
339 cell.shouldHideSeparator = YES;
340 break;
341 case ItemTypeFooter: {
342 CollectionViewFooterCell* footerCell =
343 base::mac::ObjCCastStrict<CollectionViewFooterCell>(cell);
344 // TODO(crbug.com/664648): Must use atomic text formatting operation due
345 // to LabelLinkController bug.
346 footerCell.textLabel.attributedText = [[[NSAttributedString alloc]
347 initWithString:footerCell.textLabel.text
348 attributes:@{
349 NSFontAttributeName : [MDCTypography body1Font],
350 NSForegroundColorAttributeName :
351 [[MDCPalette greyPalette] tint700]
352 }] autorelease];
353 footerCell.horizontalPadding = 16;
354 break;
355 }
356
357 default:
358 break;
359 }
360 return cell;
361 }
362
363 #pragma mark - MDCCollectionViewStylingDelegate
364
365 - (CGFloat)collectionView:(UICollectionView*)collectionView
366 cellHeightAtIndexPath:(NSIndexPath*)indexPath {
367 CollectionViewItem* item =
368 [self.collectionViewModel itemAtIndexPath:indexPath];
369
370 // ItemTypeSync, ItemTypeGoogleServices, and ItemTypeFooter all support
371 // dynamic height calculation.
372 return [MDCCollectionViewCell
373 cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds)
374 forItem:item];
375 }
376
377 - (BOOL)collectionView:(UICollectionView*)collectionView
378 hidesInkViewAtIndexPath:(NSIndexPath*)indexPath {
379 return YES;
380 }
381
382 - (BOOL)collectionView:(nonnull UICollectionView*)collectionView
383 shouldHideItemBackgroundAtIndexPath:(nonnull NSIndexPath*)indexPath {
384 return YES;
385 }
386
387 #pragma mark - CollectionViewFooterLinkDelegate
388
389 - (void)cell:(CollectionViewFooterCell*)cell didTapLinkURL:(GURL)URL {
390 [[self delegate] signinConfirmationControllerDidTapSettingsLink:self];
391 }
392
393 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698