| // Copyright 2015 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/sad_tab/sad_tab_view.h" |
| |
| #include "base/metrics/histogram_macros.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "components/grit/components_scaled_resources.h" |
| #include "components/strings/grit/components_strings.h" |
| #include "components/ui_metrics/sadtab_metrics_types.h" |
| #include "ios/chrome/browser/chrome_url_constants.h" |
| #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h" |
| #import "ios/chrome/browser/ui/commands/application_commands.h" |
| #import "ios/chrome/browser/ui/url_loader.h" |
| #import "ios/chrome/browser/ui/util/label_link_controller.h" |
| #include "ios/chrome/browser/ui/util/rtl_geometry.h" |
| #import "ios/chrome/browser/ui/util/uikit_ui_util.h" |
| #import "ios/third_party/material_components_ios/src/components/Buttons/src/MaterialButtons.h" |
| #import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h" |
| #include "ios/web/public/browser_state.h" |
| #include "ios/web/public/navigation_manager.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "url/gurl.h" |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| namespace { |
| // Color constants. |
| const CGFloat kBackgroundColorBrightness = 247.0f / 255.0f; |
| const CGFloat kTitleLabelTextColorBrightness = 22.0f / 255.0f; |
| const CGFloat kGeneralTextColorBrightness = 80.0f / 255.0f; |
| // Layout constants. |
| const UIEdgeInsets kLayoutInsets = {24.0f, 24.0f, 24.0f, 24.0f}; |
| const CGFloat kLayoutBoundsMaxWidth = 600.0f; |
| const CGFloat kContainerViewLandscapeTopPadding = 22.0f; |
| const CGFloat kTitleLabelTopPadding = 26.0f; |
| const CGFloat kMessageTextViewTopPadding = 16.0f; |
| const CGFloat kFooterLabelTopPadding = 16.0f; |
| const CGFloat kActionButtonHeight = 48.0f; |
| const CGFloat kActionButtonTopPadding = 16.0f; |
| // Label font sizes. |
| const CGFloat kTitleLabelFontSize = 23.0f; |
| const CGFloat kMessageTextViewFontSize = 14.0f; |
| const CGFloat kFooterLabelFontSize = 14.0f; |
| // Feedback message bullet indentation. |
| const CGFloat kBulletIndent = 17.0f; // Left margin to bullet indent. |
| const CGFloat kBulletedTextIndent = 15.0f; // Bullet to text indent. |
| // Format for bulleted line (<tab><bullet><tab><string>). |
| NSString* const kMessageTextViewBulletPrefix = @"\t\u2022\t"; |
| // Separator for each new bullet line. |
| NSString* const kMessageTextViewBulletSuffix = @"\n"; |
| // "<RTL Begin Indicator><NSString Token><RTL End Indicator>". |
| NSString* const kMessageTextViewBulletRTLFormat = @"\u202E%@\u202C"; |
| } // namespace |
| |
| @interface SadTabView () { |
| UITextView* _messageTextView; |
| MDCFlatButton* _actionButton; |
| } |
| |
| // YES if the SadTab UI is displayed in Off The Record browsing mode. |
| @property(nonatomic, readonly, getter=isOffTheRecord) BOOL offTheRecord; |
| // Container view that displays all other subviews. |
| @property(nonatomic, readonly, strong) UIView* containerView; |
| // Displays the Sad Tab face. |
| @property(nonatomic, readonly, strong) UIImageView* imageView; |
| // Displays the Sad Tab title. |
| @property(nonatomic, readonly, strong) UILabel* titleLabel; |
| // Displays the Sad Tab footer message (including a link to more help). |
| @property(nonatomic, readonly, strong) UILabel* footerLabel; |
| // Provides Link functionality to the footerLabel. |
| @property(nonatomic, readonly, strong) |
| LabelLinkController* footerLabelLinkController; |
| // The bounds of |containerView|, with a height updated to CGFLOAT_MAX to allow |
| // text to be laid out using as many lines as necessary. |
| @property(nonatomic, readonly) CGRect containerBounds; |
| |
| // Subview layout methods. Must be called in the following order, as subsequent |
| // layouts reference the values set in previous functions. |
| - (void)layoutImageView; |
| - (void)layoutTitleLabel; |
| - (void)layoutMessageTextView; |
| - (void)layoutFooterLabel; |
| - (void)layoutActionButton; |
| - (void)layoutContainerView; |
| |
| // Takes an array of strings and bulletizes them into a single multi-line string |
| // for display. The string has NSParagraphStyle attributes for tab alignment. |
| + (nonnull NSAttributedString*)bulletedAttributedStringFromStrings: |
| (nonnull NSArray<NSString*>*)strings; |
| |
| // Returns the appropriate title for the view, e.g. 'Aw Snap!'. |
| - (nonnull NSString*)titleLabelText; |
| // Returns the appropriate message text body for the view, this will typically |
| // be a larger body of explanation or help text. Returns an attributed string |
| // to allow for text formatting and layout to be applied to the returned string. |
| - (nonnull NSAttributedString*)messageTextViewAttributedText; |
| // Returns the full footer string containing a link, intended to be the last |
| // piece of text. |
| - (nonnull NSString*)footerLabelText; |
| // Returns the substring of the footer string which is to be the underlined link |
| // text. (May be the entire footer label string). |
| - (nonnull NSString*)footerLinkText; |
| // Returns the string to be used for the main action button. |
| - (nonnull NSString*)buttonText; |
| |
| // Attaches a link controller to |label|, finding the |linkString| |
| // within the |label| text to use as the link. |
| - (void)attachLinkControllerToLabel:(nonnull UILabel*)label |
| forLinkText:(nonnull NSString*)linkText; |
| |
| // The action selector for |_actionButton|. |
| - (void)handleActionButtonTapped; |
| |
| // Returns the desired background color. |
| + (UIColor*)sadTabBackgroundColor; |
| |
| @end |
| |
| #pragma mark - SadTabView |
| |
| @implementation SadTabView |
| |
| @synthesize offTheRecord = _offTheRecord; |
| @synthesize imageView = _imageView; |
| @synthesize containerView = _containerView; |
| @synthesize titleLabel = _titleLabel; |
| @synthesize footerLabel = _footerLabel; |
| @synthesize footerLabelLinkController = _footerLabelLinkController; |
| @synthesize mode = _mode; |
| @synthesize delegate = _delegate; |
| |
| - (instancetype)initWithMode:(SadTabViewMode)mode |
| offTheRecord:(BOOL)offTheRecord { |
| self = [super initWithFrame:CGRectZero]; |
| if (self) { |
| _mode = mode; |
| _offTheRecord = offTheRecord; |
| self.backgroundColor = [[self class] sadTabBackgroundColor]; |
| } |
| return self; |
| } |
| |
| #pragma mark - Text Utilities |
| |
| + (nonnull NSAttributedString*)bulletedAttributedStringFromStrings: |
| (nonnull NSArray<NSString*>*)strings { |
| // Ensures the bullet string is appropriately directional. |
| NSString* directionalBulletPrefix = |
| base::i18n::IsRTL() |
| ? [NSString stringWithFormat:kMessageTextViewBulletRTLFormat, |
| kMessageTextViewBulletPrefix] |
| : kMessageTextViewBulletPrefix; |
| |
| // Assemble the strings into a single string with each line preceded by a |
| // bullet point. |
| NSMutableString* bulletedString = [[NSMutableString alloc] init]; |
| for (NSString* string in strings) { |
| // If content line has been added to the bulletedString already, ensure the |
| // suffix is applied, otherwise don't (e.g. don't for the first item). |
| NSArray* newStringArray = |
| bulletedString.length |
| ? @[ kMessageTextViewBulletSuffix, directionalBulletPrefix, string ] |
| : @[ directionalBulletPrefix, string ]; |
| [bulletedString appendString:[newStringArray componentsJoinedByString:@""]]; |
| } |
| |
| // Prepare a paragraph style that will allow for the alignment of lines of |
| // text separately to the alignment of the bullet-points. |
| NSMutableParagraphStyle* paragraphStyle = |
| [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; |
| paragraphStyle.tabStops = @[ |
| [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentNatural |
| location:kBulletIndent |
| options:@{}], |
| [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentNatural |
| location:kBulletIndent + kBulletedTextIndent |
| options:@{}] |
| ]; |
| paragraphStyle.firstLineHeadIndent = 0.0f; |
| paragraphStyle.headIndent = kBulletIndent + kBulletedTextIndent; |
| |
| // Use the paragraph style on the full string. |
| NSAttributedString* bulletedAttributedString = [[NSAttributedString alloc] |
| initWithString:bulletedString |
| attributes:@{NSParagraphStyleAttributeName : paragraphStyle}]; |
| |
| DCHECK(bulletedAttributedString); |
| return bulletedAttributedString; |
| } |
| |
| #pragma mark - Label Text |
| |
| - (nonnull NSString*)titleLabelText { |
| NSString* label = nil; |
| switch (self.mode) { |
| case SadTabViewMode::RELOAD: |
| label = l10n_util::GetNSString(IDS_SAD_TAB_TITLE); |
| break; |
| case SadTabViewMode::FEEDBACK: |
| label = l10n_util::GetNSString(IDS_SAD_TAB_RELOAD_TITLE); |
| break; |
| } |
| DCHECK(label); |
| return label; |
| } |
| |
| - (nonnull NSAttributedString*)messageTextViewAttributedText { |
| NSAttributedString* label = nil; |
| switch (self.mode) { |
| case SadTabViewMode::RELOAD: |
| label = [[NSAttributedString alloc] |
| initWithString:l10n_util::GetNSString(IDS_SAD_TAB_MESSAGE)]; |
| break; |
| case SadTabViewMode::FEEDBACK: { |
| NSString* feedbackIntroductionString = [NSString |
| stringWithFormat:@"%@\n\n", |
| l10n_util::GetNSString(IDS_SAD_TAB_RELOAD_TRY)]; |
| NSMutableAttributedString* feedbackString = |
| [[NSMutableAttributedString alloc] |
| initWithString:feedbackIntroductionString]; |
| |
| NSMutableArray* stringsArray = [NSMutableArray |
| arrayWithObjects:l10n_util::GetNSString( |
| IDS_SAD_TAB_RELOAD_RESTART_BROWSER), |
| l10n_util::GetNSString( |
| IDS_SAD_TAB_RELOAD_RESTART_DEVICE), |
| nil]; |
| if (!self.offTheRecord) { |
| NSString* incognitoSuggestionString = |
| l10n_util::GetNSString(IDS_SAD_TAB_RELOAD_INCOGNITO); |
| [stringsArray insertObject:incognitoSuggestionString atIndex:0]; |
| } |
| |
| NSAttributedString* bulletedListString = |
| [[self class] bulletedAttributedStringFromStrings:stringsArray]; |
| [feedbackString appendAttributedString:bulletedListString]; |
| label = feedbackString; |
| break; |
| } |
| } |
| DCHECK(label); |
| return label; |
| } |
| |
| - (nonnull NSString*)footerLabelText { |
| NSString* label = nil; |
| switch (self.mode) { |
| case SadTabViewMode::RELOAD: { |
| base::string16 footerLinkText( |
| l10n_util::GetStringUTF16(IDS_SAD_TAB_HELP_LINK)); |
| label = base::SysUTF16ToNSString( |
| l10n_util::GetStringFUTF16(IDS_SAD_TAB_HELP_MESSAGE, footerLinkText)); |
| } break; |
| case SadTabViewMode::FEEDBACK: |
| label = l10n_util::GetNSString(IDS_SAD_TAB_RELOAD_LEARN_MORE); |
| break; |
| } |
| DCHECK(label); |
| return label; |
| } |
| |
| - (nonnull NSString*)footerLinkText { |
| NSString* label = nil; |
| switch (self.mode) { |
| case SadTabViewMode::RELOAD: { |
| base::string16 footerLinkText( |
| l10n_util::GetStringUTF16(IDS_SAD_TAB_HELP_LINK)); |
| label = base::SysUTF16ToNSString(footerLinkText); |
| } break; |
| case SadTabViewMode::FEEDBACK: |
| label = l10n_util::GetNSString(IDS_SAD_TAB_RELOAD_LEARN_MORE); |
| break; |
| } |
| DCHECK(label); |
| return label; |
| } |
| |
| - (nonnull NSString*)buttonText { |
| NSString* label = nil; |
| switch (self.mode) { |
| case SadTabViewMode::RELOAD: |
| label = l10n_util::GetNSString(IDS_SAD_TAB_RELOAD_LABEL); |
| break; |
| case SadTabViewMode::FEEDBACK: |
| label = l10n_util::GetNSString(IDS_SAD_TAB_SEND_FEEDBACK_LABEL); |
| break; |
| } |
| DCHECK(label); |
| return label; |
| } |
| |
| - (void)attachLinkControllerToLabel:(nonnull UILabel*)label |
| forLinkText:(nonnull NSString*)linkText { |
| __weak __typeof(self) weakSelf = self; |
| _footerLabelLinkController = [[LabelLinkController alloc] |
| initWithLabel:label |
| action:^(const GURL& URL) { |
| [weakSelf.delegate sadTabView:weakSelf |
| showSuggestionsPageWithURL:URL]; |
| }]; |
| |
| _footerLabelLinkController.linkFont = |
| [[MDCTypography fontLoader] boldFontOfSize:kFooterLabelFontSize]; |
| _footerLabelLinkController.linkUnderlineStyle = NSUnderlineStyleSingle; |
| NSRange linkRange = [label.text rangeOfString:linkText]; |
| DCHECK(linkRange.location != NSNotFound); |
| DCHECK(linkRange.length > 0); |
| [_footerLabelLinkController addLinkWithRange:linkRange |
| url:GURL(kCrashReasonURL)]; |
| } |
| |
| #pragma mark Accessors |
| |
| - (UIView*)containerView { |
| if (!_containerView) { |
| _containerView = [[UIView alloc] initWithFrame:CGRectZero]; |
| [_containerView setBackgroundColor:self.backgroundColor]; |
| } |
| return _containerView; |
| } |
| |
| - (UIImageView*)imageView { |
| if (!_imageView) { |
| _imageView = |
| [[UIImageView alloc] initWithImage:NativeImage(IDR_CRASH_SAD_TAB)]; |
| [_imageView setBackgroundColor:self.backgroundColor]; |
| } |
| return _imageView; |
| } |
| |
| - (UILabel*)titleLabel { |
| if (!_titleLabel) { |
| _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero]; |
| [_titleLabel setBackgroundColor:self.backgroundColor]; |
| [_titleLabel setText:[self titleLabelText]]; |
| [_titleLabel setLineBreakMode:NSLineBreakByWordWrapping]; |
| [_titleLabel setNumberOfLines:0]; |
| [_titleLabel |
| setTextColor:[UIColor colorWithWhite:kTitleLabelTextColorBrightness |
| alpha:1.0]]; |
| [_titleLabel setFont:[[MDCTypography fontLoader] |
| regularFontOfSize:kTitleLabelFontSize]]; |
| } |
| return _titleLabel; |
| } |
| |
| - (UILabel*)footerLabel { |
| if (!_footerLabel) { |
| _footerLabel = [[UILabel alloc] initWithFrame:CGRectZero]; |
| [_footerLabel setBackgroundColor:self.backgroundColor]; |
| [_footerLabel setNumberOfLines:0]; |
| [_footerLabel setFont:[[MDCTypography fontLoader] |
| regularFontOfSize:kFooterLabelFontSize]]; |
| [_footerLabel |
| setTextColor:[UIColor colorWithWhite:kGeneralTextColorBrightness |
| alpha:1.0]]; |
| |
| [_footerLabel setText:[self footerLabelText]]; |
| [self attachLinkControllerToLabel:_footerLabel |
| forLinkText:[self footerLinkText]]; |
| } |
| return _footerLabel; |
| } |
| |
| - (CGRect)containerBounds { |
| CGFloat containerWidth = std::min( |
| CGRectGetWidth(self.bounds) - kLayoutInsets.left - kLayoutInsets.right, |
| kLayoutBoundsMaxWidth); |
| return CGRectMake(0.0, 0.0, containerWidth, CGFLOAT_MAX); |
| } |
| |
| #pragma mark Layout |
| |
| - (void)willMoveToSuperview:(nullable UIView*)newSuperview { |
| [super willMoveToSuperview:newSuperview]; |
| |
| if (self.containerView.superview) { |
| DCHECK_EQ(self.containerView.superview, self); |
| return; |
| } |
| |
| [self addSubview:self.containerView]; |
| [self.containerView addSubview:self.imageView]; |
| [self.containerView addSubview:self.titleLabel]; |
| [self.containerView addSubview:self.messageTextView]; |
| [self.containerView addSubview:self.footerLabel]; |
| } |
| |
| - (void)layoutSubviews { |
| [super layoutSubviews]; |
| |
| [self layoutImageView]; |
| [self layoutTitleLabel]; |
| [self layoutMessageTextView]; |
| [self layoutFooterLabel]; |
| [self layoutActionButton]; |
| [self layoutContainerView]; |
| } |
| |
| - (CGSize)sizeThatFits:(CGSize)size { |
| return size; |
| } |
| |
| - (void)layoutImageView { |
| LayoutRect imageViewLayout = LayoutRectZero; |
| imageViewLayout.boundingWidth = CGRectGetWidth(self.containerBounds); |
| imageViewLayout.size = self.imageView.bounds.size; |
| self.imageView.frame = |
| AlignRectOriginAndSizeToPixels(LayoutRectGetRect(imageViewLayout)); |
| } |
| |
| - (void)layoutTitleLabel { |
| CGRect containerBounds = self.containerBounds; |
| LayoutRect titleLabelLayout = LayoutRectZero; |
| titleLabelLayout.boundingWidth = CGRectGetWidth(containerBounds); |
| titleLabelLayout.size = [self.titleLabel sizeThatFits:containerBounds.size]; |
| titleLabelLayout.position.originY = |
| CGRectGetMaxY(self.imageView.frame) + kTitleLabelTopPadding; |
| self.titleLabel.frame = |
| AlignRectOriginAndSizeToPixels(LayoutRectGetRect(titleLabelLayout)); |
| } |
| |
| - (void)layoutMessageTextView { |
| CGRect containerBounds = self.containerBounds; |
| LayoutRect messageTextViewLayout = LayoutRectZero; |
| messageTextViewLayout.boundingWidth = CGRectGetWidth(containerBounds); |
| messageTextViewLayout.size = |
| [self.messageTextView sizeThatFits:containerBounds.size]; |
| messageTextViewLayout.position.originY = |
| CGRectGetMaxY(self.titleLabel.frame) + kMessageTextViewTopPadding; |
| self.messageTextView.frame = |
| AlignRectOriginAndSizeToPixels(LayoutRectGetRect(messageTextViewLayout)); |
| } |
| |
| - (void)layoutFooterLabel { |
| CGRect containerBounds = self.containerBounds; |
| LayoutRect footerLabelLayout = LayoutRectZero; |
| footerLabelLayout.boundingWidth = CGRectGetWidth(containerBounds); |
| footerLabelLayout.size = [self.footerLabel sizeThatFits:containerBounds.size]; |
| footerLabelLayout.position.originY = |
| CGRectGetMaxY(self.messageTextView.frame) + kFooterLabelTopPadding; |
| self.footerLabel.frame = |
| AlignRectOriginAndSizeToPixels(LayoutRectGetRect(footerLabelLayout)); |
| } |
| |
| - (void)layoutActionButton { |
| CGRect containerBounds = self.containerBounds; |
| BOOL isIPadIdiom = IsIPadIdiom(); |
| BOOL isPortrait = IsPortrait(); |
| BOOL shouldAddActionButtonToContainer = isIPadIdiom || !isPortrait; |
| LayoutRect actionButtonLayout = LayoutRectZero; |
| actionButtonLayout.size = |
| isIPadIdiom |
| ? [self.actionButton sizeThatFits:CGSizeZero] |
| : CGSizeMake(CGRectGetWidth(containerBounds), kActionButtonHeight); |
| if (shouldAddActionButtonToContainer) { |
| // Right-align actionButton and add it below helpLabel when adding it to |
| // the containerView. |
| if (self.actionButton.superview != self.containerView) |
| [self.containerView addSubview:self.actionButton]; |
| actionButtonLayout.boundingWidth = CGRectGetWidth(containerBounds); |
| actionButtonLayout.position = LayoutRectPositionMake( |
| CGRectGetWidth(containerBounds) - actionButtonLayout.size.width, |
| CGRectGetMaxY(self.footerLabel.frame) + kActionButtonTopPadding); |
| } else { |
| // Bottom-align the actionButton with the bounds specified by kLayoutInsets. |
| if (self.actionButton.superview != self) |
| [self addSubview:self.actionButton]; |
| actionButtonLayout.boundingWidth = CGRectGetWidth(self.bounds); |
| actionButtonLayout.position = LayoutRectPositionMake( |
| UIEdgeInsetsGetLeading(kLayoutInsets), |
| CGRectGetMaxY(self.bounds) - kLayoutInsets.bottom - |
| actionButtonLayout.size.height); |
| } |
| self.actionButton.frame = |
| AlignRectOriginAndSizeToPixels(LayoutRectGetRect(actionButtonLayout)); |
| } |
| |
| - (void)layoutContainerView { |
| UIView* bottomSubview = self.actionButton.superview == self.containerView |
| ? self.actionButton |
| : self.footerLabel; |
| CGSize containerSize = CGSizeMake(CGRectGetWidth(self.containerBounds), |
| CGRectGetMaxY(bottomSubview.frame)); |
| CGFloat containerOriginX = |
| (CGRectGetWidth(self.bounds) - containerSize.width) / 2.0f; |
| CGFloat containerOriginY = 0.0f; |
| if (IsIPadIdiom()) { |
| // Center the containerView on iPads. |
| containerOriginY = |
| (CGRectGetHeight(self.bounds) - containerSize.height) / 2.0f; |
| } else if (IsPortrait()) { |
| // Align containerView to a quarter of the view height on portrait iPhones. |
| containerOriginY = |
| (CGRectGetHeight(self.bounds) - containerSize.height) / 4.0f; |
| } else { |
| // Top-align containerView on landscape iPhones. |
| containerOriginY = kContainerViewLandscapeTopPadding; |
| } |
| self.containerView.frame = AlignRectOriginAndSizeToPixels( |
| CGRectMake(containerOriginX, containerOriginY, containerSize.width, |
| containerSize.height)); |
| } |
| |
| #pragma mark Util |
| |
| - (void)handleActionButtonTapped { |
| switch (self.mode) { |
| case SadTabViewMode::RELOAD: |
| UMA_HISTOGRAM_ENUMERATION(ui_metrics::kSadTabReloadHistogramKey, |
| ui_metrics::SadTabEvent::BUTTON_CLICKED, |
| ui_metrics::SadTabEvent::MAX_SAD_TAB_EVENT); |
| [self.delegate sadTabViewReload:self]; |
| break; |
| case SadTabViewMode::FEEDBACK: { |
| UMA_HISTOGRAM_ENUMERATION(ui_metrics::kSadTabFeedbackHistogramKey, |
| ui_metrics::SadTabEvent::BUTTON_CLICKED, |
| ui_metrics::SadTabEvent::MAX_SAD_TAB_EVENT); |
| [self.delegate sadTabViewShowReportAnIssue:self]; |
| break; |
| } |
| }; |
| } |
| |
| + (UIColor*)sadTabBackgroundColor { |
| return [UIColor colorWithWhite:kBackgroundColorBrightness alpha:1.0]; |
| } |
| |
| @end |
| |
| #pragma mark - |
| |
| @implementation SadTabView (UIElements) |
| |
| - (UITextView*)messageTextView { |
| if (!_messageTextView) { |
| _messageTextView = [[UITextView alloc] initWithFrame:CGRectZero]; |
| [_messageTextView setBackgroundColor:self.backgroundColor]; |
| [_messageTextView setAttributedText:[self messageTextViewAttributedText]]; |
| _messageTextView.textContainer.lineFragmentPadding = 0.0f; |
| [_messageTextView |
| setTextColor:[UIColor colorWithWhite:kGeneralTextColorBrightness |
| alpha:1.0]]; |
| [_messageTextView setFont:[[MDCTypography fontLoader] |
| regularFontOfSize:kMessageTextViewFontSize]]; |
| [_messageTextView setUserInteractionEnabled:NO]; |
| } |
| return _messageTextView; |
| } |
| |
| - (UIButton*)actionButton { |
| if (!_actionButton) { |
| _actionButton = [[MDCFlatButton alloc] init]; |
| [_actionButton setBackgroundColor:[[MDCPalette cr_bluePalette] tint500] |
| forState:UIControlStateNormal]; |
| [_actionButton setBackgroundColor:[[MDCPalette greyPalette] tint500] |
| forState:UIControlStateDisabled]; |
| [_actionButton setTitleColor:[UIColor whiteColor] |
| forState:UIControlStateNormal]; |
| [_actionButton setUnderlyingColorHint:[UIColor blackColor]]; |
| [_actionButton setInkColor:[UIColor colorWithWhite:1 alpha:0.2f]]; |
| |
| [_actionButton setTitle:[self buttonText] forState:UIControlStateNormal]; |
| [_actionButton setTitleColor:[UIColor whiteColor] |
| forState:UIControlStateNormal]; |
| [_actionButton addTarget:self |
| action:@selector(handleActionButtonTapped) |
| forControlEvents:UIControlEventTouchUpInside]; |
| } |
| return _actionButton; |
| } |
| |
| @end |