Commit 65e0fc0a authored by Nazerke's avatar Nazerke Committed by Commit Bot

[iOS][coordinator] Remove Non Modal Alert Coordinator.

This CL removes unused NonModalAlertCoordinator,
NonModalAlertTouchForwarder and NonModalAlertPresentationUpdater.

Change-Id: Icb676ce2e1b8a057f30b55336204ab5654d9b6f6
Bug: 1065878, 1029346
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2128486
Commit-Queue: Nazerke Kalidolda <nazerke@google.com>
Reviewed-by: default avatarKurt Horimoto <kkhorimoto@chromium.org>
Cr-Commit-Position: refs/heads/master@{#754845}
parent 75b573c3
......@@ -79,7 +79,6 @@ source_set("dialogs_internal") {
"//ios/chrome/browser/overlays/public/web_content_area",
"//ios/chrome/browser/ui/alert_coordinator",
"//ios/chrome/browser/ui/alert_coordinator",
"//ios/chrome/browser/ui/dialogs/non_modal",
"//ios/chrome/browser/ui/util",
"//ios/web",
"//ui/base",
......
# 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.
source_set("non_modal") {
sources = [
"non_modal_alert_coordinator.h",
"non_modal_alert_coordinator.mm",
]
configs += [ "//build/config/compiler:enable_arc" ]
deps = [
":ui",
"//base",
"//ios/chrome/browser/ui/alert_coordinator",
"//ios/chrome/browser/ui/fullscreen",
"//ios/chrome/browser/ui/fullscreen:coordinators",
"//ios/chrome/browser/ui/fullscreen:feature_flags",
]
}
source_set("ui") {
sources = [
"non_modal_alert_presentation_updater.h",
"non_modal_alert_presentation_updater.mm",
"non_modal_alert_touch_forwarder.h",
"non_modal_alert_touch_forwarder.mm",
]
configs += [ "//build/config/compiler:enable_arc" ]
deps = [
"//base",
"//ios/chrome/browser/ui/fullscreen:ui",
]
}
source_set("unit_tests") {
testonly = true
sources = [
"non_modal_alert_presentation_updater_unittest.mm",
"non_modal_alert_touch_forwarder_unittest.mm",
]
configs += [ "//build/config/compiler:enable_arc" ]
deps = [
":ui",
"//base",
"//base/test:test_support",
"//ios/chrome/browser/ui/fullscreen",
"//ios/chrome/browser/ui/fullscreen/test",
"//testing/gtest",
]
}
// 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.
#ifndef IOS_CHROME_BROWSER_UI_DIALOGS_NON_MODAL_NON_MODAL_ALERT_COORDINATOR_H_
#define IOS_CHROME_BROWSER_UI_DIALOGS_NON_MODAL_NON_MODAL_ALERT_COORDINATOR_H_
#import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h"
// A coordinator that manages displaying alerts non-modally over the visible
// viewport of the content area.
@interface NonModalAlertCoordinator : AlertCoordinator
// Non-modal alerts must be instantiated with a BrowserState, so the initializer
// without the BrowserState parameter is disabled.
- (instancetype)initWithBaseViewController:(UIViewController*)viewController
title:(NSString*)title
message:(NSString*)message NS_UNAVAILABLE;
@end
#endif // IOS_CHROME_BROWSER_UI_DIALOGS_NON_MODAL_NON_MODAL_ALERT_COORDINATOR_H_
// 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/dialogs/non_modal/non_modal_alert_coordinator.h"
#include <memory>
#include "base/logging.h"
#import "ios/chrome/browser/ui/dialogs/non_modal/non_modal_alert_presentation_updater.h"
#import "ios/chrome/browser/ui/fullscreen/chrome_coordinator+fullscreen_disabling.h"
#import "ios/chrome/browser/ui/fullscreen/fullscreen_controller.h"
#import "ios/chrome/browser/ui/fullscreen/fullscreen_features.h"
#import "ios/chrome/browser/ui/fullscreen/fullscreen_ui_updater.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface NonModalAlertCoordinator () {
std::unique_ptr<FullscreenUIUpdater> _fullscreenUIUpdater;
}
// The non-modal presentation updater.
@property(nonatomic, strong)
NonModalAlertPresentationUpdater* nonModalPresentationUpdater;
@end
@implementation NonModalAlertCoordinator
- (instancetype)initWithBaseViewController:(UIViewController*)viewController
title:(NSString*)title
message:(NSString*)message
browserState:(ChromeBrowserState*)browserState {
DCHECK(browserState);
self = [super initWithBaseViewController:viewController
title:title
message:message
browserState:browserState];
return self;
}
#pragma mark - Accessors
- (void)setNonModalPresentationUpdater:
(NonModalAlertPresentationUpdater*)nonModalPresentationUpdater {
if (_nonModalPresentationUpdater == nonModalPresentationUpdater)
return;
FullscreenController* fullscreenController;
if (fullscreen::features::ShouldScopeFullscreenControllerToBrowser()) {
fullscreenController = FullscreenController::FromBrowser(self.browser);
} else {
fullscreenController =
FullscreenController::FromBrowserState(self.browserState);
}
_fullscreenUIUpdater = nullptr;
_nonModalPresentationUpdater = nonModalPresentationUpdater;
if (_nonModalPresentationUpdater) {
// Create an updater for the new non-modal presentation controller.
_fullscreenUIUpdater = std::make_unique<FullscreenUIUpdater>(
fullscreenController, _nonModalPresentationUpdater);
// Use the current viewport insets to set up the non-modal presentation.
[_nonModalPresentationUpdater
setUpNonModalPresentationWithViewportInsets:
fullscreenController->GetCurrentViewportInsets()];
}
}
#pragma mark - ChromeCoordinator
- (void)start {
[super start];
// Disable fullscreen while non-modal alerts are displayed to ensure that the
// toolbars are fully visible and interactable.
[self didStartFullscreenDisablingUI];
// Create the non-modal presentation controller for the alert.
self.nonModalPresentationUpdater = [[NonModalAlertPresentationUpdater alloc]
initWithAlertController:self.alertController];
}
- (void)stop {
[super stop];
self.nonModalPresentationUpdater = nil;
[self didStopFullscreenDisablingUI];
}
@end
// 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.
#ifndef IOS_CHROME_BROWSER_UI_DIALOGS_NON_MODAL_NON_MODAL_ALERT_PRESENTATION_UPDATER_H_
#define IOS_CHROME_BROWSER_UI_DIALOGS_NON_MODAL_NON_MODAL_ALERT_PRESENTATION_UPDATER_H_
#import <UIKit/UIKit.h>
#import "ios/chrome/browser/ui/fullscreen/fullscreen_ui_element.h"
// UIAlertController does not allow for customization of its presentation via
// UIPresentationControllers, UIViewControllerTransitioningDelegates, or
// UIModalPresentationStyle. This helper object applies a mask to the
// presentation container view of a UIAlertController, and forwards touches to
// the presenting view controller when they lie outside of that mask. The
// updater is a FullscreenUIElement, and should be notified of fullscreen UI
// changes.
//
// Example usage:
//
// UIAlertController* alert = [[UIAlertControler alloc] init...];
// [baseViewController presentViewController:alert animated...];
//
// NonModalAlertPresentationUpdater* presentationUpdater =
// [[NonModalAlertPresentationUpdater alloc] initWithAlertController:alert];
// [presentationUpdater setUpNonModalPresentationWithViewportInsets:
// fullscrenController->GetCurrentViewportInsets()];
//
// _fullscreenObserver =
// std::make_unique<FullscreenUIUpdater>(_nonModalPresentationController);
// fullscreenController->AddObserver(_fullscreenObserver.get());
//
@interface NonModalAlertPresentationUpdater : NSObject <FullscreenUIElement>
// Designated initializer for a presentation updater that makes
// |alertController| non-modal.
- (instancetype)initWithAlertController:(UIAlertController*)alertController
NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
// Updates the alert to be presented non-modally by applying a mask to its
// presentation container and forwarding touches outside that mask to its
// presenting view controller. |viewportInsets| are the insets from the side of
// the window to mask the alert presentation container, and should be enough so
// that the toolbars are completely visible.
- (void)setUpNonModalPresentationWithViewportInsets:
(UIEdgeInsets)viewportInsets;
@end
#endif // IOS_CHROME_BROWSER_UI_DIALOGS_NON_MODAL_NON_MODAL_ALERT_PRESENTATION_UPDATER_H_
// 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/dialogs/non_modal/non_modal_alert_presentation_updater.h"
#include "base/logging.h"
#import "ios/chrome/browser/ui/dialogs/non_modal/non_modal_alert_touch_forwarder.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface NonModalAlertPresentationUpdater ()
// The alert controller whose presentation is being controlled.
@property(nonatomic, weak, readonly) UIAlertController* alertController;
// The presentation container view for the alert.
@property(nonatomic, readonly) UIView* presentationContainerView;
@end
@implementation NonModalAlertPresentationUpdater
- (instancetype)initWithAlertController:(UIAlertController*)alertController {
if (self = [super init]) {
DCHECK(alertController);
_alertController = alertController;
}
return self;
}
#pragma mark - Accessors
- (UIView*)presentationContainerView {
return self.alertController.presentationController.containerView;
}
#pragma mark - Public
- (void)setUpNonModalPresentationWithViewportInsets:
(UIEdgeInsets)viewportInsets {
DCHECK(!self.presentationContainerView.layer.mask);
// If the alert is being presented with animations, the presentation container
// view will not be instantiated until the transition begins, so the mask is
// set up in the transition's animation block.
if (!self.presentationContainerView) {
__weak NonModalAlertPresentationUpdater* weakSelf = self;
auto addMaskBlock =
^void(id<UIViewControllerTransitionCoordinatorContext>) {
[weakSelf setUpNonModalPresentationWithViewportInsets:viewportInsets];
};
[self.alertController.transitionCoordinator
animateAlongsideTransition:addMaskBlock
completion:nil];
return;
}
// Add the mask to the presentation container.
CALayer* mask = [CALayer layer];
mask.backgroundColor = [UIColor blackColor].CGColor;
self.presentationContainerView.layer.mask = mask;
[self updateMaskForViewportInsets:viewportInsets];
// Add a touch forwarder for the mask.
NonModalAlertTouchForwarder* touchForwarder =
[[NonModalAlertTouchForwarder alloc] init];
touchForwarder.forwardingTarget =
self.alertController.presentingViewController.view;
touchForwarder.mask = mask;
UIView* alertView = self.alertController.view;
[alertView.superview insertSubview:touchForwarder belowSubview:alertView];
}
#pragma mark - FullscreenUIElement
- (void)updateForFullscreenProgress:(CGFloat)progress {
// |-updateForFullscreenProgress:| is non-optional for performance reasons,
// but the progress is never expected to change from 1.0 because fullscreen is
// disabled.
NOTREACHED() << "Fullscreen is disabled during non-modal alert presentation.";
}
- (void)updateForFullscreenMinViewportInsets:(UIEdgeInsets)minViewportInsets
maxViewportInsets:(UIEdgeInsets)maxViewportInsets {
[self updateMaskForViewportInsets:maxViewportInsets];
}
#pragma mark - Private
// Updates the alert's presentation container view mask using |viewportInsets|.
- (void)updateMaskForViewportInsets:(UIEdgeInsets)viewportInsets {
self.presentationContainerView.layer.mask.frame = UIEdgeInsetsInsetRect(
self.presentationContainerView.layer.bounds, viewportInsets);
}
@end
// 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/dialogs/non_modal/non_modal_alert_presentation_updater.h"
#import "base/mac/foundation_util.h"
#import "base/test/ios/wait_util.h"
#import "ios/chrome/browser/ui/dialogs/non_modal/non_modal_alert_touch_forwarder.h"
#include "testing/platform_test.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
using base::test::ios::WaitUntilConditionOrTimeout;
using base::test::ios::kWaitForUIElementTimeout;
// Test fixture for the NonModalAlertPresentationController.
class NonModalAlertPresentationControllerTest : public PlatformTest {
protected:
NonModalAlertPresentationControllerTest()
: window_([[UIWindow alloc] init]),
view_controller_([[UIViewController alloc] init]),
alert_controller_([UIAlertController
alertControllerWithTitle:@"Test Alert"
message:@"Message"
preferredStyle:UIAlertControllerStyleAlert]),
presentation_updater_([[NonModalAlertPresentationUpdater alloc]
initWithAlertController:alert_controller_]) {
window_.frame = CGRectMake(0, 0, 400, 400);
window_.rootViewController = view_controller_;
original_key_window_ = [UIApplication sharedApplication].keyWindow;
[window_ makeKeyAndVisible];
[alert_controller_
addAction:[UIAlertAction
actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction* _Nonnull action){
}]];
}
~NonModalAlertPresentationControllerTest() override {
[window_ resignKeyWindow];
[original_key_window_ makeKeyAndVisible];
}
// Presents the alert and waits for the presentation to be completed.
void PresentAlert() {
__block bool presented = false;
[view_controller_ presentViewController:alert_controller_
animated:YES
completion:^{
presented = true;
}];
ASSERT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForUIElementTimeout, ^{
return presented;
}));
}
UIWindow* original_key_window_ = nil;
UIWindow* window_ = nil;
UIViewController* view_controller_ = nil;
UIAlertController* alert_controller_ = nil;
NonModalAlertPresentationUpdater* presentation_updater_ = nil;
};
// Verifies that the presentation updater sets up the mask and touch forwarder
// correctly.
TEST_F(NonModalAlertPresentationControllerTest, SetUp) {
const UIEdgeInsets kInsets = UIEdgeInsetsMake(50, 50, 50, 50);
PresentAlert();
[presentation_updater_ setUpNonModalPresentationWithViewportInsets:kInsets];
UIView* presentation_container_view =
alert_controller_.presentationController.containerView;
CALayer* presentation_container_layer = presentation_container_view.layer;
// Check that the presentation container is masked.
CALayer* mask = presentation_container_layer.mask;
EXPECT_TRUE(mask);
EXPECT_TRUE(CGRectEqualToRect(
mask.frame,
UIEdgeInsetsInsetRect(presentation_container_layer.bounds, kInsets)));
// Check that the touch forwarder has been inserted.
NonModalAlertTouchForwarder* touch_forwarder = nil;
for (UIView* subview in presentation_container_view.subviews) {
touch_forwarder = base::mac::ObjCCast<NonModalAlertTouchForwarder>(subview);
if (touch_forwarder)
break;
}
EXPECT_TRUE(touch_forwarder);
EXPECT_EQ(touch_forwarder.mask, mask);
EXPECT_EQ(touch_forwarder.forwardingTarget, view_controller_.view);
}
// Verifies that the presentation updater updates the mask for fullscreen inset
// changes.
TEST_F(NonModalAlertPresentationControllerTest, UpdateMask) {
const UIEdgeInsets kInsets = UIEdgeInsetsMake(50, 50, 50, 50);
PresentAlert();
[presentation_updater_ setUpNonModalPresentationWithViewportInsets:kInsets];
// Check that the presentation container is masked.
UIView* presentation_container_view =
alert_controller_.presentationController.containerView;
CALayer* presentation_container_layer = presentation_container_view.layer;
CALayer* mask = presentation_container_layer.mask;
ASSERT_TRUE(mask);
ASSERT_TRUE(CGRectEqualToRect(
mask.frame,
UIEdgeInsetsInsetRect(presentation_container_layer.bounds, kInsets)));
// Check that the mask frame is updated for new insets.
const UIEdgeInsets kNewInsets = UIEdgeInsetsMake(15, 50, 15, 50);
[presentation_updater_ updateForFullscreenMinViewportInsets:kNewInsets
maxViewportInsets:kNewInsets];
EXPECT_TRUE(CGRectEqualToRect(
mask.frame,
UIEdgeInsetsInsetRect(presentation_container_layer.bounds, kNewInsets)));
}
// 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.
#ifndef IOS_CHROME_BROWSER_UI_DIALOGS_NON_MODAL_NON_MODAL_ALERT_TOUCH_FORWARDER_H_
#define IOS_CHROME_BROWSER_UI_DIALOGS_NON_MODAL_NON_MODAL_ALERT_TOUCH_FORWARDER_H_
#import <UIKit/UIKit.h>
// Helper view that is inserted into the UIAlertController's hierarchy that
// overrides |-hitTest:withEvent:| to forward touch events that lie outside of
// the non-modal alert presentation container mask. This view should be
// inserted between the UIAlertController's view and its presentation container
// view. This solution is necessary because UIAlertController presents a full-
// screen shim over the app that intercepts touches even if masked.
//
// Example Usage:
//
// # After adding mask, toolbars are visible but non-interactable.
// [viewController presentViewController:alertController ...];
// alertController.presentationController.containerView.layer.mask = mask;
//
// # Inserting |forwarder| into the hierarchy forwards touches to the presenter.
// NonModalAlertTouchForwarder* forwarder =
// [[NonModalAlertTouchForwarder alloc] init];
// forwarder.mask = mask;
// forwarder.forwardingTarger = viewController.view;
// UIView* alertView = alertController.view;
// [alertView.superView insertSubview:forwarder belowSubview:alertView];
@interface NonModalAlertTouchForwarder : UIView
// The non-modal alert presentation container mask.
@property(nonatomic, weak) CALayer* mask;
// The UIView that should receive touches that occur outside of |mask|.
@property(nonatomic, weak) UIView* forwardingTarget;
@end
#endif // IOS_CHROME_BROWSER_UI_DIALOGS_NON_MODAL_NON_MODAL_ALERT_TOUCH_FORWARDER_H_
// 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/dialogs/non_modal/non_modal_alert_touch_forwarder.h"
#include "base/logging.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@implementation NonModalAlertTouchForwarder
- (void)setForwardingTarget:(UIView*)forwardingTarget {
if (_forwardingTarget == forwardingTarget)
return;
_forwardingTarget = forwardingTarget;
DCHECK(!_forwardingTarget || [self canForwardToTarget]);
}
#pragma mark - UIView
- (void)didMoveToSuperview {
[super didMoveToSuperview];
// The touch forwarder should not be added as a descendant of the target.
DCHECK(!_forwardingTarget || [self canForwardToTarget]);
}
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event {
if ([self shouldForwardTouchAtLocation:point]) {
CGPoint forwardedPoint = [self.forwardingTarget convertPoint:point
fromView:self];
return [self.forwardingTarget hitTest:forwardedPoint withEvent:event];
} else {
return [super hitTest:point withEvent:event];
}
}
#pragma mark - Private
// Whether a touch in this view at |location| should be forwarded to the
// forwarding target.
- (BOOL)shouldForwardTouchAtLocation:(CGPoint)location {
if (![self canForwardToTarget] || !self.mask)
return NO;
return !CGRectContainsPoint(
self.mask.bounds, [self.mask convertPoint:location fromLayer:self.layer]);
}
// Touches are forwarded by overriding |-hitTest:withEvent:|, but if the
// forwarder is a descendant of the target, forwarding will create an infinite
// loop.
- (BOOL)canForwardToTarget {
return self.forwardingTarget &&
![self isDescendantOfView:self.forwardingTarget];
}
@end
// 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/dialogs/non_modal/non_modal_alert_touch_forwarder.h"
#include "testing/platform_test.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// The length of the test view's size.
const CGFloat kViewSize = 400.0;
// The inset to use for the mask.
const CGFloat kMaskInset = 50.0;
}
// Test fixture for the NonModalAlertTouchForwarder.
class NonModalAlertTouchForwarderTest : public PlatformTest {
public:
NonModalAlertTouchForwarderTest()
: container_view_([[UIView alloc] initWithFrame:CGRectZero]),
background_view_([[UIView alloc] initWithFrame:CGRectZero]),
foreground_view_([[UIView alloc] initWithFrame:CGRectZero]),
mask_([CALayer layer]),
touch_forwarder_([[NonModalAlertTouchForwarder alloc] init]) {
touch_forwarder_.mask = mask_;
touch_forwarder_.forwardingTarget = background_view_;
container_view_.frame = CGRectMake(0, 0, kViewSize, kViewSize);
background_view_.frame = container_view_.bounds;
[container_view_ addSubview:background_view_];
foreground_view_.frame = background_view_.bounds;
[container_view_ addSubview:foreground_view_];
mask_.backgroundColor = [UIColor blackColor].CGColor;
mask_.frame = UIEdgeInsetsInsetRect(
foreground_view_.bounds,
UIEdgeInsetsMake(kMaskInset, kMaskInset, kMaskInset, kMaskInset));
foreground_view_.layer.mask = mask_;
[foreground_view_ addSubview:touch_forwarder_];
}
// Returns the hit view in the test hierarchy at |location|.
UIView* GetHitView(CGPoint location) {
return [container_view_ hitTest:location withEvent:nil];
}
protected:
UIView* container_view_ = nil;
UIView* background_view_ = nil;
UIView* foreground_view_ = nil;
CALayer* mask_ = nil;
NonModalAlertTouchForwarder* touch_forwarder_ = nil;
};
// Tests that |-hitTest:withEvent:| for touches within the mask are routed to
// the foreground view.
TEST_F(NonModalAlertTouchForwarderTest, TouchWithinMask) {
const CGPoint kTouchPoint = CGPointMake(kViewSize / 2.0, kViewSize / 2.0);
EXPECT_EQ(GetHitView(kTouchPoint), foreground_view_);
}
// Tests that |-hitTest:withEvent:| for touches outside of the mask are routed
// to the background view.
TEST_F(NonModalAlertTouchForwarderTest, TouchOutsideMask) {
const CGPoint kTouchPoint = CGPointMake(kMaskInset / 2.0, kMaskInset / 2.0);
EXPECT_EQ(GetHitView(kTouchPoint), background_view_);
}
......@@ -249,7 +249,6 @@ test("ios_chrome_unittests") {
"//ios/chrome/browser/ui/context_menu:unit_tests",
"//ios/chrome/browser/ui/dialogs:unit_tests",
"//ios/chrome/browser/ui/dialogs:unit_tests_internal",
"//ios/chrome/browser/ui/dialogs/non_modal:unit_tests",
"//ios/chrome/browser/ui/download:unit_tests",
"//ios/chrome/browser/ui/elements:unit_tests",
"//ios/chrome/browser/ui/fancy_ui:unit_tests",
......
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