blob: c7924f6a693fe5ef2983991e29e2a6fad439024c [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_password_cell.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/credential.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 ManualFillCredentialItem ()
// The credential for this item.
@property(nonatomic, strong, readonly) ManualFillCredential* credential;
// The delegate for this item.
@property(nonatomic, weak, readonly) id<ManualFillContentDelegate> delegate;
@end
@implementation ManualFillCredentialItem
@synthesize delegate = _delegate;
@synthesize credential = _credential;
- (instancetype)initWithCredential:(ManualFillCredential*)credential
delegate:(id<ManualFillContentDelegate>)delegate {
self = [super initWithType:kItemTypeEnumZero];
if (self) {
_credential = credential;
_delegate = delegate;
self.cellClass = [ManualFillPasswordCell class];
}
return self;
}
- (void)configureCell:(ManualFillPasswordCell*)cell
withStyler:(ChromeTableViewStyler*)styler {
[super configureCell:cell withStyler:styler];
[cell setUpWithCredential:self.credential delegate:self.delegate];
}
@end
namespace {
// Left and right margins of the cell content.
static const CGFloat sideMargins = 16;
// The base multiplier for the top and bottom margins. This number multiplied by
// the font size plus the base margins will give similar results to
// |constraintEqualToSystemSpacingBelowAnchor:| which is not available on iOS
// 10.
static const CGFloat iOS10MarginFontMultiplier = 1.18;
// The base top margin, only used in iOS 10. Refer to
// |iOS10MarginFontMultiplier| for how it is used.
static const CGFloat iOS10BaseTopMargin = 28;
// The base middle margin, only used in iOS 10. Refer to
// |iOS10MarginFontMultiplier| for how it is used.
static const CGFloat iOS10BaseMiddleMargin = 24;
// The base bottom margin, only used in iOS 10. Refer to
// |iOS10MarginFontMultiplier| for how it is used.
static const CGFloat iOS10BaseBottomMargin = 18;
// 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;
} // namespace
@interface ManualFillPasswordCell ()
// The credential this cell is showing.
@property(nonatomic, strong) ManualFillCredential* manualFillCredential;
// The label with the site name and host.
@property(nonatomic, strong) UILabel* siteNameLabel;
// A button showing the username, or "No Username".
@property(nonatomic, strong) UIButton* usernameButton;
// A button showing "••••••••" to resemble a password.
@property(nonatomic, strong) UIButton* passwordButton;
// The delegate in charge of processing the user actions in this cell.
@property(nonatomic, weak) id<ManualFillContentDelegate> delegate;
@end
@implementation ManualFillPasswordCell
@synthesize manualFillCredential = _manualFillCredential;
@synthesize siteNameLabel = _siteNameLabel;
@synthesize usernameButton = _usernameButton;
@synthesize passwordButton = _passwordButton;
@synthesize delegate = _delegate;
#pragma mark - Public
- (void)prepareForReuse {
[super prepareForReuse];
self.siteNameLabel.text = @"";
[self.usernameButton setTitle:@"" forState:UIControlStateNormal];
self.usernameButton.enabled = YES;
[self.passwordButton setTitle:@"" forState:UIControlStateNormal];
self.manualFillCredential = nil;
}
- (void)setUpWithCredential:(ManualFillCredential*)credential
delegate:(id<ManualFillContentDelegate>)delegate {
if (self.contentView.subviews.count == 0) {
[self createViewHierarchy];
}
self.delegate = delegate;
self.manualFillCredential = credential;
NSMutableAttributedString* attributedString =
[[NSMutableAttributedString alloc]
initWithString:credential.siteName ? credential.siteName : @""
attributes:@{
NSForegroundColorAttributeName : UIColor.blackColor,
NSFontAttributeName :
[UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]
}];
if (credential.host && credential.host.length &&
![credential.host isEqualToString:credential.siteName]) {
NSString* hostString =
[NSString stringWithFormat:@" –– %@", credential.host];
NSDictionary* attributes = @{
NSForegroundColorAttributeName : UIColor.lightGrayColor,
NSFontAttributeName :
[UIFont preferredFontForTextStyle:UIFontTextStyleBody]
};
NSAttributedString* hostAttributedString =
[[NSAttributedString alloc] initWithString:hostString
attributes:attributes];
[attributedString appendAttributedString:hostAttributedString];
}
self.siteNameLabel.attributedText = attributedString;
if (credential.username.length) {
[self.usernameButton setTitle:credential.username
forState:UIControlStateNormal];
} else {
NSString* titleString =
l10n_util::GetNSString(IDS_IOS_MANUAL_FALLBACK_NO_USERNAME);
[self.usernameButton setTitle:titleString forState:UIControlStateNormal];
self.usernameButton.enabled = NO;
}
if (credential.password.length) {
[self.passwordButton setTitle:@"••••••••" forState:UIControlStateNormal];
}
}
#pragma mark - Private
// Creates and sets up the view hierarchy.
- (void)createViewHierarchy {
self.selectionStyle = UITableViewCellSelectionStyleNone;
UIView* grayLine = [[UIView alloc] init];
grayLine.backgroundColor = UIColor.cr_manualFillGrayLineColor;
grayLine.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:grayLine];
self.siteNameLabel = [[UILabel alloc] init];
self.siteNameLabel.translatesAutoresizingMaskIntoConstraints = NO;
self.siteNameLabel.adjustsFontForContentSizeCategory = YES;
[self.contentView addSubview:self.siteNameLabel];
self.usernameButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.usernameButton setTitleColor:UIColor.cr_manualFillTintColor
forState:UIControlStateNormal];
self.usernameButton.translatesAutoresizingMaskIntoConstraints = NO;
self.usernameButton.titleLabel.font =
[UIFont preferredFontForTextStyle:UIFontTextStyleBody];
self.usernameButton.titleLabel.adjustsFontForContentSizeCategory = YES;
[self.usernameButton addTarget:self
action:@selector(userDidTapUsernameButton:)
forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:self.usernameButton];
self.passwordButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.passwordButton setTitleColor:UIColor.cr_manualFillTintColor
forState:UIControlStateNormal];
self.passwordButton.translatesAutoresizingMaskIntoConstraints = NO;
self.passwordButton.titleLabel.font =
[UIFont preferredFontForTextStyle:UIFontTextStyleBody];
self.passwordButton.titleLabel.adjustsFontForContentSizeCategory = YES;
[self.passwordButton addTarget:self
action:@selector(userDidTapPasswordButton:)
forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:self.passwordButton];
id<LayoutGuideProvider> safeArea =
SafeAreaLayoutGuideForView(self.contentView);
NSArray* verticalConstraints;
if (@available(iOS 11, *)) {
// Multipliers of these constraints are calculated based on a 24 base
// system spacing.
verticalConstraints = @[
[self.siteNameLabel.firstBaselineAnchor
constraintEqualToSystemSpacingBelowAnchor:self.contentView.topAnchor
multiplier:TopSystemSpacingMultiplier],
[self.usernameButton.firstBaselineAnchor
constraintEqualToSystemSpacingBelowAnchor:self.siteNameLabel
.lastBaselineAnchor
multiplier:
MiddleSystemSpacingMultiplier],
[self.passwordButton.firstBaselineAnchor
constraintEqualToSystemSpacingBelowAnchor:self.usernameButton
.lastBaselineAnchor
multiplier:
MiddleSystemSpacingMultiplier],
[self.contentView.bottomAnchor
constraintEqualToSystemSpacingBelowAnchor:self.passwordButton
.lastBaselineAnchor
multiplier:
BottomSystemSpacingMultiplier],
];
} else {
CGFloat pointSize = self.usernameButton.titleLabel.font.pointSize;
// These margins are based on the design size and the current point size.
// The multipliers were selected by manually testing the different system
// font sizes.
CGFloat marginBetweenButtons =
iOS10BaseMiddleMargin + pointSize * iOS10MarginFontMultiplier;
CGFloat marginBottom =
iOS10BaseBottomMargin + pointSize * iOS10MarginFontMultiplier / 2;
CGFloat marginTop =
iOS10BaseTopMargin + pointSize * iOS10MarginFontMultiplier / 2;
verticalConstraints = @[
// This doesn't make sense when the label is to big.
[self.siteNameLabel.firstBaselineAnchor
constraintEqualToAnchor:self.contentView.topAnchor
constant:marginTop],
[self.usernameButton.firstBaselineAnchor
constraintEqualToAnchor:self.siteNameLabel.lastBaselineAnchor
constant:marginBetweenButtons],
[self.passwordButton.firstBaselineAnchor
constraintEqualToAnchor:self.usernameButton.lastBaselineAnchor
constant:marginBetweenButtons],
[self.contentView.bottomAnchor
constraintEqualToAnchor:self.passwordButton.lastBaselineAnchor
constant:marginBottom],
];
}
[NSLayoutConstraint activateConstraints:verticalConstraints];
[NSLayoutConstraint activateConstraints:@[
// Common vertical constraints.
[grayLine.bottomAnchor
constraintEqualToAnchor:self.contentView.bottomAnchor],
[grayLine.heightAnchor constraintEqualToConstant:1],
// Horizontal constraints.
[grayLine.leadingAnchor constraintEqualToAnchor:safeArea.leadingAnchor
constant:sideMargins],
[safeArea.trailingAnchor constraintEqualToAnchor:grayLine.trailingAnchor
constant:sideMargins],
[self.siteNameLabel.leadingAnchor
constraintEqualToAnchor:grayLine.leadingAnchor],
[self.siteNameLabel.trailingAnchor
constraintEqualToAnchor:grayLine.trailingAnchor],
[self.usernameButton.leadingAnchor
constraintEqualToAnchor:grayLine.leadingAnchor],
[self.usernameButton.trailingAnchor
constraintLessThanOrEqualToAnchor:grayLine.trailingAnchor],
[self.passwordButton.leadingAnchor
constraintEqualToAnchor:grayLine.leadingAnchor],
[self.passwordButton.trailingAnchor
constraintLessThanOrEqualToAnchor:grayLine.trailingAnchor],
]];
}
- (void)userDidTapUsernameButton:(UIButton*)button {
[self.delegate userDidPickContent:self.manualFillCredential.username
isSecure:NO];
}
- (void)userDidTapPasswordButton:(UIButton*)button {
[self.delegate userDidPickContent:self.manualFillCredential.password
isSecure:YES];
}
@end