chromium/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_accessory_view_controller.mm

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_accessory_view_controller.h"

#import "base/ios/ios_util.h"
#import "base/metrics/user_metrics.h"
#import "ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_accessory_view_controller_delegate.h"
#import "ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_constants.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/common/ui/util/constraints_ui_util.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ui/base/device_form_factor.h"
#import "ui/base/l10n/l10n_util.h"

namespace {

// The leading inset for the icons.
constexpr CGFloat ManualFillIconsLeadingInset = 10;

// The trailing inset for the icons.
constexpr CGFloat ManualFillIconsTrailingInset = 24;

// The iPad override for the trailing inset.
constexpr CGFloat ManualFillIconsIPadTrailingInset = 20;

// Default spacing for the icons.
constexpr CGFloat ManualFillIconsSpacing = 10;

// iPad override for the icons' spacing.
constexpr CGFloat ManualFillIconsIPadSpacing = 15;

// Size of the symbols.
constexpr CGFloat kSymbolSize = 18;

// Color to use for the buttons while enabled.
UIColor* IconActiveTintColor() {
  return [UIColor colorNamed:kToolbarButtonColor];
}

// Color to use for the buttons while highlighted.
UIColor* IconHighlightTintColor() {
  return [UIColor colorNamed:kBlueColor];
}

}  // namespace

static NSTimeInterval MFAnimationDuration = 0.2;

@interface ManualFillAccessoryViewController ()

// Delegate to handle interactions.
@property(nonatomic, readonly, weak)
    id<ManualFillAccessoryViewControllerDelegate>
        delegate;

// The button to close manual fallback.
@property(nonatomic, strong) UIButton* keyboardButton;

// The button to open the passwords section.
@property(nonatomic, strong) UIButton* passwordButton;

// The button to open the credit cards section.
@property(nonatomic, strong) UIButton* cardsButton;

// The button to open the profiles section.
@property(nonatomic, strong) UIButton* accountButton;

@end

@implementation ManualFillAccessoryViewController

#pragma mark - Public

- (instancetype)initWithDelegate:
    (id<ManualFillAccessoryViewControllerDelegate>)delegate {
  self = [super initWithNibName:nil bundle:nil];
  if (self) {
    _delegate = delegate;
  }
  return self;
}

- (void)resetAnimated:(BOOL)animated {
  [UIView animateWithDuration:animated ? MFAnimationDuration : 0
                   animations:^{
                     [self resetIcons];
                   }];
  if (!self.keyboardButton.hidden) {
    [self setKeyboardButtonHidden:YES animated:animated];
  }
}

#pragma mark - Accessors

- (BOOL)allButtonsHidden {
  BOOL manualFillButtonsHidden = self.addressButtonHidden &&
                                 self.creditCardButtonHidden &&
                                 self.passwordButtonHidden;
  return ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET
             ? manualFillButtonsHidden
             : self.keyboardButton.hidden && manualFillButtonsHidden;
}

- (void)setAddressButtonHidden:(BOOL)addressButtonHidden {
  if (addressButtonHidden == _addressButtonHidden) {
    return;
  }
  _accountButton.hidden = addressButtonHidden;
  _addressButtonHidden = addressButtonHidden;
}

- (void)setCreditCardButtonHidden:(BOOL)creditCardButtonHidden {
  if (creditCardButtonHidden == _creditCardButtonHidden) {
    return;
  }
  _cardsButton.hidden = creditCardButtonHidden;
  _creditCardButtonHidden = creditCardButtonHidden;
}

- (void)setPasswordButtonHidden:(BOOL)passwordButtonHidden {
  if (passwordButtonHidden == _passwordButtonHidden) {
    return;
  }
  _passwordButton.hidden = passwordButtonHidden;
  _passwordButtonHidden = passwordButtonHidden;
}

- (void)setViewHidden:(BOOL)hidden {
  self.view.hidden = hidden;
}

#pragma mark - Private

