Skip to content

Commit

Permalink
[iOS] Password Checkup homepage header setup
Browse files Browse the repository at this point in the history
These changes set up the header image of the new Password Checkup
homepage according to the PasswordCheckupHomepageState and the
password count for every insecure type. The header image should be
hidden when on an iPhone in landscape mode.

Mocks:
https://www.figma.com/file/0ZWgfn1GhpwSLfCbqMWRjt/Bling-Password-Checkup?node-id=652%3A107555&t=hUw3UPBIIY4zpfjc-4

Bug: 1406540
Change-Id: I7865b7087a8020e132d33295f1d99339048d8db4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4282638
Commit-Queue: Noémie St-Onge <noemies@google.com>
Reviewed-by: Ernesto Izquierdo Clua <eic@google.com>
Cr-Commit-Position: refs/heads/main@{#1114804}
  • Loading branch information
Noémie St-Onge authored and Chromium LUCI CQ committed Mar 8, 2023
1 parent 4178ff7 commit 7c9b3bd
Show file tree
Hide file tree
Showing 26 changed files with 565 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,13 @@ source_set("password_checkup_ui") {
deps = [
":password_checkup_utils",
"resources:password_checkup_header_background_color",
"resources:password_checkup_header_green",
"resources:password_checkup_header_loading",
"resources:password_checkup_header_red",
"resources:password_checkup_header_yellow",
"//base",
"//ios/chrome/app/strings",
"//ios/chrome/browser/shared/ui/util",
"//ios/chrome/browser/ui/settings:settings_root",
"//ui/base",
]
Expand Down Expand Up @@ -83,8 +88,11 @@ source_set("unit_tests") {
"//components/password_manager/core/common:features",
"//components/prefs:test_support",
"//ios/chrome/browser/browser_state:test_support",
"//ios/chrome/browser/main:test_support",
"//ios/chrome/browser/passwords",
"//ios/chrome/browser/passwords:store_factory",
"//ios/chrome/browser/ui/table_view:test_support",
"//ios/chrome/browser/ui/table_view:utils",
"//ios/web/public/test",
"//testing/gtest",
"//third_party/ocmock",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,16 @@ enum class WarningType {
// Struct used to obtain the password counts associated with the different
// insecure types.
struct InsecurePasswordCounts {
int compromisedCount;
int dismissedCount;
int reusedCount;
int weakCount;
int compromised_count;
int dismissed_count;
int reused_count;
int weak_count;
};

// Operator overload for the InsecurePasswordCounts struct.
bool operator==(const InsecurePasswordCounts& lhs,
const InsecurePasswordCounts& rhs);

// Returns the type of warning with the highest priority, the descending order
// of priority being:
// 1. Compromised password warnings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@

namespace password_manager {

bool operator==(const InsecurePasswordCounts& lhs,
const InsecurePasswordCounts& rhs) {
std::tuple lhs_tuple = std::tie(lhs.compromised_count, lhs.dismissed_count,
lhs.reused_count, lhs.weak_count);
std::tuple rhs_tuple = std::tie(rhs.compromised_count, rhs.dismissed_count,
rhs.reused_count, rhs.weak_count);
return lhs_tuple == rhs_tuple;
}

WarningType GetWarningOfHighestPriority(
const std::vector<password_manager::CredentialUIEntry>&
insecure_credentials) {
Expand Down Expand Up @@ -64,15 +73,15 @@ InsecurePasswordCounts CountInsecurePasswordsPerInsecureType(
// If a compromised credential is muted, we don't want to take it into
// account in the compromised count.
if (credential.IsMuted()) {
counts.dismissedCount++;
counts.dismissed_count++;
} else if (IsCompromised(credential)) {
counts.compromisedCount++;
counts.compromised_count++;
}
if (credential.IsReused()) {
counts.reusedCount++;
counts.reused_count++;
}
if (credential.IsWeak()) {
counts.weakCount++;
counts.weak_count++;
}
}
return counts;
Expand All @@ -86,13 +95,13 @@ int GetPasswordCountForWarningType(
CountInsecurePasswordsPerInsecureType(insecure_credentials);
switch (warningType) {
case WarningType::kCompromisedPasswordsWarning:
return counts.compromisedCount;
return counts.compromised_count;
case WarningType::kReusedPasswordsWarning:
return counts.reusedCount;
return counts.reused_count;
case WarningType::kWeakPasswordsWarning:
return counts.weakCount;
return counts.weak_count;
case WarningType::kDismissedWarningsWarning:
return counts.dismissedCount;
return counts.dismissed_count;
case WarningType::kNoInsecurePasswordsWarning:
return 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#import "ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_view_controller.h"

#import "base/metrics/user_metrics.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
#import "ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_commands.h"
#import "ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_consumer.h"
#import "ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_utils.h"
Expand All @@ -15,10 +16,54 @@
#error "This file requires ARC support."
#endif

using password_manager::InsecurePasswordCounts;

namespace {

// Height of the image used as a header for the table view.
CGFloat const kHeaderImageHeight = 99;

// Helper method to get the right trailing image for the Password Check cell
// depending on the check state.
UIImage* GetHeaderImage(PasswordCheckupHomepageState password_checkup_state,
InsecurePasswordCounts counts) {
bool has_compromised_passwords = counts.compromised_count > 0;
bool has_insecure_passwords =
counts.compromised_count > 0 || counts.dismissed_count > 0 ||
counts.reused_count > 0 || counts.weak_count > 0;
switch (password_checkup_state) {
case PasswordCheckupHomepageStateDone:
if (has_compromised_passwords) {
return [UIImage imageNamed:@"password_checkup_header_red"];
} else if (has_insecure_passwords) {
return [UIImage imageNamed:@"password_checkup_header_yellow"];
}
return [UIImage imageNamed:@"password_checkup_header_green"];
case PasswordCheckupHomepageStateRunning:
return [UIImage imageNamed:@"password_checkup_header_loading"];
case PasswordCheckupHomepageStateError:
case PasswordCheckupHomepageStateDisabled:
return nil;
}
}

} // namespace

@interface PasswordCheckupViewController () {
// Whether Settings have been dismissed.
BOOL _settingsAreDismissed;

// Current PasswordCheckupHomepageState.
PasswordCheckupHomepageState _passwordCheckupState;

// Password counts associated with the different insecure types.
InsecurePasswordCounts _insecurePasswordCounts;

// Image view at the top of the screen, indicating the overall Password
// Checkup status.
UIImageView* _headerImageView;
}

@end

@implementation PasswordCheckupViewController
Expand All @@ -29,18 +74,26 @@ - (void)viewDidLoad {
[super viewDidLoad];

self.title = l10n_util::GetNSString(IDS_IOS_PASSWORD_CHECKUP);

_headerImageView = [self createHeaderImageView];
self.tableView.tableHeaderView = _headerImageView;
[self updateHeaderImage];
}

- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navigationController.navigationBar.backgroundColor =
[UIColor colorNamed:@"password_checkup_header_background_color"];
// Update the navigation bar background color as it is different for the
// PasswordCheckupViewController than for its parent.
[self updateNavigationBarBackgroundColorForDismissal:NO];
}

- (void)willMoveToParentViewController:(UIViewController*)parent {
[super willMoveToParentViewController:parent];
// Set the navigation bar background color back to `nil`.
super.navigationController.navigationBar.backgroundColor = nil;
if (!parent) {
// Reset the navigation bar background color to what it was before getting
// to the PasswordCheckupViewController.
[self updateNavigationBarBackgroundColorForDismissal:YES];
}
}

- (void)didMoveToParentViewController:(UIViewController*)parent {
Expand All @@ -50,6 +103,15 @@ - (void)didMoveToParentViewController:(UIViewController*)parent {
}
}

- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
[super traitCollectionDidChange:previousTraitCollection];
if (self.traitCollection.verticalSizeClass !=
previousTraitCollection.verticalSizeClass) {
[self updateNavigationBarBackgroundColorForDismissal:NO];
[self updateTableViewHeaderView];
}
}

#pragma mark - SettingsControllerProtocol

- (void)reportDismissalUserAction {
Expand All @@ -72,13 +134,81 @@ - (void)settingsWillBeDismissed {

- (void)setPasswordCheckupHomepageState:(PasswordCheckupHomepageState)state
insecurePasswordCounts:
(password_manager::InsecurePasswordCounts)
insecurePasswordCounts {
// TODO(crbug.com/1406540): Add method's body.
(InsecurePasswordCounts)insecurePasswordCounts {
// If the state and the insecure password counts both haven't changed, there
// is no need to update anything.
if (_passwordCheckupState == state &&
_insecurePasswordCounts == insecurePasswordCounts) {
return;
}

// If state is PasswordCheckupHomepageStateDisabled, it means that there is no
// saved password to check, so we return to the Password Manager.
if (state == PasswordCheckupHomepageStateDisabled) {
[self.handler dismissPasswordCheckupViewController];
}

_passwordCheckupState = state;
_insecurePasswordCounts = insecurePasswordCounts;
[self updateHeaderImage];
}

- (void)setAffiliatedGroupCount:(NSInteger)affiliatedGroupCount {
// TODO(crbug.com/1406540): Add method's body.
}

#pragma mark - Private

// Creates the header image view.
- (UIImageView*)createHeaderImageView {
UIImageView* headerImageView = [[UIImageView alloc] init];
headerImageView.contentMode = UIViewContentModeScaleAspectFill;
headerImageView.frame = CGRectMake(0, 0, 0, kHeaderImageHeight);
return headerImageView;
}

// Updates the background color of the navigation bar. When iPhones are in
// landscape mode, we want to hide the header image, and so we want to update
// the background color of the navigation bar accordingly. We also want to set
// the background color back to `nil` when returning to the previous view
// controller to cleanup the color change made in this view controller.
- (void)updateNavigationBarBackgroundColorForDismissal:
(BOOL)viewControllerWillBeDismissed {
if (viewControllerWillBeDismissed || IsCompactHeight(self)) {
self.navigationController.navigationBar.backgroundColor = nil;
return;
}
self.navigationController.navigationBar.backgroundColor =
[UIColor colorNamed:@"password_checkup_header_background_color"];
}

// Updates the table view's header view depending on whether the header image
// view should be shown or not. When we're in iPhone landscape mode, we want to
// hide the image header view.
- (void)updateTableViewHeaderView {
if (IsCompactHeight(self)) {
self.tableView.tableHeaderView = nil;
} else {
self.tableView.tableHeaderView = _headerImageView;
}
}

// Updates the header image according to the current
// PasswordCheckupHomepageState.
- (void)updateHeaderImage {
switch (_passwordCheckupState) {
case PasswordCheckupHomepageStateDone:
case PasswordCheckupHomepageStateRunning: {
UIImage* headerImage =
GetHeaderImage(_passwordCheckupState, _insecurePasswordCounts);
[_headerImageView setImage:headerImage];
break;
}
case PasswordCheckupHomepageStateError:
case PasswordCheckupHomepageStateDisabled:
break;
}
[self.tableView layoutIfNeeded];
}

@end
Loading

0 comments on commit 7c9b3bd

Please sign in to comment.