blob: 53bb1bf04584efc715edea9ce57e9e65d5b69ed2 [file] [log] [blame]
// Copyright 2015 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_credit_card_edit_table_view_controller.h"
#include "base/format_macros.h"
#import "base/ios/block_types.h"
#import "base/mac/foundation_util.h"
#include "base/mac/scoped_block.h"
#include "base/strings/sys_string_conversions.h"
#include "components/autofill/core/browser/autofill_data_util.h"
#include "components/autofill/core/browser/credit_card.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/payments/payments_service_url.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#import "components/autofill/ios/browser/credit_card_util.h"
#include "components/strings/grit/components_strings.h"
#include "ios/chrome/browser/application_context.h"
#import "ios/chrome/browser/ui/autofill/autofill_ui_type.h"
#import "ios/chrome/browser/ui/autofill/autofill_ui_type_util.h"
#import "ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h"
#import "ios/chrome/browser/ui/commands/application_commands.h"
#import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
#import "ios/chrome/browser/ui/settings/autofill_edit_table_view_controller+protected.h"
#import "ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.h"
#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
#include "ios/chrome/grit/ios_chromium_strings.h"
#include "ios/chrome/grit/ios_strings.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
using ::AutofillTypeFromAutofillUIType;
NSString* const kAutofillCreditCardEditTableViewId =
@"kAutofillCreditCardEditTableViewId";
typedef NS_ENUM(NSInteger, SectionIdentifier) {
SectionIdentifierFields = kSectionIdentifierEnumZero,
SectionIdentifierCopiedToChrome,
};
typedef NS_ENUM(NSInteger, ItemType) {
ItemTypeCardholderName = kItemTypeEnumZero,
ItemTypeCardNumber,
ItemTypeExpirationMonth,
ItemTypeExpirationYear,
ItemTypeCopiedToChrome,
};
} // namespace
@implementation AutofillCreditCardEditTableViewController {
autofill::PersonalDataManager* _personalDataManager; // weak
autofill::CreditCard _creditCard;
}
#pragma mark - Initialization
- (instancetype)initWithCreditCard:(const autofill::CreditCard&)creditCard
personalDataManager:(autofill::PersonalDataManager*)dataManager {
self = [super initWithTableViewStyle:UITableViewStyleGrouped
appBarStyle:ChromeTableViewControllerStyleNoAppBar];
if (self) {
DCHECK(dataManager);
_personalDataManager = dataManager;
_creditCard = creditCard;
[self setTitle:l10n_util::GetNSString(IDS_IOS_AUTOFILL_EDIT_CREDIT_CARD)];
}
return self;
}
#pragma mark - UIViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.allowsSelectionDuringEditing = YES;
self.tableView.accessibilityIdentifier = kAutofillCreditCardEditTableViewId;
[self loadModel];
}
#pragma mark - SettingsRootTableViewController
- (void)editButtonPressed {
// In the case of server cards, open the Payments editing page instead.
if (_creditCard.record_type() == autofill::CreditCard::FULL_SERVER_CARD ||
_creditCard.record_type() == autofill::CreditCard::MASKED_SERVER_CARD) {
GURL paymentsURL = autofill::payments::GetManageInstrumentsUrl(0);
OpenNewTabCommand* command =
[OpenNewTabCommand commandWithURLFromChrome:paymentsURL];
[self.dispatcher closeSettingsUIAndOpenURL:command];
// Don't call [super editButtonPressed] because edit mode is not actually
// entered in this case.
return;
}
[super editButtonPressed];
if (!self.tableView.editing) {
TableViewModel* model = self.tableViewModel;
NSInteger itemCount =
[model numberOfItemsInSection:
[model sectionForSectionIdentifier:SectionIdentifierFields]];
// Reads the values from the fields and updates the local copy of the
// card accordingly.
NSInteger section =
[model sectionForSectionIdentifier:SectionIdentifierFields];
for (NSInteger itemIndex = 0; itemIndex < itemCount; ++itemIndex) {
NSIndexPath* path = [NSIndexPath indexPathForItem:itemIndex
inSection:section];
AutofillEditItem* item = base::mac::ObjCCastStrict<AutofillEditItem>(
[model itemAtIndexPath:path]);
_creditCard.SetInfo(autofill::AutofillType(AutofillTypeFromAutofillUIType(
item.autofillUIType)),
base::SysNSStringToUTF16(item.textFieldValue),
GetApplicationContext()->GetApplicationLocale());
}
_personalDataManager->UpdateCreditCard(_creditCard);
}
// Reload the model.
[self loadModel];
// Update the cells.
[self reconfigureCellsForItems:
[self.tableViewModel
itemsInSectionWithIdentifier:SectionIdentifierFields]];
}
#pragma mark - ChromeTableViewController
- (void)loadModel {
[super loadModel];
TableViewModel<TableViewItem*>* model = self.tableViewModel;
BOOL isEditing = self.tableView.editing;
[model addSectionWithIdentifier:SectionIdentifierFields];
AutofillEditItem* cardholderNameitem =
[[AutofillEditItem alloc] initWithType:ItemTypeCardholderName];
cardholderNameitem.textFieldName =
l10n_util::GetNSString(IDS_IOS_AUTOFILL_CARDHOLDER);
cardholderNameitem.textFieldValue = autofill::GetCreditCardName(
_creditCard, GetApplicationContext()->GetApplicationLocale());
cardholderNameitem.textFieldEnabled = isEditing;
cardholderNameitem.autofillUIType = AutofillUITypeCreditCardHolderFullName;
[model addItem:cardholderNameitem
toSectionWithIdentifier:SectionIdentifierFields];
// Card number (PAN).
AutofillEditItem* cardNumberItem =
[[AutofillEditItem alloc] initWithType:ItemTypeCardNumber];
cardNumberItem.textFieldName =
l10n_util::GetNSString(IDS_IOS_AUTOFILL_CARD_NUMBER);
// Never show full card number for Wallet cards, even if copied locally.
cardNumberItem.textFieldValue =
autofill::IsCreditCardLocal(_creditCard)
? base::SysUTF16ToNSString(_creditCard.number())
: base::SysUTF16ToNSString(
_creditCard.NetworkOrBankNameAndLastFourDigits());
cardNumberItem.textFieldEnabled = isEditing;
cardNumberItem.autofillUIType = AutofillUITypeCreditCardNumber;
cardNumberItem.keyboardType = UIKeyboardTypeNumberPad;
cardNumberItem.identifyingIcon =
[self cardTypeIconFromNetwork:_creditCard.network().c_str()];
[model addItem:cardNumberItem
toSectionWithIdentifier:SectionIdentifierFields];
// Expiration month.
AutofillEditItem* expirationMonthItem =
[[AutofillEditItem alloc] initWithType:ItemTypeExpirationMonth];
expirationMonthItem.textFieldName =
l10n_util::GetNSString(IDS_IOS_AUTOFILL_EXP_MONTH);
expirationMonthItem.textFieldValue =
[NSString stringWithFormat:@"%02d", _creditCard.expiration_month()];
expirationMonthItem.textFieldEnabled = isEditing;
expirationMonthItem.autofillUIType = AutofillUITypeCreditCardExpMonth;
expirationMonthItem.keyboardType = UIKeyboardTypeNumberPad;
[model addItem:expirationMonthItem
toSectionWithIdentifier:SectionIdentifierFields];
// Expiration year.
AutofillEditItem* expirationYearItem =
[[AutofillEditItem alloc] initWithType:ItemTypeExpirationYear];
expirationYearItem.textFieldName =
l10n_util::GetNSString(IDS_IOS_AUTOFILL_EXP_YEAR);
expirationYearItem.textFieldValue =
[NSString stringWithFormat:@"%04d", _creditCard.expiration_year()];
expirationYearItem.textFieldEnabled = isEditing;
expirationYearItem.autofillUIType = AutofillUITypeCreditCardExpYear;
expirationYearItem.keyboardType = UIKeyboardTypeNumberPad;
expirationYearItem.returnKeyType = UIReturnKeyDone;
[model addItem:expirationYearItem
toSectionWithIdentifier:SectionIdentifierFields];
if (_creditCard.record_type() == autofill::CreditCard::FULL_SERVER_CARD) {
// Add CopiedToChrome cell in its own section.
[model addSectionWithIdentifier:SectionIdentifierCopiedToChrome];
CopiedToChromeItem* copiedToChromeItem =
[[CopiedToChromeItem alloc] initWithType:ItemTypeCopiedToChrome];
[model addItem:copiedToChromeItem
toSectionWithIdentifier:SectionIdentifierCopiedToChrome];
}
}
#pragma mark - UITextFieldDelegate
// This method is called as the text is being typed in, pasted, or deleted. Asks
// the delegate if the text should be changed. Should always return YES. During
// typing/pasting text, |newText| contains one or more new characters. When user
// deletes text, |newText| is empty. |range| is the range of characters to be
// replaced.
- (BOOL)textField:(UITextField*)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString*)newText {
// Find the respective item for the text field.
NSIndexPath* indexPath = [self indexPathForCurrentTextField];
DCHECK(indexPath);
AutofillEditItem* item = base::mac::ObjCCastStrict<AutofillEditItem>(
[self.tableViewModel itemAtIndexPath:indexPath]);
// If the user is typing in the credit card number field, update the card type
// icon (e.g. "Visa") to reflect the number being typed.
if (item.autofillUIType == AutofillUITypeCreditCardNumber) {
// Obtain the text being typed.
NSString* updatedText =
[textField.text stringByReplacingCharactersInRange:range
withString:newText];
const char* network = autofill::CreditCard::GetCardNetwork(
base::SysNSStringToUTF16(updatedText));
item.identifyingIcon = [self cardTypeIconFromNetwork:network];
// Update the cell.
[self reconfigureCellsForItems:@[ item ]];
}
return YES;
}
#pragma mark - UITableViewDataSource
- (UITableViewCell*)tableView:(UITableView*)tableView
cellForRowAtIndexPath:(NSIndexPath*)indexPath {
UITableViewCell* cell = [super tableView:tableView
cellForRowAtIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
NSInteger itemType = [self.tableViewModel itemTypeForIndexPath:indexPath];
AutofillEditCell* editCell = base::mac::ObjCCast<AutofillEditCell>(cell);
editCell.textField.delegate = self;
switch (itemType) {
case ItemTypeCardholderName:
case ItemTypeCardNumber:
case ItemTypeExpirationMonth:
case ItemTypeExpirationYear:
break;
case ItemTypeCopiedToChrome: {
CopiedToChromeCell* copiedToChromeCell =
base::mac::ObjCCastStrict<CopiedToChromeCell>(cell);
[copiedToChromeCell.button addTarget:self
action:@selector(buttonTapped:)
forControlEvents:UIControlEventTouchUpInside];
break;
}
default:
break;
}
return cell;
}
- (BOOL)tableView:(UITableView*)tableView
canEditRowAtIndexPath:(NSIndexPath*)indexPath {
// Items in this table view are not deletable, so should not be seen as
// editable by the table view.
return NO;
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView*)tableView
didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
if (self.tableView.editing) {
UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:indexPath];
AutofillEditCell* textFieldCell =
base::mac::ObjCCastStrict<AutofillEditCell>(cell);
[textFieldCell.textField becomeFirstResponder];
}
}
#pragma mark - Actions
- (void)buttonTapped:(UIButton*)button {
_personalDataManager->ResetFullServerCard(_creditCard.guid());
// Reset the copy of the card data used for display immediately.
_creditCard.set_record_type(autofill::CreditCard::MASKED_SERVER_CARD);
_creditCard.SetNumber(_creditCard.LastFourDigits());
[self reloadData];
}
#pragma mark - Helper Methods
- (UIImage*)cardTypeIconFromNetwork:(const char*)network {
if (network != autofill::kGenericCard) {
int resourceID =
autofill::data_util::GetPaymentRequestData(network).icon_resource_id;
// Return the card issuer network icon.
return NativeImage(resourceID);
} else {
return nil;
}
}
@end