// Helper to create a system button with the passed data and `self` as the
// target. Such button has been configured to have some preset properties
- (UIButton*)manualFillButtonWithAction:(SEL)selector
                            symbolNamed:(NSString*)symbolName
                          defaultSymbol:(BOOL)defaultSymbol
                accessibilityIdentifier:(NSString*)accessibilityIdentifier
                     accessibilityLabel:(NSString*)accessibilityLabel {
  UIButton* button = [UIButton buttonWithType:UIButtonTypeSystem];
  UIImageConfiguration* imageConfiguration = [UIImageSymbolConfiguration
      configurationWithPointSize:kSymbolSize
                          weight:UIImageSymbolWeightBold
                           scale:UIImageSymbolScaleMedium];
  UIImage* image =
      defaultSymbol
          ? DefaultSymbolWithConfiguration(symbolName, imageConfiguration)
          : CustomSymbolWithConfiguration(symbolName, imageConfiguration);
  UIButtonConfiguration* buttonConfiguration =
      [UIButtonConfiguration plainButtonConfiguration];
  buttonConfiguration.contentInsets = NSDirectionalEdgeInsetsMake(0, 0, 0, 0);
  button.configuration = buttonConfiguration;

  [button setImage:image forState:UIControlStateNormal];
  button.tintColor = IconActiveTintColor();
  button.translatesAutoresizingMaskIntoConstraints = NO;
  [button addTarget:self
                action:selector
      forControlEvents:UIControlEventTouchUpInside];
  button.accessibilityIdentifier = accessibilityIdentifier;
  button.accessibilityLabel = accessibilityLabel;
  button.tintAdjustmentMode = UIViewTintAdjustmentModeNormal;
  return button;
}

- (void)loadView {
  self.view = [[UIView alloc] init];
  self.view.translatesAutoresizingMaskIntoConstraints = NO;

  NSMutableArray<UIView*>* icons = [[NSMutableArray alloc] init];

  if (ui::GetDeviceFormFactor() != ui::DEVICE_FORM_FACTOR_TABLET) {
    self.keyboardButton = [self
        manualFillButtonWithAction:@selector(keyboardButtonPressed:)
                       symbolNamed:kKeyboardSymbol
                     defaultSymbol:YES
           accessibilityIdentifier:manual_fill::
                                       kAccessoryKeyboardAccessibilityIdentifier
                accessibilityLabel:l10n_util::GetNSString(
                                       IDS_IOS_MANUAL_FALLBACK_SHOW_KEYBOARD)];
    [icons addObject:self.keyboardButton];
    self.keyboardButton.hidden = YES;
    self.keyboardButton.alpha = 0.0;
  }

  self.passwordButton = [self
      manualFillButtonWithAction:@selector(passwordButtonPressed:)
                     symbolNamed:kPasswordSymbol
                   defaultSymbol:NO
         accessibilityIdentifier:manual_fill::
                                     kAccessoryPasswordAccessibilityIdentifier
              accessibilityLabel:l10n_util::GetNSString(
                                     IDS_IOS_MANUAL_FALLBACK_SHOW_PASSWORDS)];

  self.passwordButton.hidden = self.isPasswordButtonHidden;

  UIButtonConfiguration* buttonConfiguration =
      self.passwordButton.configuration;
  buttonConfiguration.contentInsets = NSDirectionalEdgeInsetsMake(0, 2, 0, 2);
  self.passwordButton.configuration = buttonConfiguration;

  [icons addObject:self.passwordButton];

  self.cardsButton =
      [self manualFillButtonWithAction:@selector(cardButtonPressed:)
                           symbolNamed:kCreditCardSymbol
                         defaultSymbol:YES
               accessibilityIdentifier:
                   manual_fill::kAccessoryCreditCardAccessibilityIdentifier
                    accessibilityLabel:
                        l10n_util::GetNSString(
                            IDS_IOS_MANUAL_FALLBACK_SHOW_CREDIT_CARDS)];
  self.cardsButton.hidden = self.isCreditCardButtonHidden;
  [icons addObject:self.cardsButton];

  self.accountButton = [self
      manualFillButtonWithAction:@selector(accountButtonPressed:)
                     symbolNamed:kLocationSymbol
                   defaultSymbol:NO
         accessibilityIdentifier:manual_fill::
                                     kAccessoryAddressAccessibilityIdentifier
              accessibilityLabel:l10n_util::GetNSString(
                                     IDS_IOS_MANUAL_FALLBACK_SHOW_ADDRESSES)];

  self.accountButton.hidden = self.isAddressButtonHidden;
  [icons addObject:self.accountButton];

  for (UIButton* button in icons)
    button.pointerInteractionEnabled = YES;

  UIStackView* stackView = [[UIStackView alloc] initWithArrangedSubviews:icons];
  stackView.spacing =
      (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET)
          ? ManualFillIconsIPadSpacing
          : ManualFillIconsSpacing;
  stackView.axis = UILayoutConstraintAxisHorizontal;
  stackView.translatesAutoresizingMaskIntoConstraints = NO;
  [self.view addSubview:stackView];

  CGFloat trailingInset =
      (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET)
          ? ManualFillIconsIPadTrailingInset
          : ManualFillIconsTrailingInset;
  id<LayoutGuideProvider> safeAreaLayoutGuide = self.view.safeAreaLayoutGuide;
  [NSLayoutConstraint activateConstraints:@[
    // Vertical constraints.
    [stackView.heightAnchor constraintEqualToAnchor:self.view.heightAnchor],
    [stackView.topAnchor constraintEqualToAnchor:self.view.topAnchor],

    // Horizontal constraints.
    [stackView.leadingAnchor
        constraintEqualToAnchor:safeAreaLayoutGuide.leadingAnchor
                       constant:ManualFillIconsLeadingInset],
    [safeAreaLayoutGuide.trailingAnchor
        constraintEqualToAnchor:stackView.trailingAnchor
                       constant:trailingInset],
  ]];
}

