| Index: ios/chrome/browser/ui/autofill/cells/cvc_item.mm
|
| diff --git a/ios/chrome/browser/ui/autofill/cells/cvc_item.mm b/ios/chrome/browser/ui/autofill/cells/cvc_item.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..7a69184b17587ae4e6e9bdf1ed73ac9159c3972f
|
| --- /dev/null
|
| +++ b/ios/chrome/browser/ui/autofill/cells/cvc_item.mm
|
| @@ -0,0 +1,363 @@
|
| +// 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.
|
| +
|
| +#import "ios/chrome/browser/ui/autofill/cells/cvc_item.h"
|
| +
|
| +#include "components/strings/grit/components_strings.h"
|
| +#import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
|
| +#include "ios/chrome/grit/ios_strings.h"
|
| +#import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
|
| +#import "ios/public/provider/chrome/browser/ui/text_field_styling.h"
|
| +#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
|
| +#import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoFontLoader.h"
|
| +#include "ui/base/l10n/l10n_util.h"
|
| +#include "ui/base/resource/resource_bundle.h"
|
| +
|
| +#if !defined(__has_feature) || !__has_feature(objc_arc)
|
| +#error "This file requires ARC support."
|
| +#endif
|
| +
|
| +namespace {
|
| +// Padding used on the leading and trailing edges of the cell.
|
| +const CGFloat kHorizontalPadding = 16;
|
| +// Padding used on the top and bottom edges of the cell.
|
| +const CGFloat kVerticalPadding = 16;
|
| +// Spacing between elements.
|
| +const CGFloat kUISpacing = 5;
|
| +// Spacing around the CVC container.
|
| +const CGFloat kUICVCSpacing = 20;
|
| +// Height of the different text fields.
|
| +const CGFloat kTextFieldHeight = 50;
|
| +// Width of the date text fields.
|
| +const CGFloat kDateTextFieldWidth = 40;
|
| +// Width of the CVC text field.
|
| +const CGFloat kCVCTextFieldWidth = 60;
|
| +}
|
| +
|
| +@interface CVCCell ()<UITextFieldDelegate>
|
| +@property(nonatomic, strong) UILabel* dateSeparator;
|
| +@property(nonatomic, strong) UIView* dateContainerView;
|
| +@property(nonatomic, strong) UIView* CVCContainerView;
|
| +@property(nonatomic, strong)
|
| + NSLayoutConstraint* CVCContainerLeadingConstraintWithDate;
|
| +@property(nonatomic, strong)
|
| + NSLayoutConstraint* CVCContainerLeadingConstraintWithoutDate;
|
| +@end
|
| +
|
| +@implementation CVCItem
|
| +
|
| +@synthesize instructionsText = _instructionsText;
|
| +@synthesize errorMessage = _errorMessage;
|
| +@synthesize monthText = _monthText;
|
| +@synthesize yearText = _yearText;
|
| +@synthesize CVCText = _CVCText;
|
| +@synthesize showDateInput = _showDateInput;
|
| +@synthesize showNewCardButton = _showNewCardButton;
|
| +@synthesize showDateInputError = _showDateInputError;
|
| +@synthesize showCVCInputError = _showCVCInputError;
|
| +@synthesize CVCImageResourceID = _CVCImageResourceID;
|
| +
|
| +- (instancetype)initWithType:(NSInteger)type {
|
| + self = [super initWithType:type];
|
| + if (self) {
|
| + self.cellClass = [CVCCell class];
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +#pragma mark CollectionViewItem
|
| +
|
| +- (void)configureCell:(CVCCell*)cell {
|
| + [super configureCell:cell];
|
| + cell.instructionsTextLabel.text = self.instructionsText;
|
| + cell.errorLabel.text = self.errorMessage;
|
| +
|
| + cell.monthInput.text = self.monthText;
|
| + cell.yearInput.text = self.yearText;
|
| + cell.CVCInput.text = self.CVCText;
|
| +
|
| + cell.dateContainerView.hidden = !self.showDateInput;
|
| +
|
| + cell.CVCContainerLeadingConstraintWithDate.active = self.showDateInput;
|
| + cell.CVCContainerLeadingConstraintWithoutDate.active = !self.showDateInput;
|
| +
|
| + [cell.monthInput setUseErrorStyling:self.showDateInputError];
|
| + [cell.yearInput setUseErrorStyling:self.showDateInputError];
|
| + [cell.CVCInput setUseErrorStyling:self.showDateInputError];
|
| +
|
| + cell.buttonForNewCard.hidden = !self.showNewCardButton;
|
| +
|
| + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
|
| + cell.CVCImageView.image =
|
| + rb.GetNativeImageNamed(self.CVCImageResourceID).ToUIImage();
|
| +}
|
| +
|
| +@end
|
| +
|
| +@implementation CVCCell
|
| +
|
| +@synthesize instructionsTextLabel = _instructionsTextLabel;
|
| +@synthesize errorLabel = _errorLabel;
|
| +@synthesize monthInput = _monthInput;
|
| +@synthesize yearInput = _yearInput;
|
| +@synthesize CVCInput = _CVCInput;
|
| +@synthesize buttonForNewCard = _buttonForNewCard;
|
| +@synthesize dateSeparator = _dateSeparator;
|
| +@synthesize CVCImageView = _CVCImageView;
|
| +@synthesize dateContainerView = _dateContainerView;
|
| +@synthesize CVCContainerView = _CVCContainerView;
|
| +@synthesize CVCContainerLeadingConstraintWithDate =
|
| + _CVCContainerLeadingConstraintWithDate;
|
| +@synthesize CVCContainerLeadingConstraintWithoutDate =
|
| + _CVCContainerLeadingConstraintWithoutDate;
|
| +
|
| +- (instancetype)initWithFrame:(CGRect)frame {
|
| + self = [super initWithFrame:frame];
|
| + if (self) {
|
| + UIView* contentView = self.contentView;
|
| +
|
| + _instructionsTextLabel = [[UILabel alloc] init];
|
| + _instructionsTextLabel.font =
|
| + [[MDFRobotoFontLoader sharedInstance] mediumFontOfSize:14];
|
| + _instructionsTextLabel.textColor = [[MDCPalette greyPalette] tint500];
|
| + _instructionsTextLabel.numberOfLines = 0;
|
| + _instructionsTextLabel.lineBreakMode = NSLineBreakByWordWrapping;
|
| + _instructionsTextLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
| + [contentView addSubview:_instructionsTextLabel];
|
| +
|
| + _errorLabel = [[UILabel alloc] init];
|
| + _errorLabel.font =
|
| + [[MDFRobotoFontLoader sharedInstance] regularFontOfSize:12];
|
| + _errorLabel.textColor = [[MDCPalette cr_redPalette] tint500];
|
| + _errorLabel.numberOfLines = 0;
|
| + _errorLabel.lineBreakMode = NSLineBreakByWordWrapping;
|
| + _errorLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
| + [contentView addSubview:_errorLabel];
|
| +
|
| + _dateContainerView = [[UIView alloc] init];
|
| + _dateContainerView.translatesAutoresizingMaskIntoConstraints = NO;
|
| + [contentView addSubview:_dateContainerView];
|
| +
|
| + _monthInput =
|
| + ios::GetChromeBrowserProvider()->CreateStyledTextField(CGRectZero);
|
| + _monthInput.placeholder = l10n_util::GetNSString(
|
| + IDS_IOS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRY_MONTH);
|
| + _monthInput.keyboardType = UIKeyboardTypeNumberPad;
|
| + _monthInput.delegate = self;
|
| + _monthInput.translatesAutoresizingMaskIntoConstraints = NO;
|
| + [_dateContainerView addSubview:_monthInput];
|
| +
|
| + _dateSeparator = [[UILabel alloc] init];
|
| + _dateSeparator.text = l10n_util::GetNSString(
|
| + IDS_AUTOFILL_CARD_UNMASK_EXPIRATION_DATE_SEPARATOR);
|
| + _dateSeparator.translatesAutoresizingMaskIntoConstraints = NO;
|
| + [_dateContainerView addSubview:_dateSeparator];
|
| +
|
| + _yearInput =
|
| + ios::GetChromeBrowserProvider()->CreateStyledTextField(CGRectZero);
|
| + _yearInput.placeholder =
|
| + l10n_util::GetNSString(IDS_IOS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRY_YEAR);
|
| + _yearInput.keyboardType = UIKeyboardTypeNumberPad;
|
| + _yearInput.delegate = self;
|
| + _yearInput.translatesAutoresizingMaskIntoConstraints = NO;
|
| + [_dateContainerView addSubview:_yearInput];
|
| +
|
| + _CVCContainerView = [[UIView alloc] init];
|
| + _CVCContainerView.translatesAutoresizingMaskIntoConstraints = NO;
|
| + [contentView addSubview:_CVCContainerView];
|
| +
|
| + _CVCInput =
|
| + ios::GetChromeBrowserProvider()->CreateStyledTextField(CGRectZero);
|
| + _CVCInput.placeholder =
|
| + l10n_util::GetNSString(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC);
|
| + _CVCInput.keyboardType = UIKeyboardTypeNumberPad;
|
| + _CVCInput.delegate = self;
|
| + _CVCInput.translatesAutoresizingMaskIntoConstraints = NO;
|
| + [_CVCContainerView addSubview:_CVCInput];
|
| +
|
| + _CVCImageView = [[UIImageView alloc] init];
|
| + _CVCImageView.translatesAutoresizingMaskIntoConstraints = NO;
|
| + [_CVCContainerView addSubview:_CVCImageView];
|
| +
|
| + _buttonForNewCard = [UIButton buttonWithType:UIButtonTypeCustom];
|
| + _buttonForNewCard.titleLabel.font =
|
| + [[MDFRobotoFontLoader sharedInstance] regularFontOfSize:12];
|
| + [_buttonForNewCard
|
| + setTitle:l10n_util::GetNSString(IDS_AUTOFILL_CARD_UNMASK_NEW_CARD_LINK)
|
| + forState:UIControlStateNormal];
|
| + [_buttonForNewCard setTitleColor:[[MDCPalette cr_bluePalette] tint500]
|
| + forState:UIControlStateNormal];
|
| + _buttonForNewCard.translatesAutoresizingMaskIntoConstraints = NO;
|
| + [contentView addSubview:_buttonForNewCard];
|
| +
|
| + [NSLayoutConstraint activateConstraints:@[
|
| + // Text label
|
| + [_instructionsTextLabel.topAnchor
|
| + constraintEqualToAnchor:contentView.topAnchor
|
| + constant:kVerticalPadding],
|
| + [_instructionsTextLabel.leadingAnchor
|
| + constraintEqualToAnchor:contentView.leadingAnchor
|
| + constant:kHorizontalPadding],
|
| + [_instructionsTextLabel.trailingAnchor
|
| + constraintEqualToAnchor:contentView.trailingAnchor
|
| + constant:-kHorizontalPadding],
|
| +
|
| + // Date container
|
| + [_dateContainerView.topAnchor
|
| + constraintEqualToAnchor:_instructionsTextLabel.bottomAnchor
|
| + constant:kUISpacing],
|
| + [_dateContainerView.leadingAnchor
|
| + constraintEqualToAnchor:_instructionsTextLabel.leadingAnchor],
|
| + [_dateContainerView.heightAnchor
|
| + constraintEqualToConstant:kTextFieldHeight],
|
| +
|
| + // Date content - Month input
|
| + [_monthInput.topAnchor
|
| + constraintEqualToAnchor:_dateContainerView.topAnchor],
|
| + [_monthInput.leadingAnchor
|
| + constraintEqualToAnchor:_dateContainerView.leadingAnchor],
|
| + [_monthInput.widthAnchor constraintEqualToConstant:kDateTextFieldWidth],
|
| + [_monthInput.bottomAnchor
|
| + constraintEqualToAnchor:_dateContainerView.bottomAnchor],
|
| +
|
| + // Date content - Separator
|
| + [_dateSeparator.leadingAnchor
|
| + constraintEqualToAnchor:_monthInput.trailingAnchor
|
| + constant:kUISpacing],
|
| + [_dateSeparator.firstBaselineAnchor
|
| + constraintEqualToAnchor:_monthInput.firstBaselineAnchor],
|
| +
|
| + // Date content = Year input
|
| + [_yearInput.leadingAnchor
|
| + constraintEqualToAnchor:_dateSeparator.trailingAnchor
|
| + constant:kUISpacing],
|
| + [_yearInput.widthAnchor constraintEqualToAnchor:_monthInput.widthAnchor],
|
| + [_yearInput.heightAnchor
|
| + constraintEqualToAnchor:_monthInput.heightAnchor],
|
| + [_yearInput.firstBaselineAnchor
|
| + constraintEqualToAnchor:_monthInput.firstBaselineAnchor],
|
| + [_dateContainerView.trailingAnchor
|
| + constraintEqualToAnchor:_yearInput.trailingAnchor],
|
| +
|
| + // CVC container
|
| + [_CVCContainerView.topAnchor
|
| + constraintEqualToAnchor:_dateContainerView.topAnchor],
|
| + [_CVCContainerView.heightAnchor
|
| + constraintEqualToAnchor:_dateContainerView.heightAnchor],
|
| + // The horizontal placement of this container is dynamic. The two possible
|
| + // placements are defined below with
|
| + // _CVCContainerLeadingConstraintWithDate and
|
| + // _CVCContainerLeadingConstraintWithoutDate.
|
| +
|
| + // CVC content - CVC input
|
| + [_CVCInput.leadingAnchor
|
| + constraintEqualToAnchor:_CVCContainerView.leadingAnchor],
|
| + [_CVCInput.widthAnchor constraintEqualToConstant:kCVCTextFieldWidth],
|
| + [_CVCInput.firstBaselineAnchor
|
| + constraintEqualToAnchor:_monthInput.firstBaselineAnchor],
|
| + [_CVCInput.bottomAnchor
|
| + constraintEqualToAnchor:_CVCContainerView.bottomAnchor],
|
| +
|
| + // CVC content - CVC image view
|
| + [_CVCImageView.leadingAnchor
|
| + constraintEqualToAnchor:_CVCInput.trailingAnchor
|
| + constant:kUISpacing],
|
| + [_CVCImageView.centerYAnchor
|
| + constraintEqualToAnchor:_CVCInput.centerYAnchor],
|
| + [_CVCContainerView.trailingAnchor
|
| + constraintEqualToAnchor:_CVCImageView.trailingAnchor],
|
| +
|
| + // "New Card?" label
|
| + [_buttonForNewCard.leadingAnchor
|
| + constraintEqualToAnchor:_CVCContainerView.trailingAnchor
|
| + constant:kUICVCSpacing],
|
| + [_buttonForNewCard.firstBaselineAnchor
|
| + constraintEqualToAnchor:_CVCInput.firstBaselineAnchor],
|
| +
|
| + // Error label
|
| + [_errorLabel.topAnchor
|
| + constraintEqualToAnchor:_dateContainerView.bottomAnchor
|
| + constant:kUISpacing],
|
| + [_errorLabel.leadingAnchor
|
| + constraintEqualToAnchor:_instructionsTextLabel.leadingAnchor],
|
| + [_errorLabel.trailingAnchor
|
| + constraintEqualToAnchor:_instructionsTextLabel.trailingAnchor],
|
| +
|
| + [contentView.bottomAnchor constraintEqualToAnchor:_errorLabel.bottomAnchor
|
| + constant:kUISpacing],
|
| + ]];
|
| +
|
| + _CVCContainerLeadingConstraintWithDate = [_CVCContainerView.leadingAnchor
|
| + constraintEqualToAnchor:_dateContainerView.trailingAnchor
|
| + constant:kUICVCSpacing];
|
| + _CVCContainerLeadingConstraintWithoutDate = [_CVCContainerView.leadingAnchor
|
| + constraintEqualToAnchor:_instructionsTextLabel.leadingAnchor];
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +- (void)prepareForReuse {
|
| + [super prepareForReuse];
|
| + self.instructionsTextLabel.text = nil;
|
| + self.errorLabel.text = nil;
|
| + self.monthInput.text = nil;
|
| + [self.monthInput removeTarget:nil
|
| + action:nil
|
| + forControlEvents:UIControlEventAllEvents];
|
| + self.yearInput.text = nil;
|
| + [self.yearInput removeTarget:nil
|
| + action:nil
|
| + forControlEvents:UIControlEventAllEvents];
|
| + self.CVCInput.text = nil;
|
| + [self.CVCInput removeTarget:nil
|
| + action:nil
|
| + forControlEvents:UIControlEventAllEvents];
|
| + [self.buttonForNewCard removeTarget:nil
|
| + action:nil
|
| + forControlEvents:UIControlEventAllEvents];
|
| + self.dateContainerView.hidden = YES;
|
| + self.CVCContainerLeadingConstraintWithDate.active = NO;
|
| + self.CVCContainerLeadingConstraintWithoutDate.active = YES;
|
| + [self.monthInput setUseErrorStyling:NO];
|
| + [self.yearInput setUseErrorStyling:NO];
|
| + [self.CVCInput setUseErrorStyling:NO];
|
| + self.buttonForNewCard.hidden = YES;
|
| + self.CVCImageView.image = nil;
|
| +}
|
| +
|
| +// Implements -layoutSubviews as per instructions in documentation for
|
| +// +[MDCCollectionViewCell cr_preferredHeightForWidth:forItem:].
|
| +- (void)layoutSubviews {
|
| + [super layoutSubviews];
|
| +
|
| + // Adjust the text labels preferredMaxLayoutWidth when the parent's width
|
| + // changes, for instance on screen rotation.
|
| + CGFloat parentWidth = CGRectGetWidth(self.contentView.bounds);
|
| + CGFloat labelsPreferredMaxLayoutWidth = parentWidth - 2 * kHorizontalPadding;
|
| + self.instructionsTextLabel.preferredMaxLayoutWidth =
|
| + labelsPreferredMaxLayoutWidth;
|
| + self.errorLabel.preferredMaxLayoutWidth = labelsPreferredMaxLayoutWidth;
|
| +
|
| + // Re-layout with the new preferred width to allow the labels to adjust their
|
| + // height.
|
| + [super layoutSubviews];
|
| +}
|
| +
|
| +#pragma mark - UITextFieldDelegate
|
| +
|
| +// Limit month input to 2 characters. Year input is limited to 4 characters
|
| +// (both 2-digit and 4-digit years are accepted). CVC input is limited to 4
|
| +// characters since CVCs are never longer than that.
|
| +- (BOOL)textField:(UITextField*)textField
|
| + shouldChangeCharactersInRange:(NSRange)range
|
| + replacementString:(NSString*)string {
|
| + if (textField == self.CVCInput || textField == self.yearInput) {
|
| + return ([textField.text length] + [string length] - range.length) <= 4;
|
| + } else if (textField == self.monthInput) {
|
| + return ([textField.text length] + [string length] - range.length) <= 2;
|
| + }
|
| + return YES;
|
| +}
|
| +
|
| +@end
|
|
|