Commit b8f08357 authored by David Jean's avatar David Jean Committed by Commit Bot

[ios] combine password cells for manual fallback

Also gray out "No Username" label.

Bug: 905663
Change-Id: Iabbbf1ce7d20f62e72896ad08b5a239d0fbd14dc
Reviewed-on: https://chromium-review.googlesource.com/c/1350616
Commit-Queue: David Jean <djean@chromium.org>
Reviewed-by: default avatarJavier Ernesto Flores Robles <javierrobles@chromium.org>
Cr-Commit-Position: refs/heads/master@{#611091}
parent d70ca123
...@@ -7,17 +7,48 @@ ...@@ -7,17 +7,48 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
namespace {
// 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;
// Top and bottom margins for buttons.
static const CGFloat ButtonVerticalMargin = 12;
// Left and right margins of the cell content and buttons.
static const CGFloat ButtonHorizontalMargin = 16;
} // namespace
// Creates a blank button in fallback style, for the given |action| and // Creates a blank button in fallback style, for the given |action| and
// |target|. // |target|.
UIButton* CreateButtonWithSelectorAndTarget(SEL action, id target); UIButton* CreateButtonWithSelectorAndTarget(SEL action, id target);
// Sets vertical constraints on firstBaselineAnchor for the button or label rows // Sets vertical constraints on firstBaselineAnchor for the button or label rows
// in |views| inside |container| starting at its topAnchor. Returns the applied // in |views| inside |container| starting at its topAnchor. Returns the applied
// constrainst to allow caller to deactivate them later. // constrainst to allow caller to deactivate them later. Defaults multipliers
// are applied.
NSArray<NSLayoutConstraint*>* VerticalConstraintsSpacingForViewsInContainer( NSArray<NSLayoutConstraint*>* VerticalConstraintsSpacingForViewsInContainer(
NSArray<UIView*>* views, NSArray<UIView*>* views,
UIView* container); UIView* container);
// Sets vertical constraints on firstBaselineAnchor for the button or label rows
// in |views| inside |container| starting at its topAnchor. Returns the applied
// constrainst to allow caller to deactivate them later.
NSArray<NSLayoutConstraint*>*
VerticalConstraintsSpacingForViewsInContainerWithMultipliers(
NSArray<UIView*>* views,
UIView* container,
CGFloat topSystemSpacingMultiplier,
CGFloat middleSystemSpacingMultiplier,
CGFloat BottomSystemSpacingMultiplier);
// Sets constraints for the given |views|, so as to lay them out horizontally, // Sets constraints for the given |views|, so as to lay them out horizontally,
// parallel to the given |guide| view, and applying the given constant |shift| // parallel to the given |guide| view, and applying the given constant |shift|
// to the whole row. Returns the applied constraints to allow caller to // to the whole row. Returns the applied constraints to allow caller to
......
...@@ -16,25 +16,6 @@ ...@@ -16,25 +16,6 @@
#error "This file requires ARC support." #error "This file requires ARC support."
#endif #endif
namespace {
// 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;
// Top and bottom margins for buttons.
static const CGFloat ButtonVerticalMargin = 12;
// Left and right margins of the cell content and buttons.
static const CGFloat ButtonHorizontalMargin = 16;
} // namespace
UIButton* CreateButtonWithSelectorAndTarget(SEL action, id target) { UIButton* CreateButtonWithSelectorAndTarget(SEL action, id target) {
UIButton* button = [ManualFillCellButton buttonWithType:UIButtonTypeCustom]; UIButton* button = [ManualFillCellButton buttonWithType:UIButtonTypeCustom];
[button setTitleColor:UIColor.cr_manualFillTintColor [button setTitleColor:UIColor.cr_manualFillTintColor
...@@ -57,21 +38,33 @@ UIButton* CreateButtonWithSelectorAndTarget(SEL action, id target) { ...@@ -57,21 +38,33 @@ UIButton* CreateButtonWithSelectorAndTarget(SEL action, id target) {
NSArray<NSLayoutConstraint*>* VerticalConstraintsSpacingForViewsInContainer( NSArray<NSLayoutConstraint*>* VerticalConstraintsSpacingForViewsInContainer(
NSArray<UIView*>* views, NSArray<UIView*>* views,
UIView* container) { UIView* container) {
return VerticalConstraintsSpacingForViewsInContainerWithMultipliers(
views, container, TopSystemSpacingMultiplier,
MiddleSystemSpacingMultiplier, BottomSystemSpacingMultiplier);
}
NSArray<NSLayoutConstraint*>*
VerticalConstraintsSpacingForViewsInContainerWithMultipliers(
NSArray<UIView*>* views,
UIView* container,
CGFloat topSystemSpacingMultiplier,
CGFloat middleSystemSpacingMultiplier,
CGFloat bottomSystemSpacingMultiplier) {
NSMutableArray* verticalConstraints = [[NSMutableArray alloc] init]; NSMutableArray* verticalConstraints = [[NSMutableArray alloc] init];
// Multipliers of these constraints are calculated based on a 24 base // Multipliers of these constraints are calculated based on a 24 base
// system spacing. // system spacing.
NSLayoutYAxisAnchor* previousAnchor = container.topAnchor; NSLayoutYAxisAnchor* previousAnchor = container.topAnchor;
CGFloat multiplier = TopSystemSpacingMultiplier; CGFloat multiplier = topSystemSpacingMultiplier;
for (UIView* view in views) { for (UIView* view in views) {
[verticalConstraints [verticalConstraints
addObject:[view.firstBaselineAnchor addObject:[view.firstBaselineAnchor
constraintEqualToSystemSpacingBelowAnchor:previousAnchor constraintEqualToSystemSpacingBelowAnchor:previousAnchor
multiplier:multiplier]]; multiplier:multiplier]];
multiplier = MiddleSystemSpacingMultiplier; multiplier = middleSystemSpacingMultiplier;
previousAnchor = view.lastBaselineAnchor; previousAnchor = view.lastBaselineAnchor;
} }
multiplier = BottomSystemSpacingMultiplier; multiplier = bottomSystemSpacingMultiplier;
[verticalConstraints [verticalConstraints
addObject:[container.bottomAnchor addObject:[container.bottomAnchor
constraintEqualToSystemSpacingBelowAnchor:previousAnchor constraintEqualToSystemSpacingBelowAnchor:previousAnchor
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
@interface ManualFillCredentialItem : TableViewItem @interface ManualFillCredentialItem : TableViewItem
- (instancetype)initWithCredential:(ManualFillCredential*)credential - (instancetype)initWithCredential:(ManualFillCredential*)credential
isConnectedToPreviousItem:(BOOL)isConnectedToPreviousItem
isConnectedToNextItem:(BOOL)isConnectedToNextItem
delegate:(id<ManualFillContentDelegate>)delegate delegate:(id<ManualFillContentDelegate>)delegate
NS_DESIGNATED_INITIALIZER; NS_DESIGNATED_INITIALIZER;
...@@ -30,7 +32,9 @@ ...@@ -30,7 +32,9 @@
// Updates the cell with the |credential|. If the user iteracts with it, the // Updates the cell with the |credential|. If the user iteracts with it, the
// |delegate| will be notified. // |delegate| will be notified.
- (void)setUpWithCredential:(ManualFillCredential*)credential - (void)setUpWithCredential:(ManualFillCredential*)credential
delegate:(id<ManualFillContentDelegate>)delegate; isConnectedToPreviousCell:(BOOL)isConnectedToPreviousCell
isConnectedToNextCell:(BOOL)isConnectedToNextCell
delegate:(id<ManualFillContentDelegate>)delegate;
@end @end
......
...@@ -19,10 +19,20 @@ ...@@ -19,10 +19,20 @@
#endif #endif
@interface ManualFillCredentialItem () @interface ManualFillCredentialItem ()
// The credential for this item. // The credential for this item.
@property(nonatomic, strong, readonly) ManualFillCredential* credential; @property(nonatomic, strong, readonly) ManualFillCredential* credential;
// The cell won't show a title (site name) label if it is connected to the
// previous password item.
@property(nonatomic, assign) BOOL isConnectedToPreviousItem;
// The separator line won't show if it is connected to the next password item.
@property(nonatomic, assign) BOOL isConnectedToNextItem;
// The delegate for this item. // The delegate for this item.
@property(nonatomic, weak, readonly) id<ManualFillContentDelegate> delegate; @property(nonatomic, weak, readonly) id<ManualFillContentDelegate> delegate;
@end @end
@implementation ManualFillCredentialItem @implementation ManualFillCredentialItem
...@@ -30,10 +40,14 @@ ...@@ -30,10 +40,14 @@
@synthesize credential = _credential; @synthesize credential = _credential;
- (instancetype)initWithCredential:(ManualFillCredential*)credential - (instancetype)initWithCredential:(ManualFillCredential*)credential
isConnectedToPreviousItem:(BOOL)isConnectedToPreviousItem
isConnectedToNextItem:(BOOL)isConnectedToNextItem
delegate:(id<ManualFillContentDelegate>)delegate { delegate:(id<ManualFillContentDelegate>)delegate {
self = [super initWithType:kItemTypeEnumZero]; self = [super initWithType:kItemTypeEnumZero];
if (self) { if (self) {
_credential = credential; _credential = credential;
_isConnectedToPreviousItem = isConnectedToPreviousItem;
_isConnectedToNextItem = isConnectedToNextItem;
_delegate = delegate; _delegate = delegate;
self.cellClass = [ManualFillPasswordCell class]; self.cellClass = [ManualFillPasswordCell class];
} }
...@@ -43,37 +57,55 @@ ...@@ -43,37 +57,55 @@
- (void)configureCell:(ManualFillPasswordCell*)cell - (void)configureCell:(ManualFillPasswordCell*)cell
withStyler:(ChromeTableViewStyler*)styler { withStyler:(ChromeTableViewStyler*)styler {
[super configureCell:cell withStyler:styler]; [super configureCell:cell withStyler:styler];
[cell setUpWithCredential:self.credential delegate:self.delegate]; [cell setUpWithCredential:self.credential
isConnectedToPreviousCell:self.isConnectedToPreviousItem
isConnectedToNextCell:self.isConnectedToNextItem
delegate:self.delegate];
} }
@end @end
namespace { namespace {
// Left and right margins of the cell content. // Left and right margins of the cell content.
static const CGFloat SideMargins = 16; static const CGFloat SideMargins = 16;
// The multiplier for the base system spacing at the top margin for connected
// cells.
static const CGFloat TopSystemSpacingMultiplierForConnectedCell = 1.28;
// When no extra multiplier is required.
static const CGFloat NoMultiplier = 1.0;
} // namespace } // namespace
@interface ManualFillPasswordCell () @interface ManualFillPasswordCell ()
// The credential this cell is showing. // The credential this cell is showing.
@property(nonatomic, strong) ManualFillCredential* manualFillCredential; @property(nonatomic, strong) ManualFillCredential* manualFillCredential;
// The vertical constraints for all the lines.
@property(nonatomic, strong) NSArray<NSLayoutConstraint*>* verticalConstraints;
// The label with the site name and host. // The label with the site name and host.
@property(nonatomic, strong) UILabel* siteNameLabel; @property(nonatomic, strong) UILabel* siteNameLabel;
// A button showing the username, or "No Username". // A button showing the username, or "No Username".
@property(nonatomic, strong) UIButton* usernameButton; @property(nonatomic, strong) UIButton* usernameButton;
// A button showing "••••••••" to resemble a password. // A button showing "••••••••" to resemble a password.
@property(nonatomic, strong) UIButton* passwordButton; @property(nonatomic, strong) UIButton* passwordButton;
// Separator line between cells, if needed.
@property(nonatomic, strong) UIView* grayLine;
// The delegate in charge of processing the user actions in this cell. // The delegate in charge of processing the user actions in this cell.
@property(nonatomic, weak) id<ManualFillContentDelegate> delegate; @property(nonatomic, weak) id<ManualFillContentDelegate> delegate;
@end @end
@implementation ManualFillPasswordCell @implementation ManualFillPasswordCell
@synthesize manualFillCredential = _manualFillCredential;
@synthesize siteNameLabel = _siteNameLabel;
@synthesize usernameButton = _usernameButton;
@synthesize passwordButton = _passwordButton;
@synthesize delegate = _delegate;
#pragma mark - Public #pragma mark - Public
- (void)prepareForReuse { - (void)prepareForReuse {
...@@ -86,36 +118,47 @@ static const CGFloat SideMargins = 16; ...@@ -86,36 +118,47 @@ static const CGFloat SideMargins = 16;
} }
- (void)setUpWithCredential:(ManualFillCredential*)credential - (void)setUpWithCredential:(ManualFillCredential*)credential
delegate:(id<ManualFillContentDelegate>)delegate { isConnectedToPreviousCell:(BOOL)isConnectedToPreviousCell
isConnectedToNextCell:(BOOL)isConnectedToNextCell
delegate:(id<ManualFillContentDelegate>)delegate {
if (self.contentView.subviews.count == 0) { if (self.contentView.subviews.count == 0) {
[self createViewHierarchy]; [self createViewHierarchy];
} }
self.delegate = delegate; self.delegate = delegate;
self.manualFillCredential = credential; self.manualFillCredential = credential;
NSMutableAttributedString* attributedString =
[[NSMutableAttributedString alloc] NSMutableArray<UIView*>* verticalLeadViews = [[NSMutableArray alloc] init];
initWithString:credential.siteName ? credential.siteName : @""
attributes:@{ if (isConnectedToPreviousCell) {
NSForegroundColorAttributeName : UIColor.blackColor, self.siteNameLabel.hidden = YES;
NSFontAttributeName : } else {
[UIFont preferredFontForTextStyle:UIFontTextStyleHeadline] NSMutableAttributedString* attributedString =
}]; [[NSMutableAttributedString alloc]
if (credential.host && credential.host.length && initWithString:credential.siteName ? credential.siteName : @""
![credential.host isEqualToString:credential.siteName]) { attributes:@{
NSString* hostString = NSForegroundColorAttributeName : UIColor.blackColor,
[NSString stringWithFormat:@" –– %@", credential.host]; NSFontAttributeName :
NSDictionary* attributes = @{ [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]
NSForegroundColorAttributeName : UIColor.lightGrayColor, }];
NSFontAttributeName : if (credential.host && credential.host.length &&
[UIFont preferredFontForTextStyle:UIFontTextStyleBody] ![credential.host isEqualToString:credential.siteName]) {
}; NSString* hostString =
NSAttributedString* hostAttributedString = [NSString stringWithFormat:@" –– %@", credential.host];
[[NSAttributedString alloc] initWithString:hostString NSDictionary* attributes = @{
attributes:attributes]; NSForegroundColorAttributeName : UIColor.lightGrayColor,
[attributedString appendAttributedString:hostAttributedString]; NSFontAttributeName :
[UIFont preferredFontForTextStyle:UIFontTextStyleBody]
};
NSAttributedString* hostAttributedString =
[[NSAttributedString alloc] initWithString:hostString
attributes:attributes];
[attributedString appendAttributedString:hostAttributedString];
}
self.siteNameLabel.attributedText = attributedString;
[verticalLeadViews addObject:self.siteNameLabel];
self.siteNameLabel.hidden = NO;
} }
self.siteNameLabel.attributedText = attributedString;
if (credential.username.length) { if (credential.username.length) {
[self.usernameButton setTitle:credential.username [self.usernameButton setTitle:credential.username
forState:UIControlStateNormal]; forState:UIControlStateNormal];
...@@ -123,12 +166,37 @@ static const CGFloat SideMargins = 16; ...@@ -123,12 +166,37 @@ static const CGFloat SideMargins = 16;
NSString* titleString = NSString* titleString =
l10n_util::GetNSString(IDS_IOS_MANUAL_FALLBACK_NO_USERNAME); l10n_util::GetNSString(IDS_IOS_MANUAL_FALLBACK_NO_USERNAME);
[self.usernameButton setTitle:titleString forState:UIControlStateNormal]; [self.usernameButton setTitle:titleString forState:UIControlStateNormal];
[self.usernameButton setTitleColor:UIColor.lightGrayColor
forState:UIControlStateNormal];
self.usernameButton.enabled = NO; self.usernameButton.enabled = NO;
} }
[verticalLeadViews addObject:self.usernameButton];
if (credential.password.length) { if (credential.password.length) {
[self.passwordButton setTitle:@"••••••••" forState:UIControlStateNormal]; [self.passwordButton setTitle:@"••••••••" forState:UIControlStateNormal];
[verticalLeadViews addObject:self.passwordButton];
self.passwordButton.hidden = NO;
} else {
self.passwordButton.hidden = YES;
} }
if (!isConnectedToNextCell) {
[verticalLeadViews addObject:self.grayLine];
self.grayLine.hidden = NO;
} else {
self.grayLine.hidden = YES;
}
CGFloat topSystemSpacingMultiplier =
isConnectedToPreviousCell ? TopSystemSpacingMultiplierForConnectedCell
: TopSystemSpacingMultiplier;
CGFloat bottomSystemSpacingMultiplier =
isConnectedToNextCell ? NoMultiplier : BottomSystemSpacingMultiplier;
self.verticalConstraints =
VerticalConstraintsSpacingForViewsInContainerWithMultipliers(
verticalLeadViews, self.contentView, topSystemSpacingMultiplier,
MiddleSystemSpacingMultiplier, bottomSystemSpacingMultiplier);
} }
#pragma mark - Private #pragma mark - Private
...@@ -137,10 +205,10 @@ static const CGFloat SideMargins = 16; ...@@ -137,10 +205,10 @@ static const CGFloat SideMargins = 16;
- (void)createViewHierarchy { - (void)createViewHierarchy {
self.selectionStyle = UITableViewCellSelectionStyleNone; self.selectionStyle = UITableViewCellSelectionStyleNone;
UIView* grayLine = [[UIView alloc] init]; self.grayLine = [[UIView alloc] init];
grayLine.backgroundColor = UIColor.cr_manualFillGrayLineColor; self.grayLine.backgroundColor = UIColor.cr_manualFillGrayLineColor;
grayLine.translatesAutoresizingMaskIntoConstraints = NO; self.grayLine.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:grayLine]; [self.contentView addSubview:self.grayLine];
UIView* guide = self.contentView; UIView* guide = self.contentView;
...@@ -163,23 +231,19 @@ static const CGFloat SideMargins = 16; ...@@ -163,23 +231,19 @@ static const CGFloat SideMargins = 16;
HorizontalConstraintsForViewsOnGuideWithShift(@[ self.passwordButton ], guide, HorizontalConstraintsForViewsOnGuideWithShift(@[ self.passwordButton ], guide,
0); 0);
VerticalConstraintsSpacingForViewsInContainer(
@[ self.siteNameLabel, self.usernameButton, self.passwordButton ],
self.contentView);
id<LayoutGuideProvider> safeArea = self.contentView.safeAreaLayoutGuide; id<LayoutGuideProvider> safeArea = self.contentView.safeAreaLayoutGuide;
[NSLayoutConstraint activateConstraints:@[ [NSLayoutConstraint activateConstraints:@[
// Common vertical constraints. // Common vertical constraints.
[grayLine.bottomAnchor [self.grayLine.bottomAnchor
constraintEqualToAnchor:self.contentView.bottomAnchor], constraintEqualToAnchor:self.contentView.bottomAnchor],
[grayLine.heightAnchor constraintEqualToConstant:1], [self.grayLine.heightAnchor constraintEqualToConstant:1],
// Horizontal constraints. // Horizontal constraints.
[grayLine.leadingAnchor constraintEqualToAnchor:safeArea.leadingAnchor [self.grayLine.leadingAnchor constraintEqualToAnchor:safeArea.leadingAnchor
constant:SideMargins], constant:SideMargins],
[safeArea.trailingAnchor constraintEqualToAnchor:grayLine.trailingAnchor [safeArea.trailingAnchor
constant:SideMargins], constraintEqualToAnchor:self.grayLine.trailingAnchor
constant:SideMargins],
]]; ]];
} }
......
...@@ -39,6 +39,19 @@ NSString* const OtherPasswordsAccessibilityIdentifier = ...@@ -39,6 +39,19 @@ NSString* const OtherPasswordsAccessibilityIdentifier =
} // namespace manual_fill } // namespace manual_fill
// Checks if two credential are connected. They are considered connected if they
// have same host.
BOOL AreCredentialsAtIndexesConnected(
NSArray<ManualFillCredential*>* credentials,
int firstIndex,
int secondIndex) {
if (firstIndex < 0 || firstIndex >= (int)credentials.count ||
secondIndex < 0 || secondIndex >= (int)credentials.count)
return NO;
return [credentials[firstIndex].host
isEqualToString:credentials[secondIndex].host];
}
@interface ManualFillPasswordMediator ()<ManualFillContentDelegate, @interface ManualFillPasswordMediator ()<ManualFillContentDelegate,
PasswordFetcherDelegate> PasswordFetcherDelegate>
// The |WebStateList| containing the active web state. Used to filter the list // The |WebStateList| containing the active web state. Used to filter the list
...@@ -167,9 +180,17 @@ NSString* const OtherPasswordsAccessibilityIdentifier = ...@@ -167,9 +180,17 @@ NSString* const OtherPasswordsAccessibilityIdentifier =
(NSArray<ManualFillCredential*>*)credentials { (NSArray<ManualFillCredential*>*)credentials {
NSMutableArray* items = NSMutableArray* items =
[[NSMutableArray alloc] initWithCapacity:credentials.count]; [[NSMutableArray alloc] initWithCapacity:credentials.count];
for (ManualFillCredential* credential in credentials) { for (int i = 0; i < (int)credentials.count; i++) {
auto item = [[ManualFillCredentialItem alloc] initWithCredential:credential BOOL isConnectedToPreviousItem =
delegate:self]; AreCredentialsAtIndexesConnected(credentials, i, i - 1);
BOOL isConnectedToNextItem =
AreCredentialsAtIndexesConnected(credentials, i, i + 1);
ManualFillCredential* credential = credentials[i];
auto item = [[ManualFillCredentialItem alloc]
initWithCredential:credential
isConnectedToPreviousItem:isConnectedToPreviousItem
isConnectedToNextItem:isConnectedToNextItem
delegate:self];
[items addObject:item]; [items addObject:item];
} }
return items; return items;
......
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