// Resets the icon's color and userInteractionEnabled.
- (void)resetIcons {
  self.accountButton.userInteractionEnabled = YES;
  self.cardsButton.userInteractionEnabled = YES;
  self.passwordButton.userInteractionEnabled = YES;

  [self.accountButton setTintColor:IconActiveTintColor()];
  [self.passwordButton setTintColor:IconActiveTintColor()];
  [self.cardsButton setTintColor:IconActiveTintColor()];
}

- (void)setKeyboardButtonHidden:(BOOL)hidden animated:(BOOL)animated {
  [UIView animateWithDuration:animated ? MFAnimationDuration : 0
                   animations:^{
                     // Workaround setting more than once the `hidden` property
                     // in stacked views.
                     if (self.keyboardButton.hidden != hidden) {
                       self.keyboardButton.hidden = hidden;
                     }

                     if (hidden) {
                       self.keyboardButton.alpha = 0.0;
                     } else {
                       self.keyboardButton.alpha = 1.0;
                     }
                   }];
}

- (void)keyboardButtonPressed:(UIButton*)keyboardButton {
  base::RecordAction(base::UserMetricsAction("ManualFallback_Close"));
  [self resetAnimated:YES];
  [self.delegate manualFillAccessoryViewController:self
                            didPressKeyboardButton:keyboardButton];
}

- (void)passwordButtonPressed:(UIButton*)passwordButton {
  base::RecordAction(base::UserMetricsAction("ManualFallback_OpenPassword"));
  [self setKeyboardButtonHidden:NO animated:YES];
  [self resetIcons];
  self.passwordButton.userInteractionEnabled = NO;
  self.passwordButton.tintColor = IconHighlightTintColor();
  [self.delegate manualFillAccessoryViewController:self
                            didPressPasswordButton:passwordButton];
}

- (void)cardButtonPressed:(UIButton*)creditCardButton {
  base::RecordAction(base::UserMetricsAction("ManualFallback_OpenCreditCard"));
  [self setKeyboardButtonHidden:NO animated:YES];
  [self resetIcons];
  self.cardsButton.userInteractionEnabled = NO;
  self.cardsButton.tintColor = IconHighlightTintColor();
  [self.delegate manualFillAccessoryViewController:self
                          didPressCreditCardButton:creditCardButton];
}

- (void)accountButtonPressed:(UIButton*)accountButton {
  base::RecordAction(base::UserMetricsAction("ManualFallback_OpenProfile"));
  [self setKeyboardButtonHidden:NO animated:YES];
  [self resetIcons];
  self.accountButton.userInteractionEnabled = NO;
  self.accountButton.tintColor = IconHighlightTintColor();
  [self.delegate manualFillAccessoryViewController:self
                             didPressAccountButton:accountButton];
}

@end