// 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/ui/toolbar/secondary_toolbar_view_controller.h"
#import "base/check.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/ui/util/keyboard_observer_helper.h"
#import "ios/chrome/browser/shared/ui/util/layout_guide_names.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
#import "ios/chrome/browser/shared/ui/util/util_swift.h"
#import "ios/chrome/browser/ui/fullscreen/fullscreen_controller.h"
#import "ios/chrome/browser/ui/fullscreen/scoped_fullscreen_disabler.h"
#import "ios/chrome/browser/ui/toolbar/adaptive_toolbar_view_controller+subclassing.h"
#import "ios/chrome/browser/ui/toolbar/buttons/toolbar_button_factory.h"
#import "ios/chrome/browser/ui/toolbar/buttons/toolbar_configuration.h"
#import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
#import "ios/chrome/browser/ui/toolbar/public/toolbar_height_delegate.h"
#import "ios/chrome/browser/ui/toolbar/public/toolbar_utils.h"
#import "ios/chrome/browser/ui/toolbar/secondary_toolbar_keyboard_state_provider.h"
#import "ios/chrome/browser/ui/toolbar/secondary_toolbar_view.h"
#import "ios/chrome/common/ui/util/ui_util.h"
@interface SecondaryToolbarViewController ()
/// Redefined to be a `SecondaryToolbarView`.
@property(nonatomic, strong) SecondaryToolbarView* view;
@end
@implementation SecondaryToolbarViewController
@dynamic view;
- (void)loadView {
self.view =
[[SecondaryToolbarView alloc] initWithButtonFactory:self.buttonFactory];
DCHECK(self.layoutGuideCenter);
[self.layoutGuideCenter referenceView:self.view
underName:kSecondaryToolbarGuide];
if (IsBottomOmniboxAvailable()) {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
}
}
- (void)disconnect {
_fullscreenController = nullptr;
}
#pragma mark - AdaptiveToolbarViewController
- (void)collapsedToolbarButtonTapped {
[super collapsedToolbarButtonTapped];
if ([self.view.locationBarKeyboardConstraint isActive]) {
// When the bottom omnibox is collapsed above the keyboard, it's positioned
// behind an `omniboxTypingShield` (transparent view) in the
// `formInputAccessoryView`. This allow the keyboard to know about the size
// of the omnibox (crbug.com/1490601).
// When voice over is off, tapping the collapsed bottom omnibox interacts
// with the `omniboxTypingShield`. The logic to dismiss the keyboard is
// handled in `formInputAccessoryViewHandler`. However, the typing shield
// has `isAccessibilityElement` equals NO to let the user interact with the
// omnibox on voice over. In this mode, logic to dismiss the keyboard is
// handled here in `SecondaryToolbarViewController`.
CHECK([self hasOmnibox]);
UIResponder* responder = GetFirstResponder();
[responder resignFirstResponder];
}
}
#pragma mark - FullscreenUIElement
- (void)updateForFullscreenProgress:(CGFloat)progress {
[super updateForFullscreenProgress:progress];
CGFloat alphaValue = fmax(progress * 1.1 - 0.1, 0);
if (IsBottomOmniboxAvailable()) {
self.view.buttonStackView.alpha = alphaValue;
}
self.view.locationBarTopConstraint.constant =
[self verticalMarginForLocationBarForFullscreenProgress:progress];
}
#pragma mark - UIKeyboardNotification
- (void)keyboardWillShow:(NSNotification*)notification {
if (![self hasOmnibox]) {
return;
}
[self constraintToKeyboard:YES withNotification:notification];
}
- (void)keyboardWillHide:(NSNotification*)notification {
[self constraintToKeyboard:NO withNotification:notification];
}
#pragma mark - Private
/// Returns the vertical margin to the location bar based on fullscreen
/// `progress`, aligned to the nearest pixel.
- (CGFloat)verticalMarginForLocationBarForFullscreenProgress:(CGFloat)progress {
const CGFloat clampedFontSizeMultiplier = ToolbarClampedFontSizeMultiplier(
self.traitCollection.preferredContentSizeCategory);
const BOOL hasBottomSafeArea = self.view.window.safeAreaInsets.bottom;
const CGFloat fullscreenMargin =
hasBottomSafeArea ? kBottomAdaptiveLocationBarVerticalMarginFullscreen
: 0;
return AlignValueToPixel((kBottomAdaptiveLocationBarTopMargin * progress +
fullscreenMargin * (1 - progress)) *
clampedFontSizeMultiplier +
(clampedFontSizeMultiplier - 1) *
kLocationBarVerticalMarginDynamicType);
}
/// Collapses secondary toolbar when it's moved above the keyboard.
- (void)collapseForKeyboard {
if (_fullscreenController) {
_fullscreenController->EnterForceFullscreenMode();
}
self.view.locationBarTopConstraint.constant = 0;
self.view.bottomSeparator.alpha = 1.0;
[self.toolbarHeightDelegate secondaryToolbarMovedAboveKeyboard];
}
/// Resets secondary toolbar when it's detached from the keyboard.
- (void)removeFromKeyboard {
if (_fullscreenController) {
_fullscreenController->ExitForceFullscreenMode();
}
self.view.bottomSeparator.alpha = 0.0;
[self.toolbarHeightDelegate secondaryToolbarRemovedFromKeyboard];
}
/// Updates keyboard constraints with `notification`. When
/// `constraintToKeyboard`, the toolbar is collapsed above the keyboard.
- (void)constraintToKeyboard:(BOOL)constraintToKeyboard
withNotification:(NSNotification*)notification {
if (constraintToKeyboard) {
if ([self.keyboardStateProvider keyboardIsActiveForWebContent]) {
// Enable the constraint only when the keyboard is showing for web
// content. This will not evaluate to true each time the keyboard's frame
// is updating. Thus, update the keyboard's frame even if this is false.
if (![self.view.locationBarKeyboardConstraint isActive]) {
self.view.locationBarKeyboardConstraint.active = YES;
[self collapseForKeyboard];
[self.view layoutIfNeeded];
}
}
} else if ([self.view.locationBarKeyboardConstraint isActive]) {
self.view.locationBarKeyboardConstraint.active = NO;
[self removeFromKeyboard];
[self.view layoutIfNeeded];
}
}
#pragma mark - SecondaryToolbarConsumer
- (void)makeTranslucent {
[self.view makeTranslucent];
}
- (void)makeOpaque {
[self.view makeOpaque];
}
@end