Commit 19a91e85 authored by Kurt Horimoto's avatar Kurt Horimoto Committed by Commit Bot

[iOS] Add files to support non-modal app launcher alert.

This is the non-modal version of the UI currently supported by
AppLauncherCoordinator.

Bug: 976919
Change-Id: I881293aca913870202264097ea1088cbb782cdb8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1727397
Commit-Queue: Kurt Horimoto <kkhorimoto@chromium.org>
Reviewed-by: default avatarMike Dougherty <michaeldo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#683343}
parent e6157bb7
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
source_set("web_content_area") { source_set("web_content_area") {
sources = [ sources = [
"app_launcher_alert_overlay.h",
"app_launcher_alert_overlay.mm",
"http_auth_overlay.h", "http_auth_overlay.h",
"http_auth_overlay.mm", "http_auth_overlay.mm",
"java_script_alert_overlay.h", "java_script_alert_overlay.h",
......
// Copyright 2019 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_OVERLAYS_PUBLIC_WEB_CONTENT_AREA_APP_LAUNCHER_ALERT_OVERLAY_H_
#define IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_WEB_CONTENT_AREA_APP_LAUNCHER_ALERT_OVERLAY_H_
#include "ios/chrome/browser/overlays/public/overlay_user_data.h"
// Configuration object for OverlayRequests for alerts notifying the user that
// a navigation will open another app.
class AppLauncherAlertOverlayRequestConfig
: public OverlayUserData<AppLauncherAlertOverlayRequestConfig> {
public:
~AppLauncherAlertOverlayRequestConfig() override;
// Whether the current page has previously attempted to open another app.
bool is_repeated_request() const { return is_repeated_request_; }
private:
OVERLAY_USER_DATA_SETUP(AppLauncherAlertOverlayRequestConfig);
AppLauncherAlertOverlayRequestConfig(bool is_repeated_request);
const bool is_repeated_request_;
};
// User interaction info for OverlayResponses for app launcher alerts.
class AppLauncherAlertOverlayResponseInfo
: public OverlayUserData<AppLauncherAlertOverlayResponseInfo> {
public:
~AppLauncherAlertOverlayResponseInfo() override;
// Whether the user has chosen to allow navigation to another app.
bool allow_navigation() const { return allow_navigation_; }
private:
OVERLAY_USER_DATA_SETUP(AppLauncherAlertOverlayResponseInfo);
AppLauncherAlertOverlayResponseInfo(bool allow_navigation);
const bool allow_navigation_;
};
#endif // IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_WEB_CONTENT_AREA_APP_LAUNCHER_ALERT_OVERLAY_H_
// Copyright 2019 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/overlays/public/web_content_area/app_launcher_alert_overlay.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
OVERLAY_USER_DATA_SETUP_IMPL(AppLauncherAlertOverlayRequestConfig);
AppLauncherAlertOverlayRequestConfig::AppLauncherAlertOverlayRequestConfig(
bool is_repeated_request)
: is_repeated_request_(is_repeated_request) {}
AppLauncherAlertOverlayRequestConfig::~AppLauncherAlertOverlayRequestConfig() =
default;
OVERLAY_USER_DATA_SETUP_IMPL(AppLauncherAlertOverlayResponseInfo);
AppLauncherAlertOverlayResponseInfo::AppLauncherAlertOverlayResponseInfo(
bool allow_navigation)
: allow_navigation_(allow_navigation) {}
AppLauncherAlertOverlayResponseInfo::~AppLauncherAlertOverlayResponseInfo() =
default;
...@@ -15,6 +15,7 @@ source_set("web_content_area") { ...@@ -15,6 +15,7 @@ source_set("web_content_area") {
deps = [ deps = [
"//base", "//base",
"//ios/chrome/browser/ui/overlays:coordinators", "//ios/chrome/browser/ui/overlays:coordinators",
"//ios/chrome/browser/ui/overlays/web_content_area/app_launcher",
"//ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs", "//ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs",
"//ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs:alerts", "//ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs:alerts",
"//ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs:confirmations", "//ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs:confirmations",
......
# Copyright 2019 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("app_launcher") {
sources = [
"app_launcher_alert_overlay_coordinator.h",
"app_launcher_alert_overlay_coordinator.mm",
"app_launcher_alert_overlay_mediator.h",
"app_launcher_alert_overlay_mediator.mm",
]
configs += [ "//build/config/compiler:enable_arc" ]
deps = [
"//base",
"//components/strings:components_strings_grit",
"//components/url_formatter",
"//ios/chrome/app/strings:ios_strings_grit",
"//ios/chrome/browser/overlays",
"//ios/chrome/browser/overlays/public/web_content_area",
"//ios/chrome/browser/ui/alert_view_controller",
"//ios/chrome/browser/ui/elements",
"//ios/chrome/browser/ui/overlays:coordinators",
"//ui/base",
]
}
source_set("unit_tests") {
testonly = true
sources = [
"app_launcher_alert_overlay_mediator_unittest.mm",
]
configs += [ "//build/config/compiler:enable_arc" ]
deps = [
":app_launcher",
"//base/test:test_support",
"//components/strings:components_strings_grit",
"//components/url_formatter",
"//ios/chrome/app/strings:ios_strings_grit",
"//ios/chrome/browser/overlays",
"//ios/chrome/browser/overlays/public/web_content_area",
"//ios/chrome/browser/overlays/test",
"//ios/chrome/browser/ui/alert_view_controller",
"//ios/chrome/browser/ui/alert_view_controller/test",
"//ios/chrome/browser/ui/dialogs",
"//ios/chrome/browser/ui/elements",
"//ios/chrome/browser/ui/overlays/test",
"//testing/gmock",
"//testing/gtest",
"//ui/base",
]
}
// Copyright 2019 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_OVERLAYS_WEB_CONTENT_AREA_APP_LAUNCHER_APP_LAUNCHER_ALERT_OVERLAY_COORDINATOR_H_
#define IOS_CHROME_BROWSER_UI_OVERLAYS_WEB_CONTENT_AREA_APP_LAUNCHER_APP_LAUNCHER_ALERT_OVERLAY_COORDINATOR_H_
#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator.h"
// A coordinator that is used to display UI for HTTP authentication dialogs via
// OverlayPresenter.
@interface AppLauncherAlertOverlayCoordinator : OverlayRequestCoordinator
@end
#endif // IOS_CHROME_BROWSER_UI_OVERLAYS_WEB_CONTENT_AREA_APP_LAUNCHER_APP_LAUNCHER_ALERT_OVERLAY_COORDINATOR_H_
// Copyright 2019 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/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_coordinator.h"
#include "ios/chrome/browser/overlays/public/overlay_request.h"
#include "ios/chrome/browser/overlays/public/web_content_area/app_launcher_alert_overlay.h"
#import "ios/chrome/browser/ui/alert_view_controller/alert_view_controller.h"
#import "ios/chrome/browser/ui/overlays/overlay_ui_dismissal_delegate.h"
#import "ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_mediator.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface AppLauncherAlertOverlayCoordinator () <
AppLauncherAlertOverlayMediatorDelegate>
// Whether the coordinator has been started.
@property(nonatomic, getter=isStarted) BOOL started;
@property(nonatomic) AlertViewController* alertViewController;
@property(nonatomic) AppLauncherAlertOverlayMediator* mediator;
@end
@implementation AppLauncherAlertOverlayCoordinator
#pragma mark - Accessors
- (void)setMediator:(AppLauncherAlertOverlayMediator*)mediator {
if (_mediator == mediator)
return;
_mediator.delegate = nil;
_mediator = mediator;
_mediator.delegate = self;
}
#pragma mark - AppLauncherAlertOverlayMediatorDelegate
- (void)stopDialogForMediator:(AppLauncherAlertOverlayMediator*)mediator {
DCHECK_EQ(self.mediator, mediator);
[self stopAnimated:YES];
}
#pragma mark - OverlayCoordinator
+ (BOOL)supportsRequest:(OverlayRequest*)request {
return !!request->GetConfig<AppLauncherAlertOverlayRequestConfig>();
}
- (UIViewController*)viewController {
return self.alertViewController;
}
- (void)startAnimated:(BOOL)animated {
if (self.started)
return;
self.alertViewController = [[AlertViewController alloc] init];
self.alertViewController.modalPresentationStyle =
UIModalPresentationOverCurrentContext;
self.alertViewController.modalTransitionStyle =
UIModalTransitionStyleCrossDissolve;
self.mediator =
[[AppLauncherAlertOverlayMediator alloc] initWithRequest:self.request];
self.mediator.consumer = self.alertViewController;
[self.baseViewController presentViewController:self.alertViewController
animated:animated
completion:nil];
self.started = YES;
}
- (void)stopAnimated:(BOOL)animated {
if (!self.started)
return;
__weak __typeof__(self) weakSelf = self;
[self.baseViewController
dismissViewControllerAnimated:animated
completion:^{
__typeof__(self) strongSelf = weakSelf;
if (!strongSelf)
return;
strongSelf.alertViewController = nil;
strongSelf.dismissalDelegate
->OverlayUIDidFinishDismissal(weakSelf.request);
}];
self.started = NO;
}
@end
// Copyright 2019 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_OVERLAYS_WEB_CONTENT_AREA_APP_LAUNCHER_APP_LAUNCHER_ALERT_OVERLAY_MEDIATOR_H_
#define IOS_CHROME_BROWSER_UI_OVERLAYS_WEB_CONTENT_AREA_APP_LAUNCHER_APP_LAUNCHER_ALERT_OVERLAY_MEDIATOR_H_
#import <Foundation/Foundation.h>
class OverlayRequest;
@protocol AppLauncherAlertOverlayMediatorDelegate;
@protocol AppLauncherAlertOverlayMediatorDataSource;
@protocol AlertConsumer;
// Mediator object that uses a AppLauncherAlertOverlayRequestConfig to set up
// the UI for an alert notifying the user that a navigation will open an
// external app.
@interface AppLauncherAlertOverlayMediator : NSObject
// The consumer to be updated by this mediator. Setting to a new value uses the
// AppLauncherAlertOverlayRequestConfig to update the new consumer.
@property(nonatomic, weak) id<AlertConsumer> consumer;
// The delegate that handles action button functionality set up by the mediator.
@property(nonatomic, weak) id<AppLauncherAlertOverlayMediatorDelegate> delegate;
// Designated initializer for a mediator that uses |request|'s configuration to
// set up an AlertConsumer.
- (instancetype)initWithRequest:(OverlayRequest*)request;
@end
// Protocol used by the actions set up by the
// AppLauncherAlertOverlayMediator.
@protocol AppLauncherAlertOverlayMediatorDelegate <NSObject>
// Called by |mediator| to dismiss the dialog overlay when
// an action is tapped.
- (void)stopDialogForMediator:(AppLauncherAlertOverlayMediator*)mediator;
@end
#endif // IOS_CHROME_BROWSER_UI_OVERLAYS_WEB_CONTENT_AREA_APP_LAUNCHER_APP_LAUNCHER_ALERT_OVERLAY_MEDIATOR_H_
// Copyright 2019 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/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_mediator.h"
#include "base/logging.h"
#include "components/strings/grit/components_strings.h"
#include "ios/chrome/browser/overlays/public/overlay_request.h"
#include "ios/chrome/browser/overlays/public/overlay_response.h"
#include "ios/chrome/browser/overlays/public/web_content_area/app_launcher_alert_overlay.h"
#import "ios/chrome/browser/ui/alert_view_controller/alert_action.h"
#import "ios/chrome/browser/ui/alert_view_controller/alert_consumer.h"
#import "ios/chrome/browser/ui/elements/text_field_configuration.h"
#include "ios/chrome/grit/ios_strings.h"
#include "ui/base/l10n/l10n_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface AppLauncherAlertOverlayMediator ()
@property(nonatomic, readonly) OverlayRequest* request;
@property(nonatomic, readonly) AppLauncherAlertOverlayRequestConfig* config;
@end
@implementation AppLauncherAlertOverlayMediator
- (instancetype)initWithRequest:(OverlayRequest*)request {
if (self = [super init]) {
_request = request;
DCHECK(_request);
// Verify that the request is configured for app launcher alerts.
DCHECK(_request->GetConfig<AppLauncherAlertOverlayRequestConfig>());
}
return self;
}
#pragma mark - Accessors
- (AppLauncherAlertOverlayRequestConfig*)config {
return self.request->GetConfig<AppLauncherAlertOverlayRequestConfig>();
}
- (void)setConsumer:(id<AlertConsumer>)consumer {
if (self.consumer == consumer)
return;
_consumer = consumer;
NSString* message = nil;
NSString* allowActionTitle = nil;
NSString* rejectActionTitle = l10n_util::GetNSString(IDS_CANCEL);
if (self.config->is_repeated_request()) {
message = l10n_util::GetNSString(IDS_IOS_OPEN_REPEATEDLY_ANOTHER_APP);
allowActionTitle =
l10n_util::GetNSString(IDS_IOS_OPEN_REPEATEDLY_ANOTHER_APP_ALLOW);
} else {
message = l10n_util::GetNSString(IDS_IOS_OPEN_IN_ANOTHER_APP);
allowActionTitle =
l10n_util::GetNSString(IDS_IOS_APP_LAUNCHER_OPEN_APP_BUTTON_LABEL);
}
[self.consumer setMessage:message];
__weak __typeof__(self) weakSelf = self;
[self.consumer setActions:@[
[AlertAction actionWithTitle:allowActionTitle
style:UIAlertActionStyleDefault
handler:^(AlertAction* action) {
__typeof__(self) strongSelf = weakSelf;
[strongSelf updateResponseAllowingAppLaunch:YES];
[strongSelf.delegate
stopDialogForMediator:strongSelf];
}],
[AlertAction actionWithTitle:rejectActionTitle
style:UIAlertActionStyleCancel
handler:^(AlertAction* action) {
__typeof__(self) strongSelf = weakSelf;
[strongSelf updateResponseAllowingAppLaunch:NO];
[strongSelf.delegate
stopDialogForMediator:strongSelf];
}],
]];
}
#pragma mark - Response helpers
// Sets the OverlayResponse. |allowAppLaunch| indicates whether the alert's
// allow button was tapped to allow the navigation to open in another app.
- (void)updateResponseAllowingAppLaunch:(BOOL)allowAppLaunch {
self.request->set_response(
OverlayResponse::CreateWithInfo<AppLauncherAlertOverlayResponseInfo>(
allowAppLaunch));
}
@end
// Copyright 2019 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/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_mediator.h"
#include "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/overlays/public/overlay_request.h"
#import "ios/chrome/browser/overlays/public/overlay_response.h"
#import "ios/chrome/browser/overlays/public/web_content_area/app_launcher_alert_overlay.h"
#import "ios/chrome/browser/ui/alert_view_controller/alert_action.h"
#import "ios/chrome/browser/ui/alert_view_controller/test/fake_alert_consumer.h"
#include "ios/chrome/grit/ios_strings.h"
#include "testing/gtest_mac.h"
#include "testing/platform_test.h"
#include "ui/base/l10n/l10n_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
class AppLauncherAlertOverlayMediatorTest : public PlatformTest {
protected:
AppLauncherAlertOverlayMediatorTest()
: consumer_([[FakeAlertConsumer alloc] init]) {
UpdateConsumer();
}
FakeAlertConsumer* consumer() const { return consumer_; }
// Setter for whether the test is for a repeated app launch request.
void set_is_repeated_request(bool is_repeated_request) {
if (is_repeated_request_ == is_repeated_request)
return;
is_repeated_request_ = is_repeated_request;
UpdateConsumer();
}
private:
// Instantiates |request_| with an OverlayRequest configured with an
// AppLauncherAlertOverlayRequestConfig set up using |is_repeated_request_|.
// Resets |mediator_| to a new AppLauncherAlertOverlayMediator created with
// |request_| and sets its consumer to |consumer_|.
void UpdateConsumer() {
request_ =
OverlayRequest::CreateWithConfig<AppLauncherAlertOverlayRequestConfig>(
is_repeated_request_);
mediator_ = [[AppLauncherAlertOverlayMediator alloc]
initWithRequest:request_.get()];
mediator_.consumer = consumer_;
}
FakeAlertConsumer* consumer_;
bool is_repeated_request_ = false;
std::unique_ptr<OverlayRequest> request_;
AppLauncherAlertOverlayMediator* mediator_ = nil;
};
// Tests that the consumer values are set correctly for the first app launch
// request.
TEST_F(AppLauncherAlertOverlayMediatorTest, FirstRequestAlertSetup) {
EXPECT_NSEQ(l10n_util::GetNSString(IDS_IOS_OPEN_IN_ANOTHER_APP),
consumer().message);
ASSERT_EQ(2U, consumer().actions.count);
EXPECT_EQ(UIAlertActionStyleDefault, consumer().actions[0].style);
EXPECT_NSEQ(
l10n_util::GetNSString(IDS_IOS_APP_LAUNCHER_OPEN_APP_BUTTON_LABEL),
consumer().actions[0].title);
EXPECT_EQ(UIAlertActionStyleCancel, consumer().actions[1].style);
EXPECT_NSEQ(l10n_util::GetNSString(IDS_CANCEL), consumer().actions[1].title);
}
// Tests that the consumer values are set correctly for the repeated app launch
// requests.
TEST_F(AppLauncherAlertOverlayMediatorTest, RepeatedRequestAlertSetup) {
set_is_repeated_request(true);
EXPECT_NSEQ(l10n_util::GetNSString(IDS_IOS_OPEN_REPEATEDLY_ANOTHER_APP),
consumer().message);
ASSERT_EQ(2U, consumer().actions.count);
EXPECT_EQ(UIAlertActionStyleDefault, consumer().actions[0].style);
EXPECT_NSEQ(l10n_util::GetNSString(IDS_IOS_OPEN_REPEATEDLY_ANOTHER_APP_ALLOW),
consumer().actions[0].title);
EXPECT_EQ(UIAlertActionStyleCancel, consumer().actions[1].style);
EXPECT_NSEQ(l10n_util::GetNSString(IDS_CANCEL), consumer().actions[1].title);
}
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#import "ios/chrome/browser/ui/overlays/web_content_area/web_content_area_supported_overlay_coordinator_classes.h" #import "ios/chrome/browser/ui/overlays/web_content_area/web_content_area_supported_overlay_coordinator_classes.h"
#import "ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_coordinator.h"
#import "ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_coordinator.h" #import "ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_coordinator.h"
#import "ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_alert_overlay_coordinator.h" #import "ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_alert_overlay_coordinator.h"
#import "ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_confirmation_overlay_coordinator.h" #import "ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_confirmation_overlay_coordinator.h"
...@@ -16,7 +17,8 @@ ...@@ -16,7 +17,8 @@
namespace web_content_area { namespace web_content_area {
NSArray<Class>* GetSupportedOverlayCoordinatorClasses() { NSArray<Class>* GetSupportedOverlayCoordinatorClasses() {
return @ [[HTTPAuthDialogOverlayCoordinator class], return @ [[AppLauncherAlertOverlayCoordinator class],
[HTTPAuthDialogOverlayCoordinator class],
[JavaScriptAlertOverlayCoordinator class], [JavaScriptAlertOverlayCoordinator class],
[JavaScriptConfirmationOverlayCoordinator class], [JavaScriptConfirmationOverlayCoordinator class],
[JavaScriptPromptOverlayCoordinator class]]; [JavaScriptPromptOverlayCoordinator class]];
......
...@@ -226,6 +226,7 @@ test("ios_chrome_unittests") { ...@@ -226,6 +226,7 @@ test("ios_chrome_unittests") {
"//ios/chrome/browser/ui/omnibox/popup:unit_tests", "//ios/chrome/browser/ui/omnibox/popup:unit_tests",
"//ios/chrome/browser/ui/open_in:unit_tests", "//ios/chrome/browser/ui/open_in:unit_tests",
"//ios/chrome/browser/ui/overlays:unit_tests", "//ios/chrome/browser/ui/overlays:unit_tests",
"//ios/chrome/browser/ui/overlays/web_content_area/app_launcher:unit_tests",
"//ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs:unit_tests", "//ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs:unit_tests",
"//ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs:unit_tests", "//ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs:unit_tests",
"//ios/chrome/browser/ui/payments:unit_tests", "//ios/chrome/browser/ui/payments: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