Commit 6dde4cbf authored by Alfonso Garza's avatar Alfonso Garza Committed by Commit Bot

[AF] Add expiration date fix flow on iOS.

https://screenshot.googleplex.com/ihaqzrJ6nyG.png
https://screenshot.googleplex.com/D1Y74zXf1UM.png

BUG=993850

Change-Id: I22c9cddd60625f6620cc329f269a46d8ab379459
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1888539
Commit-Queue: Alfonso Garza <alfonsogarza@google.com>
Auto-Submit: Alfonso Garza <alfonsogarza@google.com>
Reviewed-by: default avatarFabio Tirelo <ftirelo@chromium.org>
Reviewed-by: default avatarJared Saul <jsaul@google.com>
Reviewed-by: default avatarJohn Wu <jzw@chromium.org>
Reviewed-by: default avatarOlivier Robin <olivierrobin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#711579}
parent 7f91d341
......@@ -353,9 +353,7 @@ class AutofillClient : public RiskDataLoader {
// the card should be uploaded to payments with updated name from the user.
virtual void ConfirmAccountNameFixFlow(
base::OnceCallback<void(const base::string16&)> callback) = 0;
#endif // defined(OS_ANDROID) || defined(OS_IOS)
#if defined(OS_ANDROID)
// Display the expiration date fix flow prompt with the |card| details
// and run the |callback| if the card should be uploaded to payments with
// updated expiration date from the user.
......@@ -363,7 +361,7 @@ class AutofillClient : public RiskDataLoader {
const CreditCard& card,
base::OnceCallback<void(const base::string16&, const base::string16&)>
callback) = 0;
#endif // defined(OS_ANDROID)
#endif // defined(OS_ANDROID) || defined(OS_IOS)
// Runs |callback| once the user makes a decision with respect to the
// offer-to-save prompt. Displays the contents of |legal_message_lines|
......
......@@ -775,20 +775,18 @@ void CreditCardSaveManager::OnUserDidDecideOnUploadSave(
switch (user_decision) {
case AutofillClient::ACCEPTED:
#if defined(OS_ANDROID) || defined(OS_IOS)
// On Android and iOS, requesting cardholder name is a two step flow.
// On mobile, requesting cardholder name is a two step flow.
if (should_request_name_from_user_) {
client_->ConfirmAccountNameFixFlow(base::BindOnce(
&CreditCardSaveManager::OnUserDidAcceptAccountNameFixFlow,
weak_ptr_factory_.GetWeakPtr()));
#if defined(OS_ANDROID)
// On Android, requesting expiration date is a two step flow.
// On mobile, requesting expiration date is a two step flow.
} else if (should_request_expiration_date_from_user_) {
client_->ConfirmExpirationDateFixFlow(
upload_request_.card,
base::BindOnce(
&CreditCardSaveManager::OnUserDidAcceptExpirationDateFixFlow,
weak_ptr_factory_.GetWeakPtr()));
#endif // defined(OS_ANDROID)
} else {
OnUserDidAcceptUploadHelper(user_provided_card_details);
}
......@@ -815,16 +813,14 @@ void CreditCardSaveManager::OnUserDidAcceptAccountNameFixFlow(
/*expiration_date_month=*/base::string16(),
/*expiration_date_year=*/base::string16()});
}
#endif
#if defined(OS_ANDROID)
void CreditCardSaveManager::OnUserDidAcceptExpirationDateFixFlow(
const base::string16& month,
const base::string16& year) {
OnUserDidAcceptUploadHelper(
{/*cardholder_name=*/base::string16(), month, year});
}
#endif
#endif // defined(OS_ANDROID) || defined(OS_IOS)
void CreditCardSaveManager::OnUserDidAcceptUploadHelper(
const AutofillClient::UserProvidedCardDetails& user_provided_card_details) {
......
......@@ -224,15 +224,13 @@ class CreditCardSaveManager {
// Only relevant for mobile as fix flow is two steps on mobile compared to
// one step on desktop.
void OnUserDidAcceptAccountNameFixFlow(const base::string16& cardholder_name);
#endif // defined(OS_ANDROID) || defined(OS_IOS)
#if defined(OS_ANDROID)
// Upload the card details with the user provided expiration date month and
// year. Only relevant for mobile as fix flow is two steps on mobile compared
// to one step on desktop.
void OnUserDidAcceptExpirationDateFixFlow(const base::string16& month,
const base::string16& year);
#endif // defined(OS_ANDROID)
#endif // defined(OS_ANDROID) || defined(OS_IOS)
// Helper function that calls SendUploadCardRequest by setting
// UserProvidedCardDetails.
......
......@@ -145,9 +145,7 @@ void TestAutofillClient::ConfirmAccountNameFixFlow(
credit_card_name_fix_flow_bubble_was_shown_ = true;
std::move(callback).Run(base::string16(base::ASCIIToUTF16("Gaia Name")));
}
#endif // defined(OS_ANDROID) || defined(OS_IOS)
#if defined(OS_ANDROID)
void TestAutofillClient::ConfirmExpirationDateFixFlow(
const CreditCard& card,
base::OnceCallback<void(const base::string16&, const base::string16&)>
......@@ -157,7 +155,7 @@ void TestAutofillClient::ConfirmExpirationDateFixFlow(
base::string16(base::ASCIIToUTF16("03")),
base::string16(base::ASCIIToUTF16(test::NextYear().c_str())));
}
#endif // defined(OS_ANDROID)
#endif // defined(OS_ANDROID) || defined(OS_IOS)
void TestAutofillClient::ConfirmSaveCreditCardToCloud(
const CreditCard& card,
......
......@@ -81,14 +81,11 @@ class TestAutofillClient : public AutofillClient {
#if defined(OS_ANDROID) || defined(OS_IOS)
void ConfirmAccountNameFixFlow(
base::OnceCallback<void(const base::string16&)> callback) override;
#endif // defined(OS_ANDROID) || defined(OS_IOS)
#if defined(OS_ANDROID)
void ConfirmExpirationDateFixFlow(
const CreditCard& card,
base::OnceCallback<void(const base::string16&, const base::string16&)>
callback) override;
#endif // defined(OS_ANDROID)
#endif // defined(OS_ANDROID) || defined(OS_IOS)
void ConfirmSaveCreditCardToCloud(
const CreditCard& card,
const LegalMessageLines& legal_message_lines,
......
......@@ -32,6 +32,10 @@ class CardExpirationDateFixFlowController {
virtual base::string16 GetTitleText() const = 0;
virtual base::string16 GetSaveButtonLabel() const = 0;
virtual base::string16 GetCardLabel() const = 0;
virtual base::string16 GetCancelButtonLabel() const = 0;
virtual base::string16 GetInputLabel() const = 0;
virtual base::string16 GetDateSeparator() const = 0;
virtual base::string16 GetInvalidDateError() const = 0;
};
} // namespace autofill
......
......@@ -98,4 +98,25 @@ base::string16 CardExpirationDateFixFlowControllerImpl::GetCardLabel() const {
return card_label_;
}
base::string16 CardExpirationDateFixFlowControllerImpl::GetCancelButtonLabel()
const {
return l10n_util::GetStringUTF16(IDS_CANCEL);
}
base::string16 CardExpirationDateFixFlowControllerImpl::GetInputLabel() const {
return l10n_util::GetStringUTF16(
IDS_AUTOFILL_SAVE_CARD_UPDATE_EXPIRATION_DATE_TOOLTIP);
}
base::string16 CardExpirationDateFixFlowControllerImpl::GetDateSeparator()
const {
return l10n_util::GetStringUTF16(IDS_AUTOFILL_EXPIRATION_DATE_SEPARATOR);
}
base::string16 CardExpirationDateFixFlowControllerImpl::GetInvalidDateError()
const {
return l10n_util::GetStringUTF16(
IDS_AUTOFILL_SAVE_CARD_UPDATE_EXPIRATION_DATE_ERROR_TRY_AGAIN);
}
} // namespace autofill
......@@ -42,6 +42,10 @@ class CardExpirationDateFixFlowControllerImpl
base::string16 GetTitleText() const override;
base::string16 GetSaveButtonLabel() const override;
base::string16 GetCardLabel() const override;
base::string16 GetCancelButtonLabel() const override;
base::string16 GetInputLabel() const override;
base::string16 GetDateSeparator() const override;
base::string16 GetInvalidDateError() const override;
private:
// View that displays the fix flow prompt.
......
......@@ -195,6 +195,14 @@
<message name="IDS_AUTOFILL_SAVE_CARD_UPDATE_EXPIRATION_DATE_TITLE" desc="Title for the dialog requesting the user to fill in the expiration date of the credit card during the save card to google flow.">
Enter expiration date
</message>
<if expr="is_android or is_ios">
<message name="IDS_AUTOFILL_SAVE_CARD_UPDATE_EXPIRATION_DATE_TOOLTIP" desc="The tooltip text for the credit card expiration date textfield. Note that the translation should not have period.">
Expiration date
</message>
<message name="IDS_AUTOFILL_SAVE_CARD_UPDATE_EXPIRATION_DATE_ERROR_TRY_AGAIN" desc="Error message that encourages the user to try to re-enter their credit card expiration date after selecting an invalid expiration date.">
Enter an expiration date in the future and try again
</message>
</if>
<message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_TOOLTIP" desc="The tooltip hover text that explains why credit card upload to Google Payments is being offered.">
Chrome is offering to save your cards in your Google Account because you are signed in. You can change this behavior in settings.
</message>
......
......@@ -72,6 +72,8 @@ source_set("autofill") {
source_set("bridges") {
configs += [ "//build/config/compiler:enable_arc" ]
sources = [
"card_expiration_date_fix_flow_view_bridge.h",
"card_expiration_date_fix_flow_view_bridge.mm",
"card_name_fix_flow_view_bridge.h",
"card_name_fix_flow_view_bridge.mm",
"card_unmask_prompt_view_bridge.h",
......@@ -104,6 +106,8 @@ source_set("autofill_ui") {
configs += [ "//build/config/compiler:enable_arc" ]
sources = [
"autofill_ui_type.h",
"expiration_date_picker.h",
"expiration_date_picker.mm",
"save_card_infobar_controller.h",
"save_card_infobar_controller.mm",
"save_card_infobar_view.h",
......
// 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.
#ifndef IOS_CHROME_BROWSER_UI_AUTOFILL_CARD_EXPIRATION_DATE_FIX_FLOW_VIEW_BRIDGE_H_
#define IOS_CHROME_BROWSER_UI_AUTOFILL_CARD_EXPIRATION_DATE_FIX_FLOW_VIEW_BRIDGE_H_
#import <UIKit/UIKit.h>
#include <memory>
#include "base/memory/weak_ptr.h"
#include "base/strings/string16.h"
#include "components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller.h"
#include "components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_view.h"
namespace autofill {
class CardExpirationDateFixFlowViewBridge
: public CardExpirationDateFixFlowView {
public:
CardExpirationDateFixFlowViewBridge(
CardExpirationDateFixFlowController* controller,
UIViewController* base_view_controller);
~CardExpirationDateFixFlowViewBridge() override;
// CardExpirationDateFixFlowView:
void Show() override;
void ControllerGone() override;
CardExpirationDateFixFlowController* GetController();
// Called when the user confirms the expirationn date.
void OnConfirmedExpirationDate(const base::string16& month,
const base::string16& year);
// Called when the user cancels the fix flow.
void OnDismissed();
// Closes the view.
void PerformClose();
// Deletes self. This should only be called by
// CardExpirationDateFixFlowViewController after it finishes dismissing its
// own UI elements.
void DeleteSelf();
protected:
UIViewController* view_controller_;
private:
// The controller |this| queries for logic and state.
CardExpirationDateFixFlowController* controller_; // weak
// Weak reference to the view controller used to present UI.
__weak UIViewController* presenting_view_controller_;
base::WeakPtrFactory<CardExpirationDateFixFlowViewBridge> weak_ptr_factory_{
this};
DISALLOW_COPY_AND_ASSIGN(CardExpirationDateFixFlowViewBridge);
};
} // namespace autofill
@interface CardExpirationDateFixFlowViewController : UITableViewController
// Designated initializer. |bridge| must not be null.
- (instancetype)initWithBridge:
(autofill::CardExpirationDateFixFlowViewBridge*)bridge
NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE;
- (instancetype)initWithStyle:(UITableViewStyle)style NS_UNAVAILABLE;
- (instancetype)initWithNibName:(NSString*)nibNameOrNil
bundle:(NSBundle*)nibBundleOrNil NS_UNAVAILABLE;
@end
#endif // IOS_CHROME_BROWSER_UI_AUTOFILL_CARD_EXPIRATION_DATE_FIX_FLOW_VIEW_BRIDGE_H_
// 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.
#include <memory>
#include <string>
#include "base/mac/foundation_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/values.h"
#include "components/strings/grit/components_strings.h"
#include "ios/chrome/browser/ui/autofill/card_expiration_date_fix_flow_view_bridge.h"
#include "ios/chrome/browser/ui/autofill/expiration_date_picker.h"
#import "ios/chrome/browser/ui/list_model/list_model.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_header_footer_item.h"
#import "ios/chrome/browser/ui/util/label_link_controller.h"
#import "ios/chrome/browser/ui/util/rtl_geometry.h"
#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
#import "ios/chrome/common/colors/semantic_color_names.h"
#include "ui/base/l10n/l10n_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
CGFloat const kMainSection = 0;
CGFloat const kNumberOfRowsInMainSection = 1;
namespace autofill {
#pragma mark CardExpirationDateFixFlowViewBridge
CardExpirationDateFixFlowViewBridge::CardExpirationDateFixFlowViewBridge(
CardExpirationDateFixFlowController* controller,
UIViewController* presenting_view_controller)
: controller_(controller),
presenting_view_controller_(presenting_view_controller) {
DCHECK(controller_);
DCHECK(presenting_view_controller_);
}
CardExpirationDateFixFlowViewBridge::~CardExpirationDateFixFlowViewBridge() {
if (controller_)
controller_->OnDialogClosed();
}
void CardExpirationDateFixFlowViewBridge::Show() {
// Wrap CardExpirationDateFixFlowViewController with navigation controller to
// include a navigation bar.
view_controller_ = [[UINavigationController alloc]
initWithRootViewController:[[CardExpirationDateFixFlowViewController
alloc] initWithBridge:this]];
[presenting_view_controller_ presentViewController:view_controller_
animated:YES
completion:nil];
}
void CardExpirationDateFixFlowViewBridge::ControllerGone() {
controller_ = nullptr;
PerformClose();
}
CardExpirationDateFixFlowController*
CardExpirationDateFixFlowViewBridge::GetController() {
return controller_;
}
void CardExpirationDateFixFlowViewBridge::OnConfirmedExpirationDate(
const base::string16& month,
const base::string16& year) {
controller_->OnAccepted(month, year);
PerformClose();
}
void CardExpirationDateFixFlowViewBridge::OnDismissed() {
controller_->OnDismissed();
PerformClose();
}
void CardExpirationDateFixFlowViewBridge::PerformClose() {
base::WeakPtr<CardExpirationDateFixFlowViewBridge> weakSelf =
weak_ptr_factory_.GetWeakPtr();
[view_controller_ dismissViewControllerAnimated:YES
completion:^{
weakSelf->DeleteSelf();
}];
}
void CardExpirationDateFixFlowViewBridge::DeleteSelf() {
delete this;
}
} // namespace autofill
@interface CardExpirationDateFixFlowViewController () <UITextFieldDelegate,
UITableViewDelegate> {
UIBarButtonItem* _confirmButton;
NSString* _expirationDateMonth;
NSString* _expirationDateYear;
ExpirationDatePicker* _expirationDatePicker;
TableViewTextEditCell* _confirmExpirationDateCell;
TableViewTextHeaderFooterView* _footerView;
autofill::CardExpirationDateFixFlowViewBridge* _bridge; // weak
}
@end
@implementation CardExpirationDateFixFlowViewController
- (instancetype)initWithBridge:
(autofill::CardExpirationDateFixFlowViewBridge*)bridge {
DCHECK(bridge);
if (self = [super initWithStyle:UITableViewStyleGrouped]) {
_bridge = bridge;
self.title =
base::SysUTF16ToNSString(_bridge->GetController()->GetTitleText());
}
return self;
}
#pragma mark - UIViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.delegate = self;
autofill::CardExpirationDateFixFlowController* controller =
_bridge->GetController();
NSString* cancelButtonLabel =
base::SysUTF16ToNSString(controller->GetCancelButtonLabel());
self.navigationItem.leftBarButtonItem =
[[UIBarButtonItem alloc] initWithTitle:cancelButtonLabel
style:UIBarButtonItemStylePlain
target:self
action:@selector(onCancel:)];
NSString* saveButtonLabel =
base::SysUTF16ToNSString(controller->GetSaveButtonLabel());
_confirmButton =
[[UIBarButtonItem alloc] initWithTitle:saveButtonLabel
style:UIBarButtonItemStylePlain
target:self
action:@selector(onConfirm:)];
self.navigationItem.rightBarButtonItem = _confirmButton;
_expirationDatePicker =
[[ExpirationDatePicker alloc] initWithFrame:CGRectZero];
__weak CardExpirationDateFixFlowViewController* weakSelf = self;
_expirationDatePicker.onDateSelected = ^(NSString* month, NSString* year) {
CardExpirationDateFixFlowViewController* strongSelf = weakSelf;
[strongSelf didSelectMonth:month year:year];
};
_confirmExpirationDateCell = [[TableViewTextEditCell alloc] init];
_confirmExpirationDateCell.selectionStyle = UITableViewCellSelectionStyleNone;
_confirmExpirationDateCell.useCustomSeparator = NO;
_confirmExpirationDateCell.textLabel.text =
base::SysUTF16ToNSString(controller->GetInputLabel());
UITextField* textField = _confirmExpirationDateCell.textField;
textField.inputView = _expirationDatePicker;
textField.textColor = [UIColor colorNamed:kBlueColor];
textField.clearButtonMode = UITextFieldViewModeNever;
textField.delegate = self;
_footerView = [[TableViewTextHeaderFooterView alloc] init];
_footerView.subtitleLabel.textColor = [UIColor colorNamed:kRedColor];
// Set initial value.
[self didSelectMonth:_expirationDatePicker.month
year:_expirationDatePicker.year];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[_confirmExpirationDateCell.textField becomeFirstResponder];
}
#pragma mark - UITableViewDelegate
- (NSString*)tableView:(UITableView*)tableView
titleForHeaderInSection:(NSInteger)section {
DCHECK(section == kMainSection);
return base::SysUTF16ToNSString(_bridge->GetController()->GetCardLabel());
}
- (UIView*)tableView:(UITableView*)tableView
viewForFooterInSection:(NSInteger)section {
DCHECK(section == kMainSection);
return _footerView;
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView*)tableView
numberOfRowsInSection:(NSInteger)section {
DCHECK(section == kMainSection);
return kNumberOfRowsInMainSection;
}
- (UITableViewCell*)tableView:(UITableView*)tableView
cellForRowAtIndexPath:(NSIndexPath*)indexPath {
DCHECK(indexPath.section == kMainSection);
return _confirmExpirationDateCell;
}
#pragma mark - UITextFieldDelegate
- (BOOL)textField:(UITextField*)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString*)string {
return NO; /* Prevent any input from outside the date picker. */
}
#pragma mark - Private
- (void)onCancel:(id)sender {
_bridge->OnDismissed();
}
- (void)onConfirm:(id)sender {
DCHECK(_expirationDateMonth.length > 0);
DCHECK(_expirationDateYear.length > 0);
_bridge->OnConfirmedExpirationDate(
base::SysNSStringToUTF16(_expirationDateYear),
base::SysNSStringToUTF16(_expirationDateMonth));
}
- (void)didSelectMonth:(NSString*)month year:(NSString*)year {
_expirationDateYear = year;
_expirationDateMonth = month;
NSString* dateSeparator =
base::SysUTF16ToNSString(_bridge->GetController()->GetDateSeparator());
_confirmExpirationDateCell.textField.text =
[NSString stringWithFormat:@"%@%@%@", month, dateSeparator, year];
[self updateSaveButtonEnabledStatus];
}
- (void)updateSaveButtonEnabledStatus {
NSCalendar* calendar = NSCalendar.currentCalendar;
NSDateComponents* calendarComponents =
[calendar components:NSCalendarUnitMonth | NSCalendarUnitYear
fromDate:[NSDate date]];
NSInteger currentYear = [calendarComponents year];
NSInteger currentMonth = [calendarComponents month];
if ([_expirationDateYear intValue] < currentYear ||
([_expirationDateYear intValue] == currentYear &&
[_expirationDateMonth intValue] < currentMonth)) {
_footerView.subtitleLabel.text = base::SysUTF16ToNSString(
_bridge->GetController()->GetInvalidDateError());
_confirmButton.enabled = NO;
} else {
_footerView.subtitleLabel.text = nil;
_confirmButton.enabled = YES;
}
}
@end
......@@ -24,7 +24,7 @@ class CardNameFixFlowViewBridge : public CardNameFixFlowView {
UIViewController* base_view_controller);
~CardNameFixFlowViewBridge() override;
// CardUnmaskPromptView:
// CardNameFixFlowView:
void Show() override;
void ControllerGone() override;
......
......@@ -17,6 +17,7 @@
#include "components/autofill/core/browser/payments/legal_message_line.h"
#include "components/autofill/core/browser/payments/strike_database.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.h"
#include "components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.h"
#include "components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
......@@ -90,6 +91,10 @@ class ChromeAutofillClientIOS : public AutofillClient {
LocalSaveCardPromptCallback callback) override;
void ConfirmAccountNameFixFlow(
base::OnceCallback<void(const base::string16&)> callback) override;
void ConfirmExpirationDateFixFlow(
const CreditCard& card,
base::OnceCallback<void(const base::string16&, const base::string16&)>
callback) override;
void ConfirmSaveCreditCardToCloud(
const CreditCard& card,
const LegalMessageLines& legal_message_lines,
......@@ -145,6 +150,8 @@ class ChromeAutofillClientIOS : public AutofillClient {
CardUnmaskPromptControllerImpl unmask_controller_;
std::unique_ptr<LogManager> log_manager_;
CardNameFixFlowControllerImpl card_name_fix_flow_controller_;
CardExpirationDateFixFlowControllerImpl
card_expiration_date_fix_flow_controller_;
// A weak reference to the view controller used to present UI.
__weak UIViewController* base_view_controller_;
......
......@@ -37,6 +37,7 @@
#include "ios/chrome/browser/signin/identity_manager_factory.h"
#include "ios/chrome/browser/sync/profile_sync_service_factory.h"
#include "ios/chrome/browser/translate/chrome_ios_translate_client.h"
#include "ios/chrome/browser/ui/autofill/card_expiration_date_fix_flow_view_bridge.h"
#include "ios/chrome/browser/ui/autofill/card_name_fix_flow_view_bridge.h"
#include "ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.h"
#include "ios/chrome/browser/ui/autofill/save_card_infobar_controller.h"
......@@ -278,6 +279,18 @@ void ChromeAutofillClientIOS::ConfirmAccountNameFixFlow(
account_name, std::move(callback));
}
void ChromeAutofillClientIOS::ConfirmExpirationDateFixFlow(
const CreditCard& card,
base::OnceCallback<void(const base::string16&, const base::string16&)>
callback) {
card_expiration_date_fix_flow_controller_.Show(
// autofill::CardExpirationDateFixFlowViewBridge manages its own lifetime,
// so do not use std::unique_ptr<> here.
new autofill::CardExpirationDateFixFlowViewBridge(
&card_expiration_date_fix_flow_controller_, base_view_controller_),
card, std::move(callback));
}
void ChromeAutofillClientIOS::ConfirmSaveCreditCardToCloud(
const CreditCard& card,
const LegalMessageLines& legal_message_lines,
......
// 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.
#ifndef IOS_CHROME_BROWSER_UI_AUTOFILL_EXPIRATION_DATE_PICKER_H_
#define IOS_CHROME_BROWSER_UI_AUTOFILL_EXPIRATION_DATE_PICKER_H_
#import <UIKit/UIKit.h>
// UIPickerView for selecting the month and year of an expiration date.
@interface ExpirationDatePicker : UIPickerView
@property(nonatomic, copy, readonly) NSString* month;
@property(nonatomic, copy, readonly) NSString* year;
@property(nonatomic) void (^onDateSelected)(NSString* month, NSString* year);
@end
#endif // IOS_CHROME_BROWSER_UI_AUTOFILL_EXPIRATION_DATE_PICKER_H_
// 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.
#include "ios/chrome/browser/ui/autofill/expiration_date_picker.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
int const kNumberOfSections = 2;
int const kMonthSection = 0;
int const kYearSection = 1;
int const kNumberOfYearsToShow = 20;
}
@interface ExpirationDatePicker () <UIPickerViewDelegate,
UIPickerViewDataSource>
@end
@implementation ExpirationDatePicker {
NSArray* _months;
NSArray* _years;
}
@synthesize onDateSelected = _onDateSelected;
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
NSCalendar* calendar = NSCalendar.currentCalendar;
NSDateComponents* calendarComponents =
[calendar components:NSCalendarUnitMonth | NSCalendarUnitYear
fromDate:[NSDate date]];
NSInteger currentYear = [calendarComponents year];
NSMutableArray* years = [[NSMutableArray alloc] init];
for (int i = 0; i < kNumberOfYearsToShow; i++) {
[years addObject:[NSString stringWithFormat:@"%lu", currentYear + i]];
}
_years = [years copy];
NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
NSMutableArray<NSString*>* months = [[NSMutableArray alloc]
initWithCapacity:dateFormatter.monthSymbols.count];
for (NSUInteger i = 0; i < dateFormatter.monthSymbols.count; i++) {
NSString* monthSymbol = dateFormatter.monthSymbols[i];
[months addObject:[NSString
stringWithFormat:@"%@ (%lu)", monthSymbol, i + 1]];
}
_months = months;
self.delegate = self;
self.dataSource = self;
self.backgroundColor = UIColor.whiteColor;
NSInteger currentMonth = [calendarComponents month];
[self selectRow:MAX(0, currentMonth - 1)
inComponent:kMonthSection
animated:NO];
}
return self;
}
- (NSString*)month {
NSUInteger month = [self selectedRowInComponent:kMonthSection] + 1;
return [NSString stringWithFormat:@"%lu", month];
}
- (NSString*)year {
return _years[[self selectedRowInComponent:kYearSection]];
}
#pragma mark UIPickerViewDataSource
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView*)pickerView {
return kNumberOfSections;
}
- (NSInteger)pickerView:(UIPickerView*)pickerView
numberOfRowsInComponent:(NSInteger)component {
switch (component) {
case kMonthSection:
return _months.count;
case kYearSection:
return _years.count;
default:
return 0;
}
}
- (NSString*)pickerView:(UIPickerView*)pickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component {
switch (component) {
case kMonthSection:
return _months[row];
case kYearSection:
return _years[row];
default:
return nil;
}
}
- (void)pickerView:(UIPickerView*)pickerView
didSelectRow:(NSInteger)row
inComponent:(NSInteger)component {
if (_onDateSelected != nil) {
_onDateSelected(self.month, self.year);
}
}
@end
......@@ -502,7 +502,6 @@ NSString* const kTitleViewAccessibilityIdentifier = @"titleView";
DCHECK(self.cardIssuerIcon);
DCHECK_GT(self.cardLabel.length, 0UL);
DCHECK_GT(self.cardSublabel.length, 0UL);
// The leading edge aligned card details container view. Contains the card
// issuer network icon, the card label, and the card sublabel.
......
......@@ -82,6 +82,10 @@ class WebViewAutofillClientIOS : public AutofillClient {
LocalSaveCardPromptCallback callback) override;
void ConfirmAccountNameFixFlow(
base::OnceCallback<void(const base::string16&)> callback) override;
void ConfirmExpirationDateFixFlow(
const CreditCard& card,
base::OnceCallback<void(const base::string16&, const base::string16&)>
callback) override;
void ConfirmSaveCreditCardToCloud(
const CreditCard& card,
const LegalMessageLines& legal_message_lines,
......
......@@ -189,6 +189,13 @@ void WebViewAutofillClientIOS::ConfirmAccountNameFixFlow(
NOTIMPLEMENTED();
}
void WebViewAutofillClientIOS::ConfirmExpirationDateFixFlow(
const CreditCard& card,
base::OnceCallback<void(const base::string16&, const base::string16&)>
callback) {
NOTIMPLEMENTED();
}
void WebViewAutofillClientIOS::ConfirmSaveCreditCardToCloud(
const CreditCard& card,
const LegalMessageLines& legal_message_lines,
......
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