Commit 73c81f24 authored by David Jean's avatar David Jean Committed by Commit Bot

[ios] Plug in obj-c credit card object in manual fill.

Also added use of cell utils in manual_fill_card_cell.

Bug: 845472
Change-Id: Ie015a2335c3cfe37f34620e2efb1447b8429d3c1
Reviewed-on: https://chromium-review.googlesource.com/c/1335550
Commit-Queue: David Jean <djean@chromium.org>
Reviewed-by: default avatarJavier Ernesto Flores Robles <javierrobles@chromium.org>
Cr-Commit-Position: refs/heads/master@{#608355}
parent eaf11202
......@@ -5,11 +5,9 @@
#import "ios/chrome/browser/ui/autofill/manual_fill/card_coordinator.h"
#include "base/memory/ref_counted.h"
#include "base/strings/sys_string_conversions.h"
#include "components/autofill/core/browser/credit_card.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/ios/browser/autofill_driver_ios.h"
#include "components/keyed_service/core/service_access_type.h"
#include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/card_list_delegate.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/card_mediator.h"
......@@ -66,6 +64,10 @@ initWithBaseViewController:(UIViewController*)viewController
std::vector<autofill::CreditCard*> cards =
personalDataManager->GetCreditCardsToSuggest(true);
// TODO(crbug.com/845472): add observer using
// PersonalDataManagerObserverBridge and refresh data when personal data
// changes. Applies to addresses too.
_cardMediator = [[ManualFillCardMediator alloc] initWithCards:cards];
_cardMediator.navigationDelegate = self;
_cardMediator.contentDelegate = self.manualFillInjectionHandler;
......@@ -94,13 +96,17 @@ initWithBaseViewController:(UIViewController*)viewController
}];
}
- (void)requestFullCreditCard:(const autofill::CreditCard&)card {
- (void)requestFullCreditCard:(ManualFillCreditCard*)card {
__weak __typeof(self) weakSelf = self;
// TODO(crbug.com/845472): resolve potential weak card here either using
// std::unique or an Obj-C Creditcard object as proposed in another TODO.
autofill::CreditCard cardCopy = card;
__weak ManualFillCreditCard* weakCard = card;
[self dismissIfNecessaryThenDoCompletion:^{
[weakSelf.cardRequester requestFullCreditCard:cardCopy
if (!weakSelf)
return;
const autofill::CreditCard* autofillCreditCard =
[weakSelf.cardMediator findCreditCardfromGUID:weakCard.GUID];
if (!autofillCreditCard)
return;
[weakSelf.cardRequester requestFullCreditCard:*autofillCreditCard
withBaseViewController:weakSelf.baseViewController];
}];
}
......
......@@ -5,9 +5,7 @@
#ifndef IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_CARD_LIST_DELEGATE_H_
#define IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_CARD_LIST_DELEGATE_H_
namespace autofill {
class CreditCard;
} // namespace autofill
#import "ios/chrome/browser/ui/autofill/manual_fill/credit_card.h"
// Delegate for actions in manual fallback's cards list.
@protocol CardListDelegate
......@@ -16,7 +14,7 @@ class CreditCard;
- (void)openCardSettings;
// Open credit card unlock, through CVC, prompt.
- (void)requestFullCreditCard:(const autofill::CreditCard&)card;
- (void)requestFullCreditCard:(ManualFillCreditCard*)card;
@end
......
......@@ -45,6 +45,9 @@ extern NSString* const OtherCardsAccessibilityIdentifier;
// Unavailable. Use |initWithCards:|.
- (instancetype)init NS_UNAVAILABLE;
// Finds the original autofill::CreditCard from given |GUID|.
- (const autofill::CreditCard*)findCreditCardfromGUID:(NSString*)GUID;
@end
#endif // IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_CARD_MEDIATOR_H_
......@@ -8,11 +8,14 @@
#include "base/metrics/user_metrics.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_manager.h"
#include "components/autofill/core/browser/credit_card.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/action_cell.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/card_consumer.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/card_list_delegate.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/credit_card.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/credit_card_form.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/full_card_request_result_delegate_bridge.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_card_cell.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_content_delegate.h"
......@@ -65,6 +68,16 @@ NSString* const ManageCardsAccessibilityIdentifier =
[self postActionsToConsumer];
}
- (const autofill::CreditCard*)findCreditCardfromGUID:(NSString*)GUID {
for (autofill::CreditCard* card : self.cards) {
NSString* cppGUID =
base::SysUTF16ToNSString(base::ASCIIToUTF16(card->guid()));
if ([cppGUID isEqualToString:GUID])
return card;
}
return nil;
}
#pragma mark - Private
// Posts the cards to the consumer.
......@@ -76,9 +89,10 @@ NSString* const ManageCardsAccessibilityIdentifier =
NSMutableArray* items =
[[NSMutableArray alloc] initWithCapacity:self.cards.size()];
for (autofill::CreditCard* card : self.cards) {
// TODO(crbug.com/845472): create a pure objc object instead.
ManualFillCreditCard* manualFillCreditCard =
[[ManualFillCreditCard alloc] initWithCreditCard:*card];
auto item =
[[ManualFillCardItem alloc] initWithCreditCard:*card
[[ManualFillCardItem alloc] initWithCreditCard:manualFillCreditCard
contentDelegate:self.contentDelegate
navigationDelegate:self.navigationDelegate];
[items addObject:item];
......@@ -110,16 +124,18 @@ NSString* const ManageCardsAccessibilityIdentifier =
- (void)onFullCardRequestSucceeded:(const autofill::CreditCard&)card {
// Credit card are not shown as 'Secure'.
NSString* cardNumber =
base::SysUTF16ToNSString(autofill::CreditCard::StripSeparators(
card.GetRawInfo(autofill::CREDIT_CARD_NUMBER)));
ManualFillCreditCard* manualFillCreditCard =
[[ManualFillCreditCard alloc] initWithCreditCard:card];
// Don't replace the locked card with the unlocked one, so the user will
// have to unlock it again, if needed.
// TODO(crbug.com/845472): update userDidPickContent to have an isHttps
// parameter.
[self.contentDelegate userDidPickContent:cardNumber isSecure:NO];
[self.contentDelegate userDidPickContent:manualFillCreditCard.number
isSecure:NO];
}
- (void)onFullCardRequestFailed {
// TODO(crbug.com/845472): handle failure to unlock card.
// This is called on user cancelling, so there's nothing to do here.
}
@end
......@@ -25,9 +25,11 @@
base::SysUTF16ToNSString(base::ASCIIToUTF16(creditCard.bank_name()));
NSString* cardHolder = autofill::GetCreditCardName(
creditCard, GetApplicationContext()->GetApplicationLocale());
NSString* number =
base::SysUTF16ToNSString(autofill::CreditCard::StripSeparators(
creditCard.GetRawInfo(autofill::CREDIT_CARD_NUMBER)));
NSString* number = nil;
if (creditCard.record_type() != autofill::CreditCard::MASKED_SERVER_CARD) {
number = base::SysUTF16ToNSString(autofill::CreditCard::StripSeparators(
creditCard.GetRawInfo(autofill::CREDIT_CARD_NUMBER)));
}
// Unicode characters used in card number:
// - 0x0020 - Space.
......
......@@ -87,7 +87,7 @@ TEST_F(ManualFillCreditCardFormAutofilliOSTest, CreationObfuscated) {
EXPECT_TRUE([GUID isEqualToString:manualFillCard.GUID]);
EXPECT_TRUE([@"Visa" isEqualToString:manualFillCard.network]);
EXPECT_TRUE([bankName isEqualToString:manualFillCard.bankName]);
EXPECT_TRUE([number isEqualToString:manualFillCard.number]);
EXPECT_FALSE(manualFillCard.number);
EXPECT_TRUE([manualFillCard.obfuscatedNumber containsString:@"1234"]);
EXPECT_FALSE([manualFillCard.obfuscatedNumber containsString:@"4321"]);
EXPECT_TRUE([cardHolder isEqualToString:manualFillCard.cardHolder]);
......
......@@ -7,19 +7,16 @@
#import <UIKit/UIKit.h>
#import "ios/chrome/browser/ui/autofill/manual_fill/credit_card.h"
#import "ios/chrome/browser/ui/table_view/cells/table_view_item.h"
namespace autofill {
class CreditCard;
} // namespace autofill
@protocol CardListDelegate;
@protocol ManualFillContentDelegate;
// Wrapper to show card cells in a ChromeTableViewController.
@interface ManualFillCardItem : TableViewItem
- (instancetype)initWithCreditCard:(const autofill::CreditCard&)card
- (instancetype)initWithCreditCard:(ManualFillCreditCard*)card
contentDelegate:
(id<ManualFillContentDelegate>)contentDelegate
navigationDelegate:(id<CardListDelegate>)navigationDelegate
......@@ -34,7 +31,7 @@ class CreditCard;
@interface ManualFillCardCell : UITableViewCell
// Updates the cell with credit card and the |delegate| to be notified.
- (void)setUpWithCreditCard:(const autofill::CreditCard&)card
- (void)setUpWithCreditCard:(ManualFillCreditCard*)card
contentDelegate:(id<ManualFillContentDelegate>)contentDelegate
navigationDelegate:(id<CardListDelegate>)navigationDelegate;
......
......@@ -6,10 +6,9 @@
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/credit_card.h"
#import "components/autofill/ios/browser/credit_card_util.h"
#include "ios/chrome/browser/application_context.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/card_list_delegate.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/credit_card.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_cell_utils.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_content_delegate.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/uicolor_manualfill.h"
#import "ios/chrome/browser/ui/list_model/list_model.h"
......@@ -31,7 +30,7 @@
@property(nonatomic, weak, readonly) id<CardListDelegate> navigationDelegate;
// The credit card for this item.
@property(nonatomic, assign) autofill::CreditCard card;
@property(nonatomic, readonly) ManualFillCreditCard* card;
@end
......@@ -40,7 +39,7 @@
@synthesize navigationDelegate = _navigationDelegate;
@synthesize card = _card;
- (instancetype)initWithCreditCard:(const autofill::CreditCard&)card
- (instancetype)initWithCreditCard:(ManualFillCreditCard*)card
contentDelegate:
(id<ManualFillContentDelegate>)contentDelegate
navigationDelegate:(id<CardListDelegate>)navigationDelegate {
......@@ -69,15 +68,6 @@ namespace {
// Left and right margins of the cell content.
static const CGFloat sideMargins = 16;
// The multiplier for the base system spacing at the top margin.
static const CGFloat TopSystemSpacingMultiplier = 1.58;
// The multiplier for the base system spacing between elements (vertical).
static const CGFloat MiddleSystemSpacingMultiplier = 1.83;
// The multiplier for the base system spacing at the bottom margin.
static const CGFloat BottomSystemSpacingMultiplier = 2.26;
// Margin left and right of expiration buttons.
static const CGFloat ExpirationMarginWidth = 16.0;
......@@ -107,21 +97,12 @@ static const CGFloat ExpirationMarginWidth = 16.0;
@property(nonatomic, weak) id<CardListDelegate> navigationDelegate;
// The credit card data for this cell.
@property(nonatomic, assign) autofill::CreditCard card;
@property(nonatomic, assign) ManualFillCreditCard* card;
@end
@implementation ManualFillCardCell
@synthesize cardLabel = _cardLabel;
@synthesize cardNumberButton = _cardNumberButton;
@synthesize cardholderButton = _cardholderButton;
@synthesize expirationMonthButton = _expirationMonthButton;
@synthesize expirationYearButton = _expirationYearButton;
@synthesize contentDelegate = _contentDelegate;
@synthesize navigationDelegate = _navigationDelegate;
@synthesize card = _card;
#pragma mark - Public
- (void)prepareForReuse {
......@@ -133,9 +114,10 @@ static const CGFloat ExpirationMarginWidth = 16.0;
[self.expirationYearButton setTitle:@"" forState:UIControlStateNormal];
self.contentDelegate = nil;
self.navigationDelegate = nil;
self.card = nil;
}
- (void)setUpWithCreditCard:(const autofill::CreditCard&)card
- (void)setUpWithCreditCard:(ManualFillCreditCard*)card
contentDelegate:(id<ManualFillContentDelegate>)contentDelegate
navigationDelegate:(id<CardListDelegate>)navigationDelegate {
if (self.contentView.subviews.count == 0) {
......@@ -146,12 +128,11 @@ static const CGFloat ExpirationMarginWidth = 16.0;
self.card = card;
NSString* cardName = @"";
if (card.bank_name().empty()) {
cardName = base::SysUTF16ToNSString(card.NetworkForDisplay());
if (card.bankName.length) {
cardName = card.network;
} else {
cardName = base::SysUTF16ToNSString(card.NetworkForDisplay() +
base::ASCIIToUTF16(" ") +
base::ASCIIToUTF16(card.bank_name()));
cardName =
[NSString stringWithFormat:@"%@ %@", card.network, card.bankName];
}
NSMutableAttributedString* attributedString =
......@@ -164,28 +145,15 @@ static const CGFloat ExpirationMarginWidth = 16.0;
}];
self.cardLabel.attributedText = attributedString;
// Unicode characters used in card number:
// - 0x0020 - Space.
// - 0x2060 - WORD-JOINER (makes string undivisible).
constexpr base::char16 separator[] = {0x2060, 0x0020, 0};
const base::string16 digits = card.LastFourDigits();
NSString* obfuscatedCardNumber = base::SysUTF16ToNSString(
autofill::kMidlineEllipsis + base::string16(separator) +
autofill::kMidlineEllipsis + base::string16(separator) +
autofill::kMidlineEllipsis + base::string16(separator) + digits);
[self.cardNumberButton setTitle:obfuscatedCardNumber
[self.cardNumberButton setTitle:card.obfuscatedNumber
forState:UIControlStateNormal];
[self.cardholderButton setTitle:card.cardHolder
forState:UIControlStateNormal];
NSString* cardholder = autofill::GetCreditCardName(
card, GetApplicationContext()->GetApplicationLocale());
[self.cardholderButton setTitle:cardholder forState:UIControlStateNormal];
[self.expirationMonthButton
setTitle:[NSString stringWithFormat:@"%02d", card.expiration_month()]
forState:UIControlStateNormal];
[self.expirationYearButton
setTitle:[NSString stringWithFormat:@"%04d", card.expiration_year()]
forState:UIControlStateNormal];
[self.expirationMonthButton setTitle:card.expirationMonth
forState:UIControlStateNormal];
[self.expirationYearButton setTitle:card.expirationYear
forState:UIControlStateNormal];
}
#pragma mark - Private
......@@ -199,56 +167,53 @@ static const CGFloat ExpirationMarginWidth = 16.0;
grayLine.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:grayLine];
self.cardLabel = [self createLabel];
self.cardLabel = CreateLabel();
[self.contentView addSubview:self.cardLabel];
[self setHorizontalConstraintsForViews:@[ self.cardLabel ]
guide:grayLine
shift:0];
HorizontalConstraintsForViewsOnGuideWithShift(@[ self.cardLabel ], grayLine,
0);
self.cardNumberButton =
[self createButtonForAction:@selector(userDidTapCardNumber:)];
CreateButtonWithSelectorAndTarget(@selector(userDidTapCardNumber:), self);
[self.contentView addSubview:self.cardNumberButton];
[self setHorizontalConstraintsForViews:@[ self.cardNumberButton ]
guide:grayLine
shift:0];
HorizontalConstraintsForViewsOnGuideWithShift(@[ self.cardNumberButton ],
grayLine, 0);
self.cardholderButton =
[self createButtonForAction:@selector(userDidTapCardInfo:)];
CreateButtonWithSelectorAndTarget(@selector(userDidTapCardInfo:), self);
[self.contentView addSubview:self.cardholderButton];
[self setHorizontalConstraintsForViews:@[ self.cardholderButton ]
guide:grayLine
shift:0];
HorizontalConstraintsForViewsOnGuideWithShift(@[ self.cardholderButton ],
grayLine, 0);
// Expiration line.
self.expirationMonthButton =
[self createButtonForAction:@selector(userDidTapCardInfo:)];
[self setHorizontalMarginConstraintsForButton:self.expirationMonthButton
width:ExpirationMarginWidth];
CreateButtonWithSelectorAndTarget(@selector(userDidTapCardInfo:), self);
HorizontalConstraintsMarginForButtonWithWidth(self.expirationMonthButton,
ExpirationMarginWidth);
[self.contentView addSubview:self.expirationMonthButton];
self.expirationYearButton =
[self createButtonForAction:@selector(userDidTapCardInfo:)];
[self setHorizontalMarginConstraintsForButton:self.expirationYearButton
width:ExpirationMarginWidth];
CreateButtonWithSelectorAndTarget(@selector(userDidTapCardInfo:), self);
HorizontalConstraintsMarginForButtonWithWidth(self.expirationYearButton,
ExpirationMarginWidth);
[self.contentView addSubview:self.expirationYearButton];
UILabel* expirationSeparatorLabel = [self createLabel];
UILabel* expirationSeparatorLabel = CreateLabel();
expirationSeparatorLabel.text = @"/";
[self.contentView addSubview:expirationSeparatorLabel];
[self syncBaselinesForViews:@[
expirationSeparatorLabel, self.expirationYearButton
]
onView:self.expirationMonthButton];
[self setHorizontalConstraintsForViews:@[
self.expirationMonthButton, expirationSeparatorLabel,
self.expirationYearButton
]
guide:grayLine
shift:-ExpirationMarginWidth];
[self setVerticalSpacingConstraintsForViews:@[
self.cardLabel, self.cardNumberButton, self.cardholderButton,
self.expirationMonthButton
]
container:self.contentView];
SyncBaselinesForViewsOnView(
@[ expirationSeparatorLabel, self.expirationYearButton ],
self.expirationMonthButton);
HorizontalConstraintsForViewsOnGuideWithShift(
@[
self.expirationMonthButton, expirationSeparatorLabel,
self.expirationYearButton
],
grayLine, -ExpirationMarginWidth);
VerticalConstraintsSpacingForViewsInContainer(
@[
self.cardLabel, self.cardNumberButton, self.cardholderButton,
self.expirationMonthButton
],
self.contentView);
id<LayoutGuideProvider> safeArea = self.contentView.safeAreaLayoutGuide;
......@@ -267,15 +232,11 @@ static const CGFloat ExpirationMarginWidth = 16.0;
}
- (void)userDidTapCardNumber:(UIButton*)sender {
if (self.card.record_type() == autofill::CreditCard::MASKED_SERVER_CARD) {
NSString* number = self.card.number;
if (!number.length) {
[self.navigationDelegate requestFullCreditCard:self.card];
} else {
[self.contentDelegate
userDidPickContent:base::SysUTF16ToNSString(
autofill::CreditCard::StripSeparators(
self.card.GetRawInfo(
autofill::CREDIT_CARD_NUMBER)))
isSecure:NO];
[self.contentDelegate userDidPickContent:number isSecure:NO];
}
}
......@@ -283,101 +244,4 @@ static const CGFloat ExpirationMarginWidth = 16.0;
[self.contentDelegate userDidPickContent:sender.titleLabel.text isSecure:NO];
}
// Creates a blank button for the given |action|.
- (UIButton*)createButtonForAction:(SEL)action {
UIButton* button = [UIButton buttonWithType:UIButtonTypeSystem];
[button setTitleColor:UIColor.cr_manualFillTintColor
forState:UIControlStateNormal];
button.translatesAutoresizingMaskIntoConstraints = NO;
button.titleLabel.font =
[UIFont preferredFontForTextStyle:UIFontTextStyleBody];
button.titleLabel.adjustsFontForContentSizeCategory = YES;
[button addTarget:self
action:action
forControlEvents:UIControlEventTouchUpInside];
return button;
}
// Creates horizontal constraints for given |button| based on given |width| on
// both sides.
- (void)setHorizontalMarginConstraintsForButton:(UIButton*)button
width:(CGFloat)width {
[NSLayoutConstraint activateConstraints:@[
[button.leadingAnchor
constraintEqualToAnchor:button.titleLabel.leadingAnchor
constant:-width],
[button.trailingAnchor
constraintEqualToAnchor:button.titleLabel.trailingAnchor
constant:width],
]];
}
// Sets vertical layout for the button or labels rows in |views| inside
// |container|.
- (void)setVerticalSpacingConstraintsForViews:(NSArray<UIView*>*)views
container:(UIView*)container {
NSMutableArray* verticalConstraints = [[NSMutableArray alloc] init];
// Multipliers of these constraints are calculated based on a 24 base
// system spacing.
NSLayoutYAxisAnchor* previousAnchor = container.topAnchor;
CGFloat multiplier = TopSystemSpacingMultiplier;
for (UIView* view in views) {
[verticalConstraints
addObject:[view.firstBaselineAnchor
constraintEqualToSystemSpacingBelowAnchor:previousAnchor
multiplier:multiplier]];
multiplier = MiddleSystemSpacingMultiplier;
previousAnchor = view.lastBaselineAnchor;
}
multiplier = BottomSystemSpacingMultiplier;
[verticalConstraints
addObject:[container.bottomAnchor
constraintEqualToSystemSpacingBelowAnchor:previousAnchor
multiplier:multiplier]];
[NSLayoutConstraint activateConstraints:verticalConstraints];
}
// Sets constraints for the given |views|, so at to lay them out horizontally,
// parallel to the given |guide| view, and applying the given constant |shift|
// to the whole row.
- (void)setHorizontalConstraintsForViews:(NSArray<UIView*>*)views
guide:(UIView*)guide
shift:(CGFloat)shift {
NSMutableArray* horizontalConstraints = [[NSMutableArray alloc] init];
NSLayoutXAxisAnchor* previousAnchor = guide.leadingAnchor;
for (UIView* view in views) {
[horizontalConstraints
addObject:[view.leadingAnchor constraintEqualToAnchor:previousAnchor
constant:shift]];
previousAnchor = view.trailingAnchor;
shift = 0;
}
if (views.count > 0) {
[horizontalConstraints
addObject:[views.lastObject.trailingAnchor
constraintLessThanOrEqualToAnchor:guide.trailingAnchor
constant:shift]];
}
[NSLayoutConstraint activateConstraints:horizontalConstraints];
}
// Sets all baseline anchors for the gievn |views| to match the one on |onView|.
- (void)syncBaselinesForViews:(NSArray<UIView*>*)views onView:(UIView*)onView {
NSMutableArray* baselinesConstraints = [[NSMutableArray alloc] init];
for (UIView* view in views) {
[baselinesConstraints
addObject:[view.firstBaselineAnchor
constraintEqualToAnchor:onView.firstBaselineAnchor]];
}
[NSLayoutConstraint activateConstraints:baselinesConstraints];
}
// Creates a blank label.
- (UILabel*)createLabel {
UILabel* label = [[UILabel alloc] init];
label.translatesAutoresizingMaskIntoConstraints = NO;
label.adjustsFontForContentSizeCategory = YES;
return label;
}
@end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment