blob: 8d9d3634c3dbf74c2b557408f1732a90143282e1 [file] [log] [blame]
// Copyright 2016 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/alert_coordinator/loading_alert_coordinator.h"
#import <UIKit/UIKit.h>
#include "base/ios/block_types.h"
#import "base/mac/scoped_block.h"
#include "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/ui/material_components/activity_indicator.h"
#import "ios/chrome/browser/ui/uikit_ui_util.h"
#import "ios/third_party/material_components_ios/src/components/ActivityIndicator/src/MaterialActivityIndicator.h"
#import "ios/third_party/material_components_ios/src/components/Buttons/src/MaterialButtons.h"
#import "ios/third_party/material_components_ios/src/components/Dialogs/src/MaterialDialogs.h"
#import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoFontLoader.h"
#include "ui/base/l10n/l10n_util_mac.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// MDC constraints.
const CGFloat kMDCPadding = 24.0;
const CGFloat kMDCMargin = 40.0;
const CGFloat kButtonPadding = 8.0;
// Size of the activity indicator.
const CGFloat kActivityIndicatorPadding = 50.0;
// Constant for the title
const int kTitleLabelFontColor = 0x333333;
const int kTitleLabelFontSize = 16;
// Maximum size of the dialog
const CGFloat kPrefWidth = 330;
const CGFloat kPrefHeight = 300;
} // namespace
@interface LoadingAlertCoordinator () {
// Title of the alert.
NSString* _title;
// Callback for the cancel button.
ProceduralBlock _cancelHandler;
// View Controller which will be displayed on |baseViewController|.
UIViewController* _presentedViewController;
}
// Callback called when the cancel button is pressed.
- (void)cancelCallback;
@end
// View Controller handling the layout of the dialog.
@interface LoadingViewController : UIViewController {
// Title of the dialog.
NSString* _title;
// View containing the elements of the dialog.
UIView* _contentView;
// Coordinator used for the cancel callback.
__weak LoadingAlertCoordinator* _coordinator;
// Transitioning delegate for this ViewController.
MDCDialogTransitionController* _transitionDelegate;
}
// Initializes with the |title| of the dialog and the |coordinator| which will
// be notified if the cancel callback occurs.
- (instancetype)initWithTitle:(NSString*)title
coordinator:(LoadingAlertCoordinator*)coordinator;
// Returns the maximum possible width for the dialog, taking into account the
// MDC constraints.
- (CGFloat)maxDialogWidth;
// Callback for the cancel button, calling the coordinator's callback.
- (void)cancelCallback;
@end
@implementation LoadingViewController
- (instancetype)initWithTitle:(NSString*)title
coordinator:(LoadingAlertCoordinator*)coordinator {
self = [super initWithNibName:nil bundle:nil];
if (self) {
_title = [title copy];
_coordinator = coordinator;
self.modalPresentationStyle = UIModalPresentationCustom;
_transitionDelegate = [[MDCDialogTransitionController alloc] init];
self.transitioningDelegate = _transitionDelegate;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.preferredContentSize = CGSizeMake([self maxDialogWidth], kPrefHeight);
self.view.backgroundColor = [UIColor whiteColor];
// Cancel button.
NSString* cancelTitle = l10n_util::GetNSString(IDS_CANCEL);
MDCFlatButton* cancelButton = [[MDCFlatButton alloc] init];
[cancelButton sizeToFit];
[cancelButton setCustomTitleColor:[UIColor blackColor]];
[cancelButton setTitle:cancelTitle forState:UIControlStateNormal];
[cancelButton addTarget:self
action:@selector(cancelCallback)
forControlEvents:UIControlEventTouchUpInside];
// Activity indicator.
MDCActivityIndicator* activityIndicator =
[[MDCActivityIndicator alloc] initWithFrame:CGRectZero];
[activityIndicator setCycleColors:ActivityIndicatorBrandedCycleColors()];
[activityIndicator startAnimating];
// Title.
NSMutableDictionary* attrsDictionary = [NSMutableDictionary
dictionaryWithObject:[[MDFRobotoFontLoader sharedInstance]
mediumFontOfSize:kTitleLabelFontSize]
forKey:NSFontAttributeName];
[attrsDictionary setObject:UIColorFromRGB(kTitleLabelFontColor)
forKey:NSForegroundColorAttributeName];
NSMutableAttributedString* string =
[[NSMutableAttributedString alloc] initWithString:_title
attributes:attrsDictionary];
UILabel* title = [[UILabel alloc] initWithFrame:CGRectZero];
title.attributedText = string;
// Content view.
_contentView = [[UIView alloc] initWithFrame:CGRectZero];
// Constraints.
[activityIndicator setTranslatesAutoresizingMaskIntoConstraints:NO];
[cancelButton setTranslatesAutoresizingMaskIntoConstraints:NO];
[title setTranslatesAutoresizingMaskIntoConstraints:NO];
[_contentView setTranslatesAutoresizingMaskIntoConstraints:NO];
[_contentView addSubview:title];
[_contentView addSubview:activityIndicator];
[_contentView addSubview:cancelButton];
[[self view] addSubview:_contentView];
NSDictionary* viewsDictionary = @{
@"spinner" : activityIndicator,
@"cancel" : cancelButton,
@"title" : title,
@"contentView" : _contentView
};
NSDictionary* metrics = @{
@"padding" : @(kMDCPadding),
@"buttonPadding" : @(kButtonPadding),
@"spinnerButtonPadding" : @(kButtonPadding + kMDCPadding),
@"spinnerPadding" : @(kActivityIndicatorPadding)
};
NSString* verticalConstaint = @"V:|-(padding)-[title]-(spinnerPadding)-["
@"spinner]-(spinnerButtonPadding)-[cancel]-("
@"buttonPadding)-|";
NSArray* constraints = @[
verticalConstaint, @"H:|-(>=padding)-[title]-(>=padding)-|",
@"H:|-[spinner]-|", @"H:[cancel]-(buttonPadding)-|", @"V:|[contentView]",
@"H:|[contentView]|"
];
ApplyVisualConstraintsWithMetricsAndOptions(constraints, viewsDictionary,
metrics, 0);
[_contentView addConstraint:[NSLayoutConstraint
constraintWithItem:title
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:_contentView
attribute:NSLayoutAttributeCenterX
multiplier:1.f
constant:0.f]];
}
- (CGFloat)maxDialogWidth {
CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width;
return MIN(kPrefWidth, screenWidth - 2 * kMDCMargin);
}
- (void)viewDidLayoutSubviews {
CGSize currentSize =
CGSizeMake([self maxDialogWidth], [_contentView frame].size.height);
self.preferredContentSize = currentSize;
}
- (void)cancelCallback {
[_coordinator cancelCallback];
}
@end
@implementation LoadingAlertCoordinator
- (instancetype)initWithBaseViewController:(UIViewController*)viewController {
NOTREACHED();
return nil;
}
- (instancetype)initWithBaseViewController:(UIViewController*)viewController
title:(NSString*)title
cancelHandler:(ProceduralBlock)cancelHandler {
self = [super initWithBaseViewController:viewController];
if (self) {
_title = [title copy];
_cancelHandler = cancelHandler;
}
return self;
}
- (void)start {
_presentedViewController =
[[LoadingViewController alloc] initWithTitle:_title coordinator:self];
[self.baseViewController presentViewController:_presentedViewController
animated:YES
completion:nil];
}
- (void)stop {
[[_presentedViewController presentingViewController]
dismissViewControllerAnimated:NO
completion:nil];
_presentedViewController = nil;
_cancelHandler = nil;
}
- (void)cancelCallback {
if (_cancelHandler)
_cancelHandler();
[self stop];
}
@end