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 { ...@@ -353,9 +353,7 @@ class AutofillClient : public RiskDataLoader {
// the card should be uploaded to payments with updated name from the user. // the card should be uploaded to payments with updated name from the user.
virtual void ConfirmAccountNameFixFlow( virtual void ConfirmAccountNameFixFlow(
base::OnceCallback<void(const base::string16&)> callback) = 0; 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 // Display the expiration date fix flow prompt with the |card| details
// and run the |callback| if the card should be uploaded to payments with // and run the |callback| if the card should be uploaded to payments with
// updated expiration date from the user. // updated expiration date from the user.
...@@ -363,7 +361,7 @@ class AutofillClient : public RiskDataLoader { ...@@ -363,7 +361,7 @@ class AutofillClient : public RiskDataLoader {
const CreditCard& card, const CreditCard& card,
base::OnceCallback<void(const base::string16&, const base::string16&)> base::OnceCallback<void(const base::string16&, const base::string16&)>
callback) = 0; 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 // Runs |callback| once the user makes a decision with respect to the
// offer-to-save prompt. Displays the contents of |legal_message_lines| // offer-to-save prompt. Displays the contents of |legal_message_lines|
......
...@@ -775,20 +775,18 @@ void CreditCardSaveManager::OnUserDidDecideOnUploadSave( ...@@ -775,20 +775,18 @@ void CreditCardSaveManager::OnUserDidDecideOnUploadSave(
switch (user_decision) { switch (user_decision) {
case AutofillClient::ACCEPTED: case AutofillClient::ACCEPTED:
#if defined(OS_ANDROID) || defined(OS_IOS) #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_) { if (should_request_name_from_user_) {
client_->ConfirmAccountNameFixFlow(base::BindOnce( client_->ConfirmAccountNameFixFlow(base::BindOnce(
&CreditCardSaveManager::OnUserDidAcceptAccountNameFixFlow, &CreditCardSaveManager::OnUserDidAcceptAccountNameFixFlow,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
#if defined(OS_ANDROID) // On mobile, requesting expiration date is a two step flow.
// On Android, requesting expiration date is a two step flow.
} else if (should_request_expiration_date_from_user_) { } else if (should_request_expiration_date_from_user_) {
client_->ConfirmExpirationDateFixFlow( client_->ConfirmExpirationDateFixFlow(
upload_request_.card, upload_request_.card,
base::BindOnce( base::BindOnce(
&CreditCardSaveManager::OnUserDidAcceptExpirationDateFixFlow, &CreditCardSaveManager::OnUserDidAcceptExpirationDateFixFlow,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
#endif // defined(OS_ANDROID)
} else { } else {
OnUserDidAcceptUploadHelper(user_provided_card_details); OnUserDidAcceptUploadHelper(user_provided_card_details);
} }
...@@ -815,16 +813,14 @@ void CreditCardSaveManager::OnUserDidAcceptAccountNameFixFlow( ...@@ -815,16 +813,14 @@ void CreditCardSaveManager::OnUserDidAcceptAccountNameFixFlow(
/*expiration_date_month=*/base::string16(), /*expiration_date_month=*/base::string16(),
/*expiration_date_year=*/base::string16()}); /*expiration_date_year=*/base::string16()});
} }
#endif
#if defined(OS_ANDROID)
void CreditCardSaveManager::OnUserDidAcceptExpirationDateFixFlow( void CreditCardSaveManager::OnUserDidAcceptExpirationDateFixFlow(
const base::string16& month, const base::string16& month,
const base::string16& year) { const base::string16& year) {
OnUserDidAcceptUploadHelper( OnUserDidAcceptUploadHelper(
{/*cardholder_name=*/base::string16(), month, year}); {/*cardholder_name=*/base::string16(), month, year});
} }
#endif #endif // defined(OS_ANDROID) || defined(OS_IOS)
void CreditCardSaveManager::OnUserDidAcceptUploadHelper( void CreditCardSaveManager::OnUserDidAcceptUploadHelper(
const AutofillClient::UserProvidedCardDetails& user_provided_card_details) { const AutofillClient::UserProvidedCardDetails& user_provided_card_details) {
......
...@@ -224,15 +224,13 @@ class CreditCardSaveManager { ...@@ -224,15 +224,13 @@ class CreditCardSaveManager {
// Only relevant for mobile as fix flow is two steps on mobile compared to // Only relevant for mobile as fix flow is two steps on mobile compared to
// one step on desktop. // one step on desktop.
void OnUserDidAcceptAccountNameFixFlow(const base::string16& cardholder_name); 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 // 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 // year. Only relevant for mobile as fix flow is two steps on mobile compared
// to one step on desktop. // to one step on desktop.
void OnUserDidAcceptExpirationDateFixFlow(const base::string16& month, void OnUserDidAcceptExpirationDateFixFlow(const base::string16& month,
const base::string16& year); const base::string16& year);
#endif // defined(OS_ANDROID) #endif // defined(OS_ANDROID) || defined(OS_IOS)
// Helper function that calls SendUploadCardRequest by setting // Helper function that calls SendUploadCardRequest by setting
// UserProvidedCardDetails. // UserProvidedCardDetails.
......
...@@ -145,9 +145,7 @@ void TestAutofillClient::ConfirmAccountNameFixFlow( ...@@ -145,9 +145,7 @@ void TestAutofillClient::ConfirmAccountNameFixFlow(
credit_card_name_fix_flow_bubble_was_shown_ = true; credit_card_name_fix_flow_bubble_was_shown_ = true;
std::move(callback).Run(base::string16(base::ASCIIToUTF16("Gaia Name"))); std::move(callback).Run(base::string16(base::ASCIIToUTF16("Gaia Name")));
} }
#endif // defined(OS_ANDROID) || defined(OS_IOS)
#if defined(OS_ANDROID)
void TestAutofillClient::ConfirmExpirationDateFixFlow( void TestAutofillClient::ConfirmExpirationDateFixFlow(
const CreditCard& card, const CreditCard& card,
base::OnceCallback<void(const base::string16&, const base::string16&)> base::OnceCallback<void(const base::string16&, const base::string16&)>
...@@ -157,7 +155,7 @@ void TestAutofillClient::ConfirmExpirationDateFixFlow( ...@@ -157,7 +155,7 @@ void TestAutofillClient::ConfirmExpirationDateFixFlow(
base::string16(base::ASCIIToUTF16("03")), base::string16(base::ASCIIToUTF16("03")),
base::string16(base::ASCIIToUTF16(test::NextYear().c_str()))); base::string16(base::ASCIIToUTF16(test::NextYear().c_str())));
} }
#endif // defined(OS_ANDROID) #endif // defined(OS_ANDROID) || defined(OS_IOS)
void TestAutofillClient::ConfirmSaveCreditCardToCloud( void TestAutofillClient::ConfirmSaveCreditCardToCloud(
const CreditCard& card, const CreditCard& card,
......
...@@ -81,14 +81,11 @@ class TestAutofillClient : public AutofillClient { ...@@ -81,14 +81,11 @@ class TestAutofillClient : public AutofillClient {
#if defined(OS_ANDROID) || defined(OS_IOS) #if defined(OS_ANDROID) || defined(OS_IOS)
void ConfirmAccountNameFixFlow( void ConfirmAccountNameFixFlow(
base::OnceCallback<void(const base::string16&)> callback) override; base::OnceCallback<void(const base::string16&)> callback) override;
#endif // defined(OS_ANDROID) || defined(OS_IOS)
#if defined(OS_ANDROID)
void ConfirmExpirationDateFixFlow( void ConfirmExpirationDateFixFlow(
const CreditCard& card, const CreditCard& card,
base::OnceCallback<void(const base::string16&, const base::string16&)> base::OnceCallback<void(const base::string16&, const base::string16&)>
callback) override; callback) override;
#endif // defined(OS_ANDROID) #endif // defined(OS_ANDROID) || defined(OS_IOS)
void ConfirmSaveCreditCardToCloud( void ConfirmSaveCreditCardToCloud(
const CreditCard& card, const CreditCard& card,
const LegalMessageLines& legal_message_lines, const LegalMessageLines& legal_message_lines,
......
...@@ -32,6 +32,10 @@ class CardExpirationDateFixFlowController { ...@@ -32,6 +32,10 @@ class CardExpirationDateFixFlowController {
virtual base::string16 GetTitleText() const = 0; virtual base::string16 GetTitleText() const = 0;
virtual base::string16 GetSaveButtonLabel() const = 0; virtual base::string16 GetSaveButtonLabel() const = 0;
virtual base::string16 GetCardLabel() 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 } // namespace autofill
......
...@@ -98,4 +98,25 @@ base::string16 CardExpirationDateFixFlowControllerImpl::GetCardLabel() const { ...@@ -98,4 +98,25 @@ base::string16 CardExpirationDateFixFlowControllerImpl::GetCardLabel() const {
return card_label_; 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 } // namespace autofill
...@@ -42,6 +42,10 @@ class CardExpirationDateFixFlowControllerImpl ...@@ -42,6 +42,10 @@ class CardExpirationDateFixFlowControllerImpl
base::string16 GetTitleText() const override; base::string16 GetTitleText() const override;
base::string16 GetSaveButtonLabel() const override; base::string16 GetSaveButtonLabel() const override;
base::string16 GetCardLabel() 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: private:
// View that displays the fix flow prompt. // View that displays the fix flow prompt.
......
...@@ -195,6 +195,14 @@ ...@@ -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."> <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 Enter expiration date
</message> </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."> <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. Chrome is offering to save your cards in your Google Account because you are signed in. You can change this behavior in settings.
</message> </message>
......
...@@ -72,6 +72,8 @@ source_set("autofill") { ...@@ -72,6 +72,8 @@ source_set("autofill") {
source_set("bridges") { source_set("bridges") {
configs += [ "//build/config/compiler:enable_arc" ] configs += [ "//build/config/compiler:enable_arc" ]
sources = [ 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.h",
"card_name_fix_flow_view_bridge.mm", "card_name_fix_flow_view_bridge.mm",
"card_unmask_prompt_view_bridge.h", "card_unmask_prompt_view_bridge.h",
...@@ -104,6 +106,8 @@ source_set("autofill_ui") { ...@@ -104,6 +106,8 @@ source_set("autofill_ui") {
configs += [ "//build/config/compiler:enable_arc" ] configs += [ "//build/config/compiler:enable_arc" ]
sources = [ sources = [
"autofill_ui_type.h", "autofill_ui_type.h",
"expiration_date_picker.h",
"expiration_date_picker.mm",
"save_card_infobar_controller.h", "save_card_infobar_controller.h",
"save_card_infobar_controller.mm", "save_card_infobar_controller.mm",
"save_card_infobar_view.h", "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 { ...@@ -24,7 +24,7 @@ class CardNameFixFlowViewBridge : public CardNameFixFlowView {
UIViewController* base_view_controller); UIViewController* base_view_controller);
~CardNameFixFlowViewBridge() override; ~CardNameFixFlowViewBridge() override;
// CardUnmaskPromptView: // CardNameFixFlowView:
void Show() override; void Show() override;
void ControllerGone() override; void ControllerGone() override;
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "components/autofill/core/browser/payments/legal_message_line.h" #include "components/autofill/core/browser/payments/legal_message_line.h"
#include "components/autofill/core/browser/payments/strike_database.h" #include "components/autofill/core/browser/payments/strike_database.h"
#include "components/autofill/core/browser/personal_data_manager.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_name_fix_flow_controller_impl.h"
#include "components/autofill/core/browser/ui/payments/card_unmask_prompt_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" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
...@@ -90,6 +91,10 @@ class ChromeAutofillClientIOS : public AutofillClient { ...@@ -90,6 +91,10 @@ class ChromeAutofillClientIOS : public AutofillClient {
LocalSaveCardPromptCallback callback) override; LocalSaveCardPromptCallback callback) override;
void ConfirmAccountNameFixFlow( void ConfirmAccountNameFixFlow(
base::OnceCallback<void(const base::string16&)> callback) override; 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( void ConfirmSaveCreditCardToCloud(
const CreditCard& card, const CreditCard& card,
const LegalMessageLines& legal_message_lines, const LegalMessageLines& legal_message_lines,
...@@ -145,6 +150,8 @@ class ChromeAutofillClientIOS : public AutofillClient { ...@@ -145,6 +150,8 @@ class ChromeAutofillClientIOS : public AutofillClient {
CardUnmaskPromptControllerImpl unmask_controller_; CardUnmaskPromptControllerImpl unmask_controller_;
std::unique_ptr<LogManager> log_manager_; std::unique_ptr<LogManager> log_manager_;
CardNameFixFlowControllerImpl card_name_fix_flow_controller_; CardNameFixFlowControllerImpl card_name_fix_flow_controller_;
CardExpirationDateFixFlowControllerImpl
card_expiration_date_fix_flow_controller_;
// A weak reference to the view controller used to present UI. // A weak reference to the view controller used to present UI.
__weak UIViewController* base_view_controller_; __weak UIViewController* base_view_controller_;
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "ios/chrome/browser/signin/identity_manager_factory.h" #include "ios/chrome/browser/signin/identity_manager_factory.h"
#include "ios/chrome/browser/sync/profile_sync_service_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/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_name_fix_flow_view_bridge.h"
#include "ios/chrome/browser/ui/autofill/card_unmask_prompt_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" #include "ios/chrome/browser/ui/autofill/save_card_infobar_controller.h"
...@@ -278,6 +279,18 @@ void ChromeAutofillClientIOS::ConfirmAccountNameFixFlow( ...@@ -278,6 +279,18 @@ void ChromeAutofillClientIOS::ConfirmAccountNameFixFlow(
account_name, std::move(callback)); 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( void ChromeAutofillClientIOS::ConfirmSaveCreditCardToCloud(
const CreditCard& card, const CreditCard& card,
const LegalMessageLines& legal_message_lines, 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"; ...@@ -502,7 +502,6 @@ NSString* const kTitleViewAccessibilityIdentifier = @"titleView";
DCHECK(self.cardIssuerIcon); DCHECK(self.cardIssuerIcon);
DCHECK_GT(self.cardLabel.length, 0UL); DCHECK_GT(self.cardLabel.length, 0UL);
DCHECK_GT(self.cardSublabel.length, 0UL);
// The leading edge aligned card details container view. Contains the card // The leading edge aligned card details container view. Contains the card
// issuer network icon, the card label, and the card sublabel. // issuer network icon, the card label, and the card sublabel.
......
...@@ -82,6 +82,10 @@ class WebViewAutofillClientIOS : public AutofillClient { ...@@ -82,6 +82,10 @@ class WebViewAutofillClientIOS : public AutofillClient {
LocalSaveCardPromptCallback callback) override; LocalSaveCardPromptCallback callback) override;
void ConfirmAccountNameFixFlow( void ConfirmAccountNameFixFlow(
base::OnceCallback<void(const base::string16&)> callback) override; 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( void ConfirmSaveCreditCardToCloud(
const CreditCard& card, const CreditCard& card,
const LegalMessageLines& legal_message_lines, const LegalMessageLines& legal_message_lines,
......
...@@ -189,6 +189,13 @@ void WebViewAutofillClientIOS::ConfirmAccountNameFixFlow( ...@@ -189,6 +189,13 @@ void WebViewAutofillClientIOS::ConfirmAccountNameFixFlow(
NOTIMPLEMENTED(); NOTIMPLEMENTED();
} }
void WebViewAutofillClientIOS::ConfirmExpirationDateFixFlow(
const CreditCard& card,
base::OnceCallback<void(const base::string16&, const base::string16&)>
callback) {
NOTIMPLEMENTED();
}
void WebViewAutofillClientIOS::ConfirmSaveCreditCardToCloud( void WebViewAutofillClientIOS::ConfirmSaveCreditCardToCloud(
const CreditCard& card, const CreditCard& card,
const LegalMessageLines& legal_message_lines, 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