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 #import "ios/chrome/browser/ui/autofill/cells/cvc_item.h" |
| 6 |
| 7 #include "components/strings/grit/components_strings.h" |
| 8 #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h" |
| 9 #include "ios/chrome/grit/ios_strings.h" |
| 10 #import "ios/public/provider/chrome/browser/chrome_browser_provider.h" |
| 11 #import "ios/public/provider/chrome/browser/ui/text_field_styling.h" |
| 12 #import "ios/third_party/material_components_ios/src/components/Palettes/src/Mat
erialPalettes.h" |
| 13 #import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoF
ontLoader.h" |
| 14 #include "ui/base/l10n/l10n_util.h" |
| 15 #include "ui/base/resource/resource_bundle.h" |
| 16 |
| 17 #if !defined(__has_feature) || !__has_feature(objc_arc) |
| 18 #error "This file requires ARC support." |
| 19 #endif |
| 20 |
| 21 namespace { |
| 22 // Padding used on the leading and trailing edges of the cell. |
| 23 const CGFloat kHorizontalPadding = 16; |
| 24 // Padding used on the top and bottom edges of the cell. |
| 25 const CGFloat kVerticalPadding = 16; |
| 26 // Spacing between elements. |
| 27 const CGFloat kUISpacing = 5; |
| 28 // Spacing around the CVC container. |
| 29 const CGFloat kUICVCSpacing = 20; |
| 30 // Height of the different text fields. |
| 31 const CGFloat kTextFieldHeight = 50; |
| 32 // Width of the date text fields. |
| 33 const CGFloat kDateTextFieldWidth = 40; |
| 34 // Width of the CVC text field. |
| 35 const CGFloat kCVCTextFieldWidth = 60; |
| 36 } |
| 37 |
| 38 @interface CVCCell ()<UITextFieldDelegate> |
| 39 @property(nonatomic, strong) UILabel* dateSeparator; |
| 40 @property(nonatomic, strong) UIView* dateContainerView; |
| 41 @property(nonatomic, strong) UIView* CVCContainerView; |
| 42 @property(nonatomic, strong) |
| 43 NSLayoutConstraint* CVCContainerLeadingConstraintWithDate; |
| 44 @property(nonatomic, strong) |
| 45 NSLayoutConstraint* CVCContainerLeadingConstraintWithoutDate; |
| 46 @end |
| 47 |
| 48 @implementation CVCItem |
| 49 |
| 50 @synthesize instructionsText = _instructionsText; |
| 51 @synthesize errorMessage = _errorMessage; |
| 52 @synthesize monthText = _monthText; |
| 53 @synthesize yearText = _yearText; |
| 54 @synthesize CVCText = _CVCText; |
| 55 @synthesize showDateInput = _showDateInput; |
| 56 @synthesize showNewCardButton = _showNewCardButton; |
| 57 @synthesize showDateInputError = _showDateInputError; |
| 58 @synthesize showCVCInputError = _showCVCInputError; |
| 59 @synthesize CVCImageResourceID = _CVCImageResourceID; |
| 60 |
| 61 - (instancetype)initWithType:(NSInteger)type { |
| 62 self = [super initWithType:type]; |
| 63 if (self) { |
| 64 self.cellClass = [CVCCell class]; |
| 65 } |
| 66 return self; |
| 67 } |
| 68 |
| 69 #pragma mark CollectionViewItem |
| 70 |
| 71 - (void)configureCell:(CVCCell*)cell { |
| 72 [super configureCell:cell]; |
| 73 cell.instructionsTextLabel.text = self.instructionsText; |
| 74 cell.errorLabel.text = self.errorMessage; |
| 75 |
| 76 cell.monthInput.text = self.monthText; |
| 77 cell.yearInput.text = self.yearText; |
| 78 cell.CVCInput.text = self.CVCText; |
| 79 |
| 80 cell.dateContainerView.hidden = !self.showDateInput; |
| 81 |
| 82 cell.CVCContainerLeadingConstraintWithDate.active = self.showDateInput; |
| 83 cell.CVCContainerLeadingConstraintWithoutDate.active = !self.showDateInput; |
| 84 |
| 85 [cell.monthInput setUseErrorStyling:self.showDateInputError]; |
| 86 [cell.yearInput setUseErrorStyling:self.showDateInputError]; |
| 87 [cell.CVCInput setUseErrorStyling:self.showDateInputError]; |
| 88 |
| 89 cell.buttonForNewCard.hidden = !self.showNewCardButton; |
| 90 |
| 91 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| 92 cell.CVCImageView.image = |
| 93 rb.GetNativeImageNamed(self.CVCImageResourceID).ToUIImage(); |
| 94 } |
| 95 |
| 96 @end |
| 97 |
| 98 @implementation CVCCell |
| 99 |
| 100 @synthesize instructionsTextLabel = _instructionsTextLabel; |
| 101 @synthesize errorLabel = _errorLabel; |
| 102 @synthesize monthInput = _monthInput; |
| 103 @synthesize yearInput = _yearInput; |
| 104 @synthesize CVCInput = _CVCInput; |
| 105 @synthesize buttonForNewCard = _buttonForNewCard; |
| 106 @synthesize dateSeparator = _dateSeparator; |
| 107 @synthesize CVCImageView = _CVCImageView; |
| 108 @synthesize dateContainerView = _dateContainerView; |
| 109 @synthesize CVCContainerView = _CVCContainerView; |
| 110 @synthesize CVCContainerLeadingConstraintWithDate = |
| 111 _CVCContainerLeadingConstraintWithDate; |
| 112 @synthesize CVCContainerLeadingConstraintWithoutDate = |
| 113 _CVCContainerLeadingConstraintWithoutDate; |
| 114 |
| 115 - (instancetype)initWithFrame:(CGRect)frame { |
| 116 self = [super initWithFrame:frame]; |
| 117 if (self) { |
| 118 UIView* contentView = self.contentView; |
| 119 |
| 120 _instructionsTextLabel = [[UILabel alloc] init]; |
| 121 _instructionsTextLabel.font = |
| 122 [[MDFRobotoFontLoader sharedInstance] mediumFontOfSize:14]; |
| 123 _instructionsTextLabel.textColor = [[MDCPalette greyPalette] tint500]; |
| 124 _instructionsTextLabel.numberOfLines = 0; |
| 125 _instructionsTextLabel.lineBreakMode = NSLineBreakByWordWrapping; |
| 126 _instructionsTextLabel.translatesAutoresizingMaskIntoConstraints = NO; |
| 127 [contentView addSubview:_instructionsTextLabel]; |
| 128 |
| 129 _errorLabel = [[UILabel alloc] init]; |
| 130 _errorLabel.font = |
| 131 [[MDFRobotoFontLoader sharedInstance] regularFontOfSize:12]; |
| 132 _errorLabel.textColor = [[MDCPalette cr_redPalette] tint500]; |
| 133 _errorLabel.numberOfLines = 0; |
| 134 _errorLabel.lineBreakMode = NSLineBreakByWordWrapping; |
| 135 _errorLabel.translatesAutoresizingMaskIntoConstraints = NO; |
| 136 [contentView addSubview:_errorLabel]; |
| 137 |
| 138 _dateContainerView = [[UIView alloc] init]; |
| 139 _dateContainerView.translatesAutoresizingMaskIntoConstraints = NO; |
| 140 [contentView addSubview:_dateContainerView]; |
| 141 |
| 142 _monthInput = |
| 143 ios::GetChromeBrowserProvider()->CreateStyledTextField(CGRectZero); |
| 144 _monthInput.placeholder = l10n_util::GetNSString( |
| 145 IDS_IOS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRY_MONTH); |
| 146 _monthInput.keyboardType = UIKeyboardTypeNumberPad; |
| 147 _monthInput.delegate = self; |
| 148 _monthInput.translatesAutoresizingMaskIntoConstraints = NO; |
| 149 [_dateContainerView addSubview:_monthInput]; |
| 150 |
| 151 _dateSeparator = [[UILabel alloc] init]; |
| 152 _dateSeparator.text = l10n_util::GetNSString( |
| 153 IDS_AUTOFILL_CARD_UNMASK_EXPIRATION_DATE_SEPARATOR); |
| 154 _dateSeparator.translatesAutoresizingMaskIntoConstraints = NO; |
| 155 [_dateContainerView addSubview:_dateSeparator]; |
| 156 |
| 157 _yearInput = |
| 158 ios::GetChromeBrowserProvider()->CreateStyledTextField(CGRectZero); |
| 159 _yearInput.placeholder = |
| 160 l10n_util::GetNSString(IDS_IOS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRY_YEAR); |
| 161 _yearInput.keyboardType = UIKeyboardTypeNumberPad; |
| 162 _yearInput.delegate = self; |
| 163 _yearInput.translatesAutoresizingMaskIntoConstraints = NO; |
| 164 [_dateContainerView addSubview:_yearInput]; |
| 165 |
| 166 _CVCContainerView = [[UIView alloc] init]; |
| 167 _CVCContainerView.translatesAutoresizingMaskIntoConstraints = NO; |
| 168 [contentView addSubview:_CVCContainerView]; |
| 169 |
| 170 _CVCInput = |
| 171 ios::GetChromeBrowserProvider()->CreateStyledTextField(CGRectZero); |
| 172 _CVCInput.placeholder = |
| 173 l10n_util::GetNSString(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC); |
| 174 _CVCInput.keyboardType = UIKeyboardTypeNumberPad; |
| 175 _CVCInput.delegate = self; |
| 176 _CVCInput.translatesAutoresizingMaskIntoConstraints = NO; |
| 177 [_CVCContainerView addSubview:_CVCInput]; |
| 178 |
| 179 _CVCImageView = [[UIImageView alloc] init]; |
| 180 _CVCImageView.translatesAutoresizingMaskIntoConstraints = NO; |
| 181 [_CVCContainerView addSubview:_CVCImageView]; |
| 182 |
| 183 _buttonForNewCard = [UIButton buttonWithType:UIButtonTypeCustom]; |
| 184 _buttonForNewCard.titleLabel.font = |
| 185 [[MDFRobotoFontLoader sharedInstance] regularFontOfSize:12]; |
| 186 [_buttonForNewCard |
| 187 setTitle:l10n_util::GetNSString(IDS_AUTOFILL_CARD_UNMASK_NEW_CARD_LINK) |
| 188 forState:UIControlStateNormal]; |
| 189 [_buttonForNewCard setTitleColor:[[MDCPalette cr_bluePalette] tint500] |
| 190 forState:UIControlStateNormal]; |
| 191 _buttonForNewCard.translatesAutoresizingMaskIntoConstraints = NO; |
| 192 [contentView addSubview:_buttonForNewCard]; |
| 193 |
| 194 [NSLayoutConstraint activateConstraints:@[ |
| 195 // Text label |
| 196 [_instructionsTextLabel.topAnchor |
| 197 constraintEqualToAnchor:contentView.topAnchor |
| 198 constant:kVerticalPadding], |
| 199 [_instructionsTextLabel.leadingAnchor |
| 200 constraintEqualToAnchor:contentView.leadingAnchor |
| 201 constant:kHorizontalPadding], |
| 202 [_instructionsTextLabel.trailingAnchor |
| 203 constraintEqualToAnchor:contentView.trailingAnchor |
| 204 constant:-kHorizontalPadding], |
| 205 |
| 206 // Date container |
| 207 [_dateContainerView.topAnchor |
| 208 constraintEqualToAnchor:_instructionsTextLabel.bottomAnchor |
| 209 constant:kUISpacing], |
| 210 [_dateContainerView.leadingAnchor |
| 211 constraintEqualToAnchor:_instructionsTextLabel.leadingAnchor], |
| 212 [_dateContainerView.heightAnchor |
| 213 constraintEqualToConstant:kTextFieldHeight], |
| 214 |
| 215 // Date content - Month input |
| 216 [_monthInput.topAnchor |
| 217 constraintEqualToAnchor:_dateContainerView.topAnchor], |
| 218 [_monthInput.leadingAnchor |
| 219 constraintEqualToAnchor:_dateContainerView.leadingAnchor], |
| 220 [_monthInput.widthAnchor constraintEqualToConstant:kDateTextFieldWidth], |
| 221 [_monthInput.bottomAnchor |
| 222 constraintEqualToAnchor:_dateContainerView.bottomAnchor], |
| 223 |
| 224 // Date content - Separator |
| 225 [_dateSeparator.leadingAnchor |
| 226 constraintEqualToAnchor:_monthInput.trailingAnchor |
| 227 constant:kUISpacing], |
| 228 [_dateSeparator.firstBaselineAnchor |
| 229 constraintEqualToAnchor:_monthInput.firstBaselineAnchor], |
| 230 |
| 231 // Date content = Year input |
| 232 [_yearInput.leadingAnchor |
| 233 constraintEqualToAnchor:_dateSeparator.trailingAnchor |
| 234 constant:kUISpacing], |
| 235 [_yearInput.widthAnchor constraintEqualToAnchor:_monthInput.widthAnchor], |
| 236 [_yearInput.heightAnchor |
| 237 constraintEqualToAnchor:_monthInput.heightAnchor], |
| 238 [_yearInput.firstBaselineAnchor |
| 239 constraintEqualToAnchor:_monthInput.firstBaselineAnchor], |
| 240 [_dateContainerView.trailingAnchor |
| 241 constraintEqualToAnchor:_yearInput.trailingAnchor], |
| 242 |
| 243 // CVC container |
| 244 [_CVCContainerView.topAnchor |
| 245 constraintEqualToAnchor:_dateContainerView.topAnchor], |
| 246 [_CVCContainerView.heightAnchor |
| 247 constraintEqualToAnchor:_dateContainerView.heightAnchor], |
| 248 // The horizontal placement of this container is dynamic. The two possible |
| 249 // placements are defined below with |
| 250 // _CVCContainerLeadingConstraintWithDate and |
| 251 // _CVCContainerLeadingConstraintWithoutDate. |
| 252 |
| 253 // CVC content - CVC input |
| 254 [_CVCInput.leadingAnchor |
| 255 constraintEqualToAnchor:_CVCContainerView.leadingAnchor], |
| 256 [_CVCInput.widthAnchor constraintEqualToConstant:kCVCTextFieldWidth], |
| 257 [_CVCInput.firstBaselineAnchor |
| 258 constraintEqualToAnchor:_monthInput.firstBaselineAnchor], |
| 259 [_CVCInput.bottomAnchor |
| 260 constraintEqualToAnchor:_CVCContainerView.bottomAnchor], |
| 261 |
| 262 // CVC content - CVC image view |
| 263 [_CVCImageView.leadingAnchor |
| 264 constraintEqualToAnchor:_CVCInput.trailingAnchor |
| 265 constant:kUISpacing], |
| 266 [_CVCImageView.centerYAnchor |
| 267 constraintEqualToAnchor:_CVCInput.centerYAnchor], |
| 268 [_CVCContainerView.trailingAnchor |
| 269 constraintEqualToAnchor:_CVCImageView.trailingAnchor], |
| 270 |
| 271 // "New Card?" label |
| 272 [_buttonForNewCard.leadingAnchor |
| 273 constraintEqualToAnchor:_CVCContainerView.trailingAnchor |
| 274 constant:kUICVCSpacing], |
| 275 [_buttonForNewCard.firstBaselineAnchor |
| 276 constraintEqualToAnchor:_CVCInput.firstBaselineAnchor], |
| 277 |
| 278 // Error label |
| 279 [_errorLabel.topAnchor |
| 280 constraintEqualToAnchor:_dateContainerView.bottomAnchor |
| 281 constant:kUISpacing], |
| 282 [_errorLabel.leadingAnchor |
| 283 constraintEqualToAnchor:_instructionsTextLabel.leadingAnchor], |
| 284 [_errorLabel.trailingAnchor |
| 285 constraintEqualToAnchor:_instructionsTextLabel.trailingAnchor], |
| 286 |
| 287 [contentView.bottomAnchor constraintEqualToAnchor:_errorLabel.bottomAnchor |
| 288 constant:kUISpacing], |
| 289 ]]; |
| 290 |
| 291 _CVCContainerLeadingConstraintWithDate = [_CVCContainerView.leadingAnchor |
| 292 constraintEqualToAnchor:_dateContainerView.trailingAnchor |
| 293 constant:kUICVCSpacing]; |
| 294 _CVCContainerLeadingConstraintWithoutDate = [_CVCContainerView.leadingAnchor |
| 295 constraintEqualToAnchor:_instructionsTextLabel.leadingAnchor]; |
| 296 } |
| 297 return self; |
| 298 } |
| 299 |
| 300 - (void)prepareForReuse { |
| 301 [super prepareForReuse]; |
| 302 self.instructionsTextLabel.text = nil; |
| 303 self.errorLabel.text = nil; |
| 304 self.monthInput.text = nil; |
| 305 [self.monthInput removeTarget:nil |
| 306 action:nil |
| 307 forControlEvents:UIControlEventAllEvents]; |
| 308 self.yearInput.text = nil; |
| 309 [self.yearInput removeTarget:nil |
| 310 action:nil |
| 311 forControlEvents:UIControlEventAllEvents]; |
| 312 self.CVCInput.text = nil; |
| 313 [self.CVCInput removeTarget:nil |
| 314 action:nil |
| 315 forControlEvents:UIControlEventAllEvents]; |
| 316 [self.buttonForNewCard removeTarget:nil |
| 317 action:nil |
| 318 forControlEvents:UIControlEventAllEvents]; |
| 319 self.dateContainerView.hidden = YES; |
| 320 self.CVCContainerLeadingConstraintWithDate.active = NO; |
| 321 self.CVCContainerLeadingConstraintWithoutDate.active = YES; |
| 322 [self.monthInput setUseErrorStyling:NO]; |
| 323 [self.yearInput setUseErrorStyling:NO]; |
| 324 [self.CVCInput setUseErrorStyling:NO]; |
| 325 self.buttonForNewCard.hidden = YES; |
| 326 self.CVCImageView.image = nil; |
| 327 } |
| 328 |
| 329 // Implements -layoutSubviews as per instructions in documentation for |
| 330 // +[MDCCollectionViewCell cr_preferredHeightForWidth:forItem:]. |
| 331 - (void)layoutSubviews { |
| 332 [super layoutSubviews]; |
| 333 |
| 334 // Adjust the text labels preferredMaxLayoutWidth when the parent's width |
| 335 // changes, for instance on screen rotation. |
| 336 CGFloat parentWidth = CGRectGetWidth(self.contentView.bounds); |
| 337 CGFloat labelsPreferredMaxLayoutWidth = parentWidth - 2 * kHorizontalPadding; |
| 338 self.instructionsTextLabel.preferredMaxLayoutWidth = |
| 339 labelsPreferredMaxLayoutWidth; |
| 340 self.errorLabel.preferredMaxLayoutWidth = labelsPreferredMaxLayoutWidth; |
| 341 |
| 342 // Re-layout with the new preferred width to allow the labels to adjust their |
| 343 // height. |
| 344 [super layoutSubviews]; |
| 345 } |
| 346 |
| 347 #pragma mark - UITextFieldDelegate |
| 348 |
| 349 // Limit month input to 2 characters. Year input is limited to 4 characters |
| 350 // (both 2-digit and 4-digit years are accepted). CVC input is limited to 4 |
| 351 // characters since CVCs are never longer than that. |
| 352 - (BOOL)textField:(UITextField*)textField |
| 353 shouldChangeCharactersInRange:(NSRange)range |
| 354 replacementString:(NSString*)string { |
| 355 if (textField == self.CVCInput || textField == self.yearInput) { |
| 356 return ([textField.text length] + [string length] - range.length) <= 4; |
| 357 } else if (textField == self.monthInput) { |
| 358 return ([textField.text length] + [string length] - range.length) <= 2; |
| 359 } |
| 360 return YES; |
| 361 } |
| 362 |
| 363 @end |
OLD | NEW |