blob: d8ce2948c40dbeddda13edf8bcb660c41710f293 [file] [log] [blame]
// Copyright 2019 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/settings/autofill/autofill_add_credit_card_view_controller.h"
#include "base/feature_list.h"
#include "base/mac/foundation_util.h"
#include "base/metrics/user_metrics.h"
#include "components/autofill/core/common/autofill_payments_features.h"
#import "ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h"
#import "ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_view_controller_delegate.h"
#import "ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.h"
#import "ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item_delegate.h"
#import "ios/chrome/browser/ui/table_view/cells/table_view_text_item.h"
#import "ios/chrome/browser/ui/table_view/chrome_table_view_controller.h"
#import "ios/chrome/browser/ui/table_view/table_view_utils.h"
#include "ios/chrome/browser/ui/ui_feature_flags.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#include "ios/chrome/grit/ios_strings.h"
#include "ui/base/l10n/l10n_util_mac.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
NSString* const kAddCreditCardViewID = @"kAddCreditCardViewID";
NSString* const kSettingsAddCreditCardButtonID =
@"kSettingsAddCreditCardButtonID";
NSString* const kSettingsAddCreditCardCancelButtonID =
@"kSettingsAddCreditCardCancelButtonID";
namespace {
typedef NS_ENUM(NSInteger, SectionIdentifier) {
SectionIdentifierName = kSectionIdentifierEnumZero,
SectionIdentifierCreditCardDetails,
SectionIdentifierCameraButton,
};
typedef NS_ENUM(NSInteger, ItemType) {
ItemTypeName = kItemTypeEnumZero,
ItemTypeCardNumber,
ItemTypeExpirationMonth,
ItemTypeExpirationYear,
ItemTypeCardNickname,
};
} // namespace
@interface AutofillAddCreditCardViewController () <
TableViewTextEditItemDelegate>
// The AddCreditCardViewControllerDelegate for this ViewController.
@property(nonatomic, weak) id<AddCreditCardViewControllerDelegate> delegate;
// The card holder name updated with the text in tableview cell.
@property(nonatomic, strong) NSString* cardHolderName;
// The card number in the UI.
@property(nonatomic, strong) NSString* cardNumber;
// The expiration month in the UI.
@property(nonatomic, strong) NSString* expirationMonth;
// The expiration year in the UI.
@property(nonatomic, strong) NSString* expirationYear;
// The user provided nickname for the credit card.
@property(nonatomic, strong) NSString* cardNickname;
@end
@implementation AutofillAddCreditCardViewController
- (instancetype)initWithDelegate:
(id<AddCreditCardViewControllerDelegate>)delegate {
self = [super initWithStyle:ChromeTableViewStyle()];
if (self) {
_delegate = delegate;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor =
[UIColor colorNamed:kGroupedPrimaryBackgroundColor];
self.tableView.accessibilityIdentifier = kAddCreditCardViewID;
self.navigationItem.title = l10n_util::GetNSString(
IDS_IOS_CREDIT_CARD_SETTINGS_ADD_PAYMENT_METHOD_TITLE);
// Adds 'Cancel' and 'Add' buttons to Navigation bar.
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]
initWithTitle:l10n_util::GetNSString(IDS_IOS_NAVIGATION_BAR_CANCEL_BUTTON)
style:UIBarButtonItemStylePlain
target:self
action:@selector(handleCancelButton:)];
self.navigationItem.leftBarButtonItem.accessibilityIdentifier =
kSettingsAddCreditCardCancelButtonID;
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
initWithTitle:l10n_util::GetNSString(IDS_IOS_NAVIGATION_BAR_ADD_BUTTON)
style:UIBarButtonItemStyleDone
target:self
action:@selector(didTapAddButton:)];
self.navigationItem.rightBarButtonItem.enabled = NO;
self.navigationItem.rightBarButtonItem.accessibilityIdentifier =
kSettingsAddCreditCardButtonID;
[self loadModel];
}
- (BOOL)tableViewHasUserInput {
[self updateCreditCardData];
BOOL hasUserInput = self.cardHolderName.length || self.cardNumber.length ||
self.expirationMonth.length || self.expirationYear.length;
if ([self isCardNicknameManagementEnabled]) {
hasUserInput = hasUserInput || self.cardNickname.length;
}
return hasUserInput;
}
#pragma mark - ChromeTableViewController
- (void)loadModel {
[super loadModel];
TableViewModel* model = self.tableViewModel;
AutofillEditItem* cardHolderNameItem = [self cardHolderNameItem];
AutofillEditItem* cardNumberItem = [self cardNumberItem];
AutofillEditItem* expirationMonthItem = [self expirationMonthItem];
AutofillEditItem* expirationYearItem = [self expirationYearItem];
if ([self isCardNicknameManagementEnabled]) {
[model addSectionWithIdentifier:SectionIdentifierCreditCardDetails];
[model addItem:cardNumberItem
toSectionWithIdentifier:SectionIdentifierCreditCardDetails];
[model addItem:expirationMonthItem
toSectionWithIdentifier:SectionIdentifierCreditCardDetails];
[model addItem:expirationYearItem
toSectionWithIdentifier:SectionIdentifierCreditCardDetails];
[model addItem:cardHolderNameItem
toSectionWithIdentifier:SectionIdentifierCreditCardDetails];
[model addItem:[self cardNicknameItem]
toSectionWithIdentifier:SectionIdentifierCreditCardDetails];
} else {
[model addSectionWithIdentifier:SectionIdentifierName];
[model addItem:cardHolderNameItem
toSectionWithIdentifier:SectionIdentifierName];
[model addSectionWithIdentifier:SectionIdentifierCreditCardDetails];
[model addItem:cardNumberItem
toSectionWithIdentifier:SectionIdentifierCreditCardDetails];
[model addItem:expirationMonthItem
toSectionWithIdentifier:SectionIdentifierCreditCardDetails];
[model addItem:expirationYearItem
toSectionWithIdentifier:SectionIdentifierCreditCardDetails];
}
}
#pragma mark - TableViewTextEditItemDelegate
- (void)tableViewItemDidBeginEditing:
(TableViewTextEditItem*)tableViewTextEditItem {
// Sets a field to have valid text when a user begins editing so that the
// error icon is not visible while a user edits a field.
tableViewTextEditItem.hasValidText = YES;
[self reconfigureCellsForItems:@[ tableViewTextEditItem ]];
}
- (void)tableViewItemDidChange:(TableViewTextEditItem*)tableViewTextEditItem {
// Checks the validity of the form and enables/disables the add button when
// the user types a character that makes the form valid/invalid.
[self updateCreditCardData];
self.navigationItem.rightBarButtonItem.enabled =
[self.delegate addCreditCardViewController:self
isValidCreditCardNumber:self.cardNumber
expirationMonth:self.expirationMonth
expirationYear:self.expirationYear
cardNickname:self.cardNickname];
[self reconfigureCellsForItems:@[ tableViewTextEditItem ]];
}
- (void)tableViewItemDidEndEditing:
(TableViewTextEditItem*)tableViewTextEditItem {
// Checks the validity of the field when a user ends editing and updates the
// cells to display the error icon if the text is invalid.
[self updateCreditCardData];
// Considers a textfield to be valid if it has no data.
if (tableViewTextEditItem.textFieldValue.length == 0) {
tableViewTextEditItem.hasValidText = YES;
[self reconfigureCellsForItems:@[ tableViewTextEditItem ]];
return;
}
switch (tableViewTextEditItem.type) {
case ItemTypeCardNumber:
tableViewTextEditItem.hasValidText =
[self.delegate addCreditCardViewController:self
isValidCreditCardNumber:self.cardNumber];
break;
case ItemTypeExpirationMonth:
tableViewTextEditItem.hasValidText =
[self.delegate addCreditCardViewController:self
isValidCreditCardExpirationMonth:self.expirationMonth];
break;
case ItemTypeExpirationYear:
tableViewTextEditItem.hasValidText =
[self.delegate addCreditCardViewController:self
isValidCreditCardExpirationYear:self.expirationYear];
break;
case ItemTypeCardNickname:
tableViewTextEditItem.hasValidText =
[self.delegate addCreditCardViewController:self
isValidCardNickname:self.cardNickname];
break;
default:
// For the 'Name on card' textfield.
tableViewTextEditItem.hasValidText = YES;
}
[self reconfigureCellsForItems:@[ tableViewTextEditItem ]];
}
#pragma mark - UITableViewDataSource
- (UITableViewCell*)tableView:(UITableView*)tableView
cellForRowAtIndexPath:(NSIndexPath*)indexPath {
UITableViewCell* cell = [super tableView:tableView
cellForRowAtIndexPath:indexPath];
// Use |ObjCCast| because |cell| might not be |TableViewTextEditCell|.
// Set the delegate and style for only |TableViewTextEditCell| type of cell
// not other types.
TableViewTextEditCell* editCell =
base::mac::ObjCCast<TableViewTextEditCell>(cell);
editCell.textField.delegate = self;
editCell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
#pragma mark - Private
// Handles Add button to add a new credit card.
- (void)didTapAddButton:(id)sender {
[self updateCreditCardData];
[self.delegate addCreditCardViewController:self
addCreditCardWithHolderName:self.cardHolderName
cardNumber:self.cardNumber
expirationMonth:self.expirationMonth
expirationYear:self.expirationYear
cardNickname:self.cardNickname];
}
// Updates credit card data properties with the text in TableView cells.
- (void)updateCreditCardData {
// TODO: Remove section handling when flag is default and cleaned up.
NSInteger cardHolderNameSection = [self isCardNicknameManagementEnabled]
? SectionIdentifierCreditCardDetails
: SectionIdentifierName;
self.cardHolderName = [self readTextFromItemtype:ItemTypeName
sectionIdentifier:cardHolderNameSection];
self.cardNumber =
[self readTextFromItemtype:ItemTypeCardNumber
sectionIdentifier:SectionIdentifierCreditCardDetails];
self.expirationMonth =
[self readTextFromItemtype:ItemTypeExpirationMonth
sectionIdentifier:SectionIdentifierCreditCardDetails];
self.expirationYear =
[self readTextFromItemtype:ItemTypeExpirationYear
sectionIdentifier:SectionIdentifierCreditCardDetails];
if ([self isCardNicknameManagementEnabled]) {
self.cardNickname =
[self readTextFromItemtype:ItemTypeCardNickname
sectionIdentifier:SectionIdentifierCreditCardDetails];
}
}
// Reads and returns the data from the item with passed |itemType| and
// |sectionIdentifier|.
- (NSString*)readTextFromItemtype:(NSInteger)itemType
sectionIdentifier:(NSInteger)sectionIdentifier {
NSIndexPath* path =
[self.tableViewModel indexPathForItemType:itemType
sectionIdentifier:sectionIdentifier];
AutofillEditItem* item = base::mac::ObjCCastStrict<AutofillEditItem>(
[self.tableViewModel itemAtIndexPath:path]);
NSString* text = item.textFieldValue;
return text;
}
// Updates TableView cell of |itemType| in |sectionIdentifier| textfieldValue
// with |text|.
- (void)updateCellForItemType:(NSInteger)itemType
inSectionIdentifier:(NSInteger)sectionIdentifier
withText:(NSString*)text {
NSIndexPath* path =
[self.tableViewModel indexPathForItemType:itemType
sectionIdentifier:sectionIdentifier];
AutofillEditItem* item = base::mac::ObjCCastStrict<AutofillEditItem>(
[self.tableViewModel itemAtIndexPath:path]);
item.textFieldValue = text;
[self reconfigureCellsForItems:@[ item ]];
}
// Dimisses this view controller when Cancel button is tapped.
- (void)handleCancelButton:(id)sender {
[self.delegate addCreditCardViewControllerDidCancel:self];
}
// Returns initialized tableViewItem with passed arguments.
- (AutofillEditItem*)createTableViewItemWithType:(NSInteger)itemType
textFieldName:(NSString*)textFieldName
textFieldValue:(NSString*)textFieldValue
textFieldPlaceholder:(NSString*)textFieldPlaceholder
keyboardType:(UIKeyboardType)keyboardType
autofillUIType:
(AutofillUIType)autofillUIType {
AutofillEditItem* item = [[AutofillEditItem alloc] initWithType:itemType];
item.delegate = self;
item.textFieldName = textFieldName;
item.textFieldValue = textFieldValue;
item.textFieldPlaceholder = textFieldPlaceholder;
item.keyboardType = keyboardType;
item.hideIcon = NO;
item.textFieldEnabled = YES;
item.autofillUIType = autofillUIType;
return item;
}
- (AutofillEditItem*)expirationYearItem {
AutofillEditItem* expirationYearItem =
[self createTableViewItemWithType:ItemTypeExpirationYear
textFieldName:l10n_util::GetNSString(
IDS_IOS_AUTOFILL_EXP_YEAR)
textFieldValue:self.expirationYear
textFieldPlaceholder:
l10n_util::GetNSString(
IDS_IOS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRATION_YEAR)
keyboardType:UIKeyboardTypeNumberPad
autofillUIType:AutofillUITypeCreditCardExpYear];
return expirationYearItem;
}
- (AutofillEditItem*)expirationMonthItem {
AutofillEditItem* expirationMonthItem =
[self createTableViewItemWithType:ItemTypeExpirationMonth
textFieldName:l10n_util::GetNSString(
IDS_IOS_AUTOFILL_EXP_MONTH)
textFieldValue:self.expirationMonth
textFieldPlaceholder:
l10n_util::GetNSString(
IDS_IOS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRY_MONTH)
keyboardType:UIKeyboardTypeNumberPad
autofillUIType:AutofillUITypeCreditCardExpMonth];
return expirationMonthItem;
}
- (AutofillEditItem*)cardNumberItem {
AutofillEditItem* cardNumberItem =
[self createTableViewItemWithType:ItemTypeCardNumber
textFieldName:l10n_util::GetNSString(
IDS_IOS_AUTOFILL_CARD_NUMBER)
textFieldValue:self.cardNumber
textFieldPlaceholder:
l10n_util::GetNSString(
IDS_IOS_AUTOFILL_DIALOG_PLACEHOLDER_CARD_NUMBER)
keyboardType:UIKeyboardTypeNumberPad
autofillUIType:AutofillUITypeCreditCardNumber];
return cardNumberItem;
}
- (AutofillEditItem*)cardHolderNameItem {
AutofillEditItem* cardHolderNameItem =
[self createTableViewItemWithType:ItemTypeName
textFieldName:l10n_util::GetNSString(
IDS_IOS_AUTOFILL_CARDHOLDER)
textFieldValue:self.cardHolderName
textFieldPlaceholder:
l10n_util::GetNSString(
IDS_IOS_AUTOFILL_DIALOG_PLACEHOLDER_CARD_HOLDER_NAME)
keyboardType:UIKeyboardTypeDefault
autofillUIType:AutofillUITypeCreditCardHolderFullName];
return cardHolderNameItem;
}
- (AutofillEditItem*)cardNicknameItem {
AutofillEditItem* cardNicknameItem =
[self createTableViewItemWithType:ItemTypeCardNickname
textFieldName:l10n_util::GetNSString(
IDS_IOS_AUTOFILL_NICKNAME)
textFieldValue:self.cardNickname
textFieldPlaceholder:
l10n_util::GetNSString(
IDS_IOS_AUTOFILL_DIALOG_PLACEHOLDER_NICKNAME)
keyboardType:UIKeyboardTypeDefault
autofillUIType:AutofillUITypeUnknown];
return cardNicknameItem;
}
// Returns whether card nickname managment feature is enabled.
- (BOOL)isCardNicknameManagementEnabled {
return base::FeatureList::IsEnabled(
autofill::features::kAutofillEnableCardNicknameManagement);
}
@end