blob: dd43ea0c9b75b13e22d6c6ef1d58e649b9d07495 [file] [log] [blame]
// Copyright 2018 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/autofill/manual_fill/manual_fill_address_cell.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_profile.h"
#include "ios/chrome/browser/application_context.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/address_list_delegate.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"
#import "ios/chrome/common/ui_util/constraints_ui_util.h"
#include "ios/chrome/grit/ios_strings.h"
#include "ui/base/l10n/l10n_util_mac.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface ManualFillAddressItem ()
// The content delegate for this item.
@property(nonatomic, weak, readonly) id<ManualFillContentDelegate> delegate;
// The address/profile for this item.
@property(nonatomic, readonly) autofill::AutofillProfile profile;
@end
@implementation ManualFillAddressItem
- (instancetype)
initWithAutofillProfile:(const autofill::AutofillProfile&)profile
delegate:(id<ManualFillContentDelegate>)delegate {
self = [super initWithType:kItemTypeEnumZero];
if (self) {
_delegate = delegate;
_profile = profile;
self.cellClass = [ManualFillAddressCell class];
}
return self;
}
- (void)configureCell:(ManualFillAddressCell*)cell
withStyler:(ChromeTableViewStyler*)styler {
[super configureCell:cell withStyler:styler];
[cell setUpWithAutofillProfile:self.profile delegate:self.delegate];
}
@end
namespace {
// Left and right margins of the cell content.
static const CGFloat sideMargins = 16;
// Margin left and right of multiple buttons on same line.
static const CGFloat InnerMarginWidth = 16.0;
} // namespace
@interface ManualFillAddressCell ()
// The separator gray line.
@property(nonatomic, strong) UIView* grayLine;
// The label with the line1 -- line2.
@property(nonatomic, strong) UILabel* addressLabel;
// The vertical constraints for all the lines.
@property(nonatomic, strong) NSArray<NSLayoutConstraint*>* verticalConstraints;
// The constraints for the first/middle/last name line.
@property(nonatomic, strong) NSArray<NSLayoutConstraint*>* nameLineConstraints;
// The constraints for the zip/city line.
@property(nonatomic, strong)
NSArray<NSLayoutConstraint*>* zipCityLineConstraints;
// The constraints for the state/country line.
@property(nonatomic, strong)
NSArray<NSLayoutConstraint*>* stateCountryLineConstraints;
// A button showing the address associated first name.
@property(nonatomic, strong) UIButton* firstNameButton;
// The separator label between first and middle name/initial.
@property(nonatomic, strong) UILabel* middleNameSeparatorLabel;
// A button showing the address associated middle name or initial.
@property(nonatomic, strong) UIButton* middleNameButton;
// The separator label between middle name/initial and last name.
@property(nonatomic, strong) UILabel* lastNameSeparatorLabel;
// A button showing the address associated last name.
@property(nonatomic, strong) UIButton* lastNameButton;
// A button showing the address line 1.
@property(nonatomic, strong) UIButton* line1Button;
// An optional button showing the address line 2.
@property(nonatomic, strong) UIButton* line2Button;
// A button showing zip code.
@property(nonatomic, strong) UIButton* zipButton;
// The separator label between zip and city.
@property(nonatomic, strong) UILabel* citySeparatorLabel;
// A button showing city.
@property(nonatomic, strong) UIButton* cityButton;
// A button showing state/province.
@property(nonatomic, strong) UIButton* stateButton;
// The separator label between state and country.
@property(nonatomic, strong) UILabel* countrySeparatorLabel;
// A button showing country.
@property(nonatomic, strong) UIButton* countryButton;
// The content delegate for this item.
@property(nonatomic, weak) id<ManualFillContentDelegate> delegate;
// The credit address/profile data for this cell.
// TODO(crbug.com/845472): move to cocoa model.
@property(nonatomic, assign) autofill::AutofillProfile profile;
@end
@implementation ManualFillAddressCell
#pragma mark - Public
- (void)prepareForReuse {
[super prepareForReuse];
[NSLayoutConstraint deactivateConstraints:self.verticalConstraints];
self.verticalConstraints = @[];
[NSLayoutConstraint deactivateConstraints:self.nameLineConstraints];
self.nameLineConstraints = @[];
[NSLayoutConstraint deactivateConstraints:self.zipCityLineConstraints];
self.zipCityLineConstraints = @[];
[NSLayoutConstraint deactivateConstraints:self.stateCountryLineConstraints];
self.stateCountryLineConstraints = @[];
self.addressLabel.text = @"";
[self.firstNameButton setTitle:@"" forState:UIControlStateNormal];
[self.middleNameButton setTitle:@"" forState:UIControlStateNormal];
[self.lastNameButton setTitle:@"" forState:UIControlStateNormal];
[self.line1Button setTitle:@"" forState:UIControlStateNormal];
[self.line2Button setTitle:@"" forState:UIControlStateNormal];
[self.zipButton setTitle:@"" forState:UIControlStateNormal];
[self.cityButton setTitle:@"" forState:UIControlStateNormal];
[self.stateButton setTitle:@"" forState:UIControlStateNormal];
[self.countryButton setTitle:@"" forState:UIControlStateNormal];
[self.line2Button setTitle:@"" forState:UIControlStateNormal];
self.delegate = nil;
// TODO(crbug.com/845472): clear profile.
}
- (void)setUpWithAutofillProfile:(const autofill::AutofillProfile&)profile
delegate:(id<ManualFillContentDelegate>)delegate {
if (self.contentView.subviews.count == 0) {
[self createViewHierarchy];
}
self.delegate = delegate;
self.profile = profile;
NSMutableArray<UIView*>* verticalLeadViews = [[NSMutableArray alloc] init];
NSString* line1 = [self fieldValueOfType:autofill::ADDRESS_HOME_LINE1];
NSString* line2 = [self fieldValueOfType:autofill::ADDRESS_HOME_LINE2];
// Top label, summary of line 1 and 2.
NSMutableAttributedString* attributedString =
[[NSMutableAttributedString alloc]
initWithString:line1 ? line1 : @""
attributes:@{
NSForegroundColorAttributeName : UIColor.blackColor,
NSFontAttributeName :
[UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]
}];
if (line2.length) {
NSString* line2String = [NSString stringWithFormat:@" –– %@", line2];
NSDictionary* attributes = @{
NSForegroundColorAttributeName : UIColor.lightGrayColor,
NSFontAttributeName :
[UIFont preferredFontForTextStyle:UIFontTextStyleBody]
};
NSAttributedString* line2StringAttributedString =
[[NSAttributedString alloc] initWithString:line2String
attributes:attributes];
[attributedString appendAttributedString:line2StringAttributedString];
}
self.addressLabel.attributedText = attributedString;
[verticalLeadViews addObject:self.addressLabel];
// Name line, first middle and last.
NSMutableArray<UIView*>* nameLineViews = [[NSMutableArray alloc] init];
NSString* firstName = [self fieldValueOfType:autofill::NAME_FIRST];
NSString* lastName = [self fieldValueOfType:autofill::NAME_LAST];
NSString* middleName = [self fieldValueOfType:autofill::NAME_MIDDLE];
if (!middleName || middleName.length == 0) {
middleName = [self fieldValueOfType:autofill::NAME_MIDDLE_INITIAL];
}
bool showFirstName = firstName.length;
bool showMiddleName = middleName.length;
bool showLastName = lastName.length;
if (showFirstName) {
[self.firstNameButton setTitle:firstName forState:UIControlStateNormal];
[nameLineViews addObject:self.firstNameButton];
self.firstNameButton.hidden = NO;
} else {
self.firstNameButton.hidden = YES;
}
if (showFirstName && showMiddleName) {
[nameLineViews addObject:self.middleNameSeparatorLabel];
self.middleNameSeparatorLabel.hidden = NO;
} else {
self.middleNameSeparatorLabel.hidden = YES;
}
if (showMiddleName) {
[self.middleNameButton setTitle:middleName forState:UIControlStateNormal];
[nameLineViews addObject:self.middleNameButton];
self.middleNameButton.hidden = NO;
} else {
self.middleNameButton.hidden = YES;
}
if ((showFirstName || showMiddleName) && showLastName) {
[nameLineViews addObject:self.lastNameSeparatorLabel];
self.lastNameSeparatorLabel.hidden = NO;
} else {
self.lastNameSeparatorLabel.hidden = YES;
}
if (showLastName) {
[self.lastNameButton setTitle:lastName forState:UIControlStateNormal];
[nameLineViews addObject:self.lastNameButton];
self.lastNameButton.hidden = NO;
} else {
self.lastNameButton.hidden = YES;
}
self.nameLineConstraints = HorizontalConstraintsForViewsOnGuideWithShift(
nameLineViews, self.grayLine, -InnerMarginWidth);
if (nameLineViews.count) {
[verticalLeadViews addObject:nameLineViews.firstObject];
}
// Address line 1.
if (line1.length) {
[self.line1Button setTitle:line1 forState:UIControlStateNormal];
[verticalLeadViews addObject:self.line1Button];
self.line1Button.hidden = NO;
} else {
self.line1Button.hidden = YES;
}
// Address line 2.
if (line2.length) {
[self.line2Button setTitle:line2 forState:UIControlStateNormal];
[verticalLeadViews addObject:self.line2Button];
self.line2Button.hidden = NO;
} else {
self.line2Button.hidden = YES;
}
// Zip and city line.
NSMutableArray<UIView*>* zipCityLineViews = [[NSMutableArray alloc] init];
NSString* zip = [self fieldValueOfType:autofill::ADDRESS_HOME_ZIP];
NSString* city = [self fieldValueOfType:autofill::ADDRESS_HOME_CITY];
if (zip.length) {
[self.zipButton setTitle:zip forState:UIControlStateNormal];
[zipCityLineViews addObject:self.zipButton];
self.zipButton.hidden = NO;
} else {
self.zipButton.hidden = YES;
}
if (zip.length && city.length) {
[zipCityLineViews addObject:self.citySeparatorLabel];
self.citySeparatorLabel.hidden = NO;
} else {
self.citySeparatorLabel.hidden = YES;
}
if (city.length) {
[self.cityButton setTitle:city forState:UIControlStateNormal];
[zipCityLineViews addObject:self.cityButton];
self.cityButton.hidden = NO;
} else {
self.cityButton.hidden = YES;
}
self.zipCityLineConstraints = HorizontalConstraintsForViewsOnGuideWithShift(
zipCityLineViews, self.grayLine, -InnerMarginWidth);
if (zipCityLineViews.count) {
[verticalLeadViews addObject:zipCityLineViews.firstObject];
}
// State and country line.
NSMutableArray<UIView*>* stateCountryLineViews =
[[NSMutableArray alloc] init];
NSString* state = [self fieldValueOfType:autofill::ADDRESS_HOME_STATE];
NSString* country = [self fieldValueOfType:autofill::ADDRESS_HOME_COUNTRY];
if (state.length) {
[self.stateButton setTitle:state forState:UIControlStateNormal];
[stateCountryLineViews addObject:self.stateButton];
self.stateButton.hidden = NO;
} else {
self.stateButton.hidden = YES;
}
if (state.length && country.length) {
[stateCountryLineViews addObject:self.countrySeparatorLabel];
self.countrySeparatorLabel.hidden = NO;
} else {
self.countrySeparatorLabel.hidden = YES;
}
if (country.length) {
[self.countryButton setTitle:country forState:UIControlStateNormal];
[stateCountryLineViews addObject:self.countryButton];
self.countryButton.hidden = NO;
} else {
self.countryButton.hidden = YES;
}
self.stateCountryLineConstraints =
HorizontalConstraintsForViewsOnGuideWithShift(
stateCountryLineViews, self.grayLine, -InnerMarginWidth);
if (stateCountryLineViews.count) {
[verticalLeadViews addObject:stateCountryLineViews.firstObject];
}
self.verticalConstraints = VerticalConstraintsSpacingForViewsInContainer(
verticalLeadViews, self.contentView);
}
#pragma mark - Private
// Creates and sets up the view hierarchy.
- (void)createViewHierarchy {
self.selectionStyle = UITableViewCellSelectionStyleNone;
self.grayLine = [[UIView alloc] init];
self.grayLine.backgroundColor = [UIColor colorWithWhite:0.88 alpha:1];
self.grayLine.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:self.grayLine];
self.addressLabel = CreateLabel();
[self.contentView addSubview:self.addressLabel];
HorizontalConstraintsForViewsOnGuideWithShift(@[ self.addressLabel ],
self.grayLine, 0);
self.firstNameButton = CreateButtonWithSelectorAndTarget(
@selector(userDidTapAddressInfo:), self);
[self.contentView addSubview:self.firstNameButton];
HorizontalConstraintsMarginForButtonWithWidth(self.firstNameButton,
InnerMarginWidth);
self.middleNameSeparatorLabel = CreateLabel();
self.middleNameSeparatorLabel.text = @"·";
[self.contentView addSubview:self.middleNameSeparatorLabel];
self.middleNameButton = CreateButtonWithSelectorAndTarget(
@selector(userDidTapAddressInfo:), self);
[self.contentView addSubview:self.middleNameButton];
HorizontalConstraintsMarginForButtonWithWidth(self.middleNameButton,
InnerMarginWidth);
self.lastNameSeparatorLabel = CreateLabel();
self.lastNameSeparatorLabel.text = @"·";
[self.contentView addSubview:self.lastNameSeparatorLabel];
self.lastNameButton = CreateButtonWithSelectorAndTarget(
@selector(userDidTapAddressInfo:), self);
[self.contentView addSubview:self.lastNameButton];
HorizontalConstraintsMarginForButtonWithWidth(self.lastNameButton,
InnerMarginWidth);
SyncBaselinesForViewsOnView(
@[
self.middleNameSeparatorLabel, self.middleNameButton,
self.lastNameSeparatorLabel, self.lastNameButton
],
self.firstNameButton);
self.line1Button = CreateButtonWithSelectorAndTarget(
@selector(userDidTapAddressInfo:), self);
[self.contentView addSubview:self.line1Button];
HorizontalConstraintsForViewsOnGuideWithShift(@[ self.line1Button ],
self.grayLine, 0);
self.line2Button = CreateButtonWithSelectorAndTarget(
@selector(userDidTapAddressInfo:), self);
[self.contentView addSubview:self.line2Button];
HorizontalConstraintsForViewsOnGuideWithShift(@[ self.line2Button ],
self.grayLine, 0);
self.zipButton = CreateButtonWithSelectorAndTarget(
@selector(userDidTapAddressInfo:), self);
[self.contentView addSubview:self.zipButton];
HorizontalConstraintsMarginForButtonWithWidth(self.zipButton,
InnerMarginWidth);
self.citySeparatorLabel = CreateLabel();
self.citySeparatorLabel.text = @"·";
[self.contentView addSubview:self.citySeparatorLabel];
self.cityButton = CreateButtonWithSelectorAndTarget(
@selector(userDidTapAddressInfo:), self);
[self.contentView addSubview:self.cityButton];
HorizontalConstraintsMarginForButtonWithWidth(self.cityButton,
InnerMarginWidth);
SyncBaselinesForViewsOnView(@[ self.citySeparatorLabel, self.cityButton ],
self.zipButton);
self.stateButton = CreateButtonWithSelectorAndTarget(
@selector(userDidTapAddressInfo:), self);
[self.contentView addSubview:self.stateButton];
HorizontalConstraintsMarginForButtonWithWidth(self.stateButton,
InnerMarginWidth);
self.countrySeparatorLabel = CreateLabel();
self.countrySeparatorLabel.text = @"·";
[self.contentView addSubview:self.countrySeparatorLabel];
self.countryButton = CreateButtonWithSelectorAndTarget(
@selector(userDidTapAddressInfo:), self);
[self.contentView addSubview:self.countryButton];
HorizontalConstraintsMarginForButtonWithWidth(self.countryButton,
InnerMarginWidth);
SyncBaselinesForViewsOnView(
@[ self.countrySeparatorLabel, self.countryButton ], self.stateButton);
self.nameLineConstraints = @[];
self.zipCityLineConstraints = @[];
self.stateCountryLineConstraints = @[];
self.verticalConstraints = @[];
id<LayoutGuideProvider> safeArea =
SafeAreaLayoutGuideForView(self.contentView);
[NSLayoutConstraint activateConstraints:@[
// Common vertical constraints.
[self.grayLine.bottomAnchor
constraintEqualToAnchor:self.contentView.bottomAnchor],
[self.grayLine.heightAnchor constraintEqualToConstant:1],
// Horizontal constraints.
[self.grayLine.leadingAnchor constraintEqualToAnchor:safeArea.leadingAnchor
constant:sideMargins],
[safeArea.trailingAnchor
constraintEqualToAnchor:self.grayLine.trailingAnchor
constant:sideMargins],
]];
}
// Takes in an autofill profile and an autofill field type and returns the
// corresponding field value.
- (NSString*)fieldValueOfType:(autofill::ServerFieldType)fieldType {
return base::SysUTF16ToNSString(
self.profile.GetInfo(autofill::AutofillType(fieldType),
GetApplicationContext()->GetApplicationLocale()));
}
- (void)userDidTapAddressInfo:(UIButton*)sender {
[self.delegate userDidPickContent:sender.titleLabel.text isSecure:NO];
}
@end