Commit 069fc4a2 authored by Eugene But's avatar Eugene But Committed by Commit Bot

Add and integrate DownloadManagerCoordinator class.

This is the final CL for New Download Manager UI skeleton. This adds
coordinator class and creates DownloadManagerTabHelper object in BVC.
New Download Manager feature is still not enabled in flags.

Design doc: https://goto.google.com/ios-chrome-download

Bug: 791806
Cq-Include-Trybots: master.tryserver.chromium.mac:ios-simulator-cronet;master.tryserver.chromium.mac:ios-simulator-full-configs
Change-Id: I8fcd0646762ef90465aa722622102a5e2b23c413
Reviewed-on: https://chromium-review.googlesource.com/889399Reviewed-by: default avatarMark Cogan <marq@chromium.org>
Reviewed-by: default avatarSylvain Defresne <sdefresne@chromium.org>
Commit-Queue: Eugene But <eugenebut@chromium.org>
Cr-Commit-Position: refs/heads/master@{#532923}
parent 49c66f63
...@@ -18,7 +18,7 @@ class DownloadTask; ...@@ -18,7 +18,7 @@ class DownloadTask;
// Informs the delegate that a DownloadTask was created. // Informs the delegate that a DownloadTask was created.
- (void)downloadManagerTabHelper:(nonnull DownloadManagerTabHelper*)tabHelper - (void)downloadManagerTabHelper:(nonnull DownloadManagerTabHelper*)tabHelper
didCreateDownload:(nonnull web::DownloadTask*)download didCreateDownload:(nonnull web::DownloadTask*)download
webStateIsVisible:(BOOL)vebStateIsVisible; webStateIsVisible:(BOOL)webStateIsVisible;
// Informs the delegate that a DownloadTask was updated (download progress was // Informs the delegate that a DownloadTask was updated (download progress was
// changed, or download has reached a terminal state). // changed, or download has reached a terminal state).
......
...@@ -60,6 +60,7 @@ ...@@ -60,6 +60,7 @@
#include "ios/chrome/browser/browser_state/chrome_browser_state.h" #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#include "ios/chrome/browser/chrome_url_constants.h" #include "ios/chrome/browser/chrome_url_constants.h"
#include "ios/chrome/browser/chrome_url_util.h" #include "ios/chrome/browser/chrome_url_util.h"
#import "ios/chrome/browser/download/download_manager_tab_helper.h"
#import "ios/chrome/browser/download/pass_kit_tab_helper.h" #import "ios/chrome/browser/download/pass_kit_tab_helper.h"
#include "ios/chrome/browser/experimental_flags.h" #include "ios/chrome/browser/experimental_flags.h"
#import "ios/chrome/browser/favicon/favicon_loader.h" #import "ios/chrome/browser/favicon/favicon_loader.h"
...@@ -139,6 +140,7 @@ ...@@ -139,6 +140,7 @@
#import "ios/chrome/browser/ui/context_menu/context_menu_coordinator.h" #import "ios/chrome/browser/ui/context_menu/context_menu_coordinator.h"
#import "ios/chrome/browser/ui/dialogs/dialog_presenter.h" #import "ios/chrome/browser/ui/dialogs/dialog_presenter.h"
#import "ios/chrome/browser/ui/dialogs/java_script_dialog_presenter_impl.h" #import "ios/chrome/browser/ui/dialogs/java_script_dialog_presenter_impl.h"
#import "ios/chrome/browser/ui/download/download_manager_coordinator.h"
#import "ios/chrome/browser/ui/download/legacy_download_manager_controller.h" #import "ios/chrome/browser/ui/download/legacy_download_manager_controller.h"
#import "ios/chrome/browser/ui/download/pass_kit_coordinator.h" #import "ios/chrome/browser/ui/download/pass_kit_coordinator.h"
#import "ios/chrome/browser/ui/elements/activity_overlay_coordinator.h" #import "ios/chrome/browser/ui/elements/activity_overlay_coordinator.h"
...@@ -599,6 +601,9 @@ NSString* const kBrowserViewControllerSnackbarCategory = ...@@ -599,6 +601,9 @@ NSString* const kBrowserViewControllerSnackbarCategory =
// Coordinator for the External Search UI. // Coordinator for the External Search UI.
ExternalSearchCoordinator* _externalSearchCoordinator; ExternalSearchCoordinator* _externalSearchCoordinator;
// Coordinator for the Download Manager UI.
DownloadManagerCoordinator* _downloadManagerCoordinator;
// Coordinator for the language selection UI. // Coordinator for the language selection UI.
LanguageSelectionCoordinator* _languageSelectionCoordinator; LanguageSelectionCoordinator* _languageSelectionCoordinator;
...@@ -986,6 +991,11 @@ applicationCommandEndpoint:(id<ApplicationCommands>)applicationCommandEndpoint { ...@@ -986,6 +991,11 @@ applicationCommandEndpoint:(id<ApplicationCommands>)applicationCommandEndpoint {
_snackbarCoordinator.dispatcher = _dispatcher; _snackbarCoordinator.dispatcher = _dispatcher;
[_snackbarCoordinator start]; [_snackbarCoordinator start];
_downloadManagerCoordinator =
[[DownloadManagerCoordinator alloc] initWithBaseViewController:self];
_downloadManagerCoordinator.presenter =
[[VerticalAnimationContainer alloc] init];
_languageSelectionCoordinator = _languageSelectionCoordinator =
[[LanguageSelectionCoordinator alloc] initWithBaseViewController:self]; [[LanguageSelectionCoordinator alloc] initWithBaseViewController:self];
_languageSelectionCoordinator.presenter = _languageSelectionCoordinator.presenter =
...@@ -2927,6 +2937,8 @@ bubblePresenterForFeature:(const base::Feature&)feature ...@@ -2927,6 +2937,8 @@ bubblePresenterForFeature:(const base::Feature&)feature
AppLauncherTabHelper::CreateForWebState( AppLauncherTabHelper::CreateForWebState(
tab.webState, [[ExternalAppsLaunchPolicyDecider alloc] init], tab.webState, [[ExternalAppsLaunchPolicyDecider alloc] init],
_appLauncherCoordinator); _appLauncherCoordinator);
DownloadManagerTabHelper::CreateForWebState(tab.webState,
_downloadManagerCoordinator);
// The language detection helper accepts a callback from the translate // The language detection helper accepts a callback from the translate
// client, so must be created after it. // client, so must be created after it.
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
source_set("download") { source_set("download") {
configs += [ "//build/config/compiler:enable_arc" ] configs += [ "//build/config/compiler:enable_arc" ]
sources = [ sources = [
"download_manager_coordinator.h",
"download_manager_coordinator.mm",
"download_manager_view_controller.h", "download_manager_view_controller.h",
"download_manager_view_controller.mm", "download_manager_view_controller.mm",
"legacy_download_manager_controller.h", "legacy_download_manager_controller.h",
...@@ -30,6 +32,7 @@ source_set("download") { ...@@ -30,6 +32,7 @@ source_set("download") {
"//ios/chrome/browser/ui/alert_coordinator", "//ios/chrome/browser/ui/alert_coordinator",
"//ios/chrome/browser/ui/colors", "//ios/chrome/browser/ui/colors",
"//ios/chrome/browser/ui/coordinators:chrome_coordinators", "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
"//ios/chrome/browser/ui/presenters",
"//ios/chrome/browser/ui/util", "//ios/chrome/browser/ui/util",
"//ios/chrome/browser/web:web_internal", "//ios/chrome/browser/web:web_internal",
"//ios/third_party/material_components_ios", "//ios/third_party/material_components_ios",
...@@ -47,6 +50,7 @@ source_set("unit_tests") { ...@@ -47,6 +50,7 @@ source_set("unit_tests") {
configs += [ "//build/config/compiler:enable_arc" ] configs += [ "//build/config/compiler:enable_arc" ]
testonly = true testonly = true
sources = [ sources = [
"download_manager_coordinator_unittest.mm",
"download_manager_view_controller_unittest.mm", "download_manager_view_controller_unittest.mm",
"legacy_download_manager_controller_unittest.mm", "legacy_download_manager_controller_unittest.mm",
"pass_kit_coordinator_unittest.mm", "pass_kit_coordinator_unittest.mm",
......
// 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_DOWNLOAD_DOWNLOAD_MANAGER_COORDINATOR_H_
#define IOS_CHROME_BROWSER_UI_DOWNLOAD_DOWNLOAD_MANAGER_COORDINATOR_H_
#import "ios/chrome/browser/download/download_manager_tab_helper_delegate.h"
#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
namespace web {
class DownloadTask;
} // namespace web
@protocol ContainedPresenter;
// Coordinates presentation of Download Manager UI.
@interface DownloadManagerCoordinator
: ChromeCoordinator<DownloadManagerTabHelperDelegate>
// Presents the receiver's view controller.
@property(nonatomic) id<ContainedPresenter> presenter;
// YES if presentation should be animated. Default is NO.
@property(nonatomic) BOOL animatesPresentation;
// Download Manager supports only one download task at a time. Set to null when
// stop method is called.
@property(nonatomic) web::DownloadTask* downloadTask;
@end
#endif // IOS_CHROME_BROWSER_UI_DOWNLOAD_DOWNLOAD_MANAGER_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/download/download_manager_coordinator.h"
#include <memory>
#include "base/files/file_util.h"
#import "base/logging.h"
#import "base/mac/bind_objc_block.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task_scheduler/post_task.h"
#include "base/task_scheduler/task_traits.h"
#include "ios/chrome/browser/download/download_directory_util.h"
#import "ios/chrome/browser/download/download_manager_tab_helper.h"
#import "ios/chrome/browser/ui/download/download_manager_view_controller.h"
#import "ios/chrome/browser/ui/presenters/contained_presenter.h"
#import "ios/chrome/browser/ui/presenters/contained_presenter_delegate.h"
#import "ios/web/public/download/download_task.h"
#include "ios/web/public/web_thread.h"
#include "net/url_request/url_fetcher_response_writer.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
using web::WebThread;
@interface DownloadManagerCoordinator ()<
ContainedPresenterDelegate,
DownloadManagerViewControllerDelegate> {
// View controller for presenting Download Manager UI.
DownloadManagerViewController* _viewController;
// View controller for presenting "Open In.." dialog.
UIDocumentInteractionController* _openInController;
}
@end
@implementation DownloadManagerCoordinator
@synthesize presenter = _presenter;
@synthesize animatesPresentation = _animatesPresentation;
@synthesize downloadTask = _downloadTask;
- (void)start {
DCHECK(self.presenter);
_viewController = [[DownloadManagerViewController alloc] init];
_viewController.delegate = self;
[self updateViewController];
self.presenter.baseViewController = self.baseViewController;
self.presenter.presentedViewController = _viewController;
self.presenter.delegate = self;
[self.presenter prepareForPresentation];
[self.presenter presentAnimated:self.animatesPresentation];
}
- (void)stop {
if (_viewController) {
[self.presenter dismissAnimated:YES];
_viewController = nil;
}
_downloadTask = nullptr;
}
#pragma mark - DownloadManagerTabHelperDelegate
- (void)downloadManagerTabHelper:(nonnull DownloadManagerTabHelper*)tabHelper
didCreateDownload:(nonnull web::DownloadTask*)download
webStateIsVisible:(BOOL)vebStateIsVisible {
if (!vebStateIsVisible) {
// Do nothing if a background Tab requested download UI presentation.
return;
}
_downloadTask = download;
self.animatesPresentation = YES;
[self start];
}
- (void)downloadManagerTabHelper:(nonnull DownloadManagerTabHelper*)tabHelper
didUpdateDownload:(nonnull web::DownloadTask*)download {
if (_downloadTask != download) {
// Do nothing if download was updated for a background Tab.
return;
}
[self updateViewController];
}
- (void)downloadManagerTabHelper:(nonnull DownloadManagerTabHelper*)tabHelper
didHideDownload:(nonnull web::DownloadTask*)download {
if (!_downloadTask) {
// TODO(crbug.com/805653): This callback can be called multiple times.
return;
}
DCHECK_EQ(_downloadTask, download);
[self stop];
}
- (void)downloadManagerTabHelper:(nonnull DownloadManagerTabHelper*)tabHelper
didShowDownload:(nonnull web::DownloadTask*)download {
DCHECK_NE(_downloadTask, download);
_downloadTask = download;
self.animatesPresentation = NO;
[self start];
}
#pragma mark - ContainedPresenterDelegate
- (void)containedPresenterDidDismiss:(id<ContainedPresenter>)presenter {
DCHECK(presenter == self.presenter);
}
#pragma mark - DownloadManagerViewControllerDelegate
- (void)downloadManagerViewControllerDidClose:
(DownloadManagerViewController*)controller {
_downloadTask->Cancel();
[self stop];
}
- (void)downloadManagerViewControllerDidStartDownload:
(DownloadManagerViewController*)controller {
[self startDownload];
}
- (void)downloadManagerViewController:(DownloadManagerViewController*)controller
presentOpenInMenuWithLayoutGuide:(UILayoutGuide*)layoutGuide {
base::FilePath path =
_downloadTask->GetResponseWriter()->AsFileWriter()->file_path();
NSURL* URL = [NSURL fileURLWithPath:base::SysUTF8ToNSString(path.value())];
_openInController =
[UIDocumentInteractionController interactionControllerWithURL:URL];
BOOL menuShown =
[_openInController presentOpenInMenuFromRect:layoutGuide.layoutFrame
inView:layoutGuide.owningView
animated:YES];
DCHECK(menuShown);
}
#pragma mark - Private
// Updates presented view controller with web::DownloadTask data.
- (void)updateViewController {
_viewController.state = [self downloadManagerState];
_viewController.countOfBytesReceived = _downloadTask->GetReceivedBytes();
_viewController.countOfBytesExpectedToReceive =
_downloadTask->GetTotalBytes();
_viewController.fileName =
base::SysUTF16ToNSString(_downloadTask->GetSuggestedFilename());
}
// Returns DownloadManagerState for the current download task.
- (DownloadManagerState)downloadManagerState {
switch (_downloadTask->GetState()) {
case web::DownloadTask::State::kNotStarted:
return kDownloadManagerStateNotStarted;
case web::DownloadTask::State::kInProgress:
return kDownloadManagerStateInProgress;
case web::DownloadTask::State::kComplete:
return _downloadTask->GetErrorCode() ? kDownloadManagerStateFailed
: kDownloadManagerStateSuceeded;
case web::DownloadTask::State::kCancelled:
// Download Manager should dismiss the UI after download cancellation.
NOTREACHED();
return kDownloadManagerStateNotStarted;
}
}
// Asynchronously starts download operation.
- (void)startDownload {
base::FilePath downloadDir;
if (!GetDownloadsDirectory(&downloadDir)) {
[self didFailFileWriterCreation];
return;
}
// Download will start once writer is created by background task, however it
// OK to change view controller state now to preven further user interactions
// with "Start Download" button.
_viewController.state = kDownloadManagerStateInProgress;
base::string16 suggestedFileName = _downloadTask->GetSuggestedFilename();
__weak DownloadManagerCoordinator* weakSelf = self;
base::PostTaskWithTraits(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
base::BindBlockArc(^{
if (!weakSelf)
return;
if (!base::CreateDirectory(downloadDir)) {
WebThread::PostTask(WebThread::UI, FROM_HERE, base::BindBlockArc(^{
[weakSelf didFailFileWriterCreation];
}));
return;
}
base::FilePath downloadFilePath =
downloadDir.Append(base::UTF16ToUTF8(suggestedFileName));
auto taskRunner = base::CreateSequencedTaskRunnerWithTraits(
{base::MayBlock(), base::TaskPriority::BACKGROUND});
__block auto writer = std::make_unique<net::URLFetcherFileWriter>(
taskRunner, downloadFilePath);
WebThread::PostTask(WebThread::UI, FROM_HERE, base::BindBlockArc(^{
writer->Initialize(base::BindBlockArc(^(int error) {
DownloadManagerCoordinator* strongSelf = weakSelf;
if (!strongSelf)
return;
if (!error) {
strongSelf.downloadTask->Start(std::move(writer));
} else {
[strongSelf didFailFileWriterCreation];
}
}));
}));
}));
}
// Called when coordinator failed to create file writer.
- (void)didFailFileWriterCreation {
_viewController.state = kDownloadManagerStateFailed;
}
@end
...@@ -7,6 +7,8 @@ source_set("fakes") { ...@@ -7,6 +7,8 @@ source_set("fakes") {
testonly = true testonly = true
sources = [ sources = [
"fake_contained_presenter.h",
"fake_contained_presenter.mm",
"fake_download_manager_tab_helper_delegate.h", "fake_download_manager_tab_helper_delegate.h",
"fake_download_manager_tab_helper_delegate.mm", "fake_download_manager_tab_helper_delegate.mm",
"fake_pass_kit_tab_helper_delegate.h", "fake_pass_kit_tab_helper_delegate.h",
...@@ -14,7 +16,9 @@ source_set("fakes") { ...@@ -14,7 +16,9 @@ source_set("fakes") {
] ]
deps = [ deps = [
"//base",
"//ios/chrome/browser/download", "//ios/chrome/browser/download",
"//ios/chrome/browser/ui/presenters",
"//ios/web/public", "//ios/web/public",
"//ios/web/public/download", "//ios/web/public/download",
] ]
......
// 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_TEST_FAKES_FAKE_CONTAINED_PRESENTER_H_
#define IOS_CHROME_TEST_FAKES_FAKE_CONTAINED_PRESENTER_H_
#import <Foundation/Foundation.h>
#import "ios/chrome/browser/ui/presenters/contained_presenter.h"
// ContainedPresenter used for testing.
@interface FakeContainedPresenter : NSObject<ContainedPresenter>
// YES if |presentAnimated:| was called with YES.
@property(nonatomic, assign) BOOL lastPresentationWasAnimated;
@end
#endif // IOS_CHROME_TEST_FAKES_FAKE_CONTAINED_PRESENTER_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/test/fakes/fake_contained_presenter.h"
#include "base/logging.h"
#import "ios/chrome/browser/ui/presenters/contained_presenter_delegate.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@implementation FakeContainedPresenter
@synthesize baseViewController = _baseViewController;
@synthesize presentedViewController = _presentedViewController;
@synthesize delegate = _delegate;
@synthesize lastPresentationWasAnimated = _lastPresentationWasAnimated;
- (void)prepareForPresentation {
DCHECK(self.presentedViewController);
DCHECK(self.baseViewController);
[self.baseViewController addChildViewController:self.presentedViewController];
[self.baseViewController.view addSubview:self.presentedViewController.view];
[self.presentedViewController.view updateConstraints];
[self.presentedViewController.view layoutIfNeeded];
[self.presentedViewController
didMoveToParentViewController:self.baseViewController];
}
- (void)presentAnimated:(BOOL)animated {
self.lastPresentationWasAnimated = animated;
}
- (void)dismissAnimated:(BOOL)animated {
DCHECK(self.presentedViewController);
[self.presentedViewController willMoveToParentViewController:nil];
[self.presentedViewController.view removeFromSuperview];
[self.presentedViewController removeFromParentViewController];
[self.delegate containedPresenterDidDismiss:self];
}
@end
...@@ -18,8 +18,8 @@ ...@@ -18,8 +18,8 @@
- (void)downloadManagerTabHelper:(nonnull DownloadManagerTabHelper*)tabHelper - (void)downloadManagerTabHelper:(nonnull DownloadManagerTabHelper*)tabHelper
didCreateDownload:(nonnull web::DownloadTask*)download didCreateDownload:(nonnull web::DownloadTask*)download
webStateIsVisible:(BOOL)vebStateIsVisible { webStateIsVisible:(BOOL)webStateIsVisible {
if (vebStateIsVisible) { if (webStateIsVisible) {
_state = std::make_unique<web::DownloadTask::State>(download->GetState()); _state = std::make_unique<web::DownloadTask::State>(download->GetState());
} }
} }
......
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