Index: ios/chrome/browser/ui/ntp/most_visited_cell.mm |
diff --git a/ios/chrome/browser/ui/ntp/most_visited_cell.mm b/ios/chrome/browser/ui/ntp/most_visited_cell.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..21e5a017f589ac181fe5fd2696697d23b53384c8 |
--- /dev/null |
+++ b/ios/chrome/browser/ui/ntp/most_visited_cell.mm |
@@ -0,0 +1,242 @@ |
+// 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/ntp/most_visited_cell.h" |
+ |
+#include <memory> |
+ |
+#import "base/ios/weak_nsobject.h" |
+#include "base/mac/bind_objc_block.h" |
+#import "base/mac/scoped_nsobject.h" |
+#include "base/memory/ref_counted_memory.h" |
+#include "base/strings/sys_string_conversions.h" |
+#include "base/task/cancelable_task_tracker.h" |
+#include "components/favicon/core/fallback_url_util.h" |
+#include "components/favicon/core/large_icon_service.h" |
+#include "components/favicon_base/fallback_icon_style.h" |
+#include "components/favicon_base/favicon_types.h" |
+#include "components/history/core/browser/top_sites.h" |
+#include "components/suggestions/suggestions_service.h" |
+#import "ios/chrome/browser/favicon/favicon_loader.h" |
+#include "ios/chrome/browser/favicon/favicon_service_factory.h" |
+#include "ios/chrome/browser/favicon/ios_chrome_favicon_loader_factory.h" |
+#include "ios/chrome/browser/favicon/ios_chrome_large_icon_cache_factory.h" |
+#include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h" |
+#include "ios/chrome/browser/favicon/large_icon_cache.h" |
+#include "ios/chrome/browser/history/top_sites_factory.h" |
+#include "ios/chrome/browser/suggestions/suggestions_service_factory.h" |
+#import "ios/chrome/browser/ui/uikit_ui_util.h" |
+#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h" |
+#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h" |
+#include "skia/ext/skia_utils_ios.h" |
+ |
+const CGFloat kFaviconSize = 48; |
+const CGFloat kFaviconMinSize = 32; |
+const CGFloat kImageViewBackgroundColor = 0.941; |
+const CGFloat kImageViewCornerRadius = 3; |
+const NSInteger kLabelNumLines = 2; |
+const CGFloat kLabelTextColor = 0.314; |
+const CGFloat kMaximumWidth = 73; |
+const CGFloat kMaximumHeight = 100; |
+ |
+@interface MostVisitedCell () { |
+ // Backs property with the same name. |
+ GURL _URL; |
+ // Weak reference to the relevant BrowserState. |
+ ios::ChromeBrowserState* _browserState; |
+ // Backs property with the same name. |
+ ntp_tiles::metrics::MostVisitedTileType _tileType; |
+ |
+ base::scoped_nsobject<UILabel> _label; |
+ base::scoped_nsobject<UILabel> _noIconLabel; |
+ base::scoped_nsobject<UIImageView> _imageView; |
+ // Used to cancel tasks for the LargeIconService. |
+ base::CancelableTaskTracker _cancelable_task_tracker; |
+} |
+// Set the background color and center the first letter of the site title (or |
+// domain if the title is a url). |
+- (void)updateIconLabelWithColor:(UIColor*)textColor |
+ backgroundColor:(UIColor*)backgroundColor |
+ isDefaultBackgroundColor:(BOOL)isDefaultBackgroundColor; |
+// Set icon of top site. |
+- (void)setImage:(UIImage*)image; |
+@end |
+ |
+@implementation MostVisitedCell |
+ |
+@synthesize URL = _URL; |
+@synthesize browserState = _browserState; |
+@synthesize tileType = _tileType; |
+ |
+- (instancetype)initWithFrame:(CGRect)frame { |
+ self = [super initWithFrame:frame]; |
+ if (!self) { |
+ return nil; |
+ } |
+ _label.reset([[UILabel alloc] initWithFrame:CGRectZero]); |
+ [_label setTextColor:[UIColor colorWithWhite:kLabelTextColor alpha:1.0]]; |
+ [_label setBackgroundColor:[UIColor clearColor]]; |
+ [_label setFont:[MDCTypography captionFont]]; |
+ [_label setTextAlignment:NSTextAlignmentCenter]; |
+ CGSize maxSize = [self.class maximumSize]; |
+ [_label setPreferredMaxLayoutWidth:maxSize.width]; |
+ [_label setNumberOfLines:kLabelNumLines]; |
+ |
+ _noIconLabel.reset([[UILabel alloc] initWithFrame:CGRectZero]); |
+ [_noIconLabel setBackgroundColor:[UIColor clearColor]]; |
+ [_noIconLabel setFont:[MDCTypography headlineFont]]; |
+ [_noIconLabel setTextAlignment:NSTextAlignmentCenter]; |
+ |
+ _imageView.reset([[UIImageView alloc] initWithFrame:CGRectZero]); |
+ [_imageView layer].cornerRadius = kImageViewCornerRadius; |
+ [_imageView setClipsToBounds:YES]; |
+ [self addSubview:_imageView]; |
+ [self addSubview:_label]; |
+ [self addSubview:_noIconLabel]; |
+ |
+ [_noIconLabel setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ [_imageView setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ [_label setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ |
+ NSDictionary* views = |
+ @{ @"label" : _label, |
+ @"image" : _imageView, |
+ @"noIcon" : _noIconLabel }; |
+ NSArray* constraints = @[ |
+ @"V:|[image(==iconSize)]-10-[label]", @"V:[noIcon(==iconSize)]", |
+ @"H:[image(==iconSize)]", @"H:|[label]|", @"H:|[noIcon]|" |
+ ]; |
+ ApplyVisualConstraintsWithMetrics(constraints, views, |
+ @{ @"iconSize" : @(kFaviconSize) }); |
+ AddSameCenterXConstraint(self, _imageView); |
+ AddSameCenterXConstraint(self, _noIconLabel); |
+ |
+ [self prepareForReuse]; |
+ return self; |
+} |
+ |
+- (void)prepareForReuse { |
+ [super prepareForReuse]; |
+ [self setImage:nil]; |
+ [self setText:nil]; |
+ // TODO(crbug.com/495600): -stepToVerifyAccessibilityOnCurrentScreen does not |
+ // honor -setIsAccessibilityElement and setAccessibilityElementsHidden for |
+ // UICollectionViewCell's, so setting the label to a single space string to |
+ // pass tests. This string will not be read by screen readers. |
+ [_label setText:@" "]; |
+ [self setURL:GURL()]; |
+ [_imageView |
+ setBackgroundColor:[UIColor colorWithWhite:kImageViewBackgroundColor |
+ alpha:1.0]]; |
+ [_noIconLabel setText:nil]; |
+} |
+ |
+#pragma mark - Public Setters |
+ |
+- (void)setText:(NSString*)text { |
+ [_label setText:text]; |
+ [self setAccessibilityLabel:text]; |
+ [self setIsAccessibilityElement:text != nil]; |
+ [self setAccessibilityElementsHidden:text == nil]; |
+ [self setUserInteractionEnabled:YES]; |
+} |
+ |
+- (void)showPlaceholder { |
+ [_imageView setBackgroundColor:[[MDCPalette greyPalette] tint50]]; |
+} |
+ |
+- (void)setupWithURL:(GURL)URL |
+ title:(NSString*)title |
+ browserState:(ios::ChromeBrowserState*)browserState { |
+ _browserState = browserState; |
+ _tileType = ntp_tiles::metrics::NONE; |
+ [self setText:title]; |
+ [self setURL:URL]; |
+ base::WeakNSObject<MostVisitedCell> weakSelf(self); |
+ |
+ void (^faviconBlock)(const favicon_base::LargeIconResult&) = |
+ ^(const favicon_base::LargeIconResult& result) { |
+ base::scoped_nsobject<MostVisitedCell> strongSelf([weakSelf retain]); |
+ if (!strongSelf) |
+ return; |
+ |
+ if (URL == [strongSelf URL]) { // Tile has not been reused. |
+ if (result.bitmap.is_valid()) { |
+ scoped_refptr<base::RefCountedMemory> data = |
+ result.bitmap.bitmap_data.get(); |
+ UIImage* favicon = |
+ [UIImage imageWithData:[NSData dataWithBytes:data->front() |
+ length:data->size()] |
+ scale:[UIScreen mainScreen].scale]; |
+ [strongSelf setImage:favicon]; |
+ } else if (result.fallback_icon_style) { |
+ UIColor* backgroundColor = skia::UIColorFromSkColor( |
+ result.fallback_icon_style->background_color); |
+ UIColor* textColor = skia::UIColorFromSkColor( |
+ result.fallback_icon_style->text_color); |
+ [strongSelf updateIconLabelWithColor:textColor |
+ backgroundColor:backgroundColor |
+ isDefaultBackgroundColor:result.fallback_icon_style-> |
+ is_default_background_color]; |
+ } |
+ } |
+ |
+ if (result.bitmap.is_valid() || result.fallback_icon_style) { |
+ IOSChromeLargeIconCacheFactory::GetForBrowserState( |
+ [strongSelf browserState]) |
+ ->SetCachedResult(URL, result); |
+ } |
+ }; |
+ |
+ LargeIconCache* cache = |
+ IOSChromeLargeIconCacheFactory::GetForBrowserState(self.browserState); |
+ std::unique_ptr<favicon_base::LargeIconResult> cached_result = |
+ cache->GetCachedResult(URL); |
+ if (cached_result) { |
+ faviconBlock(*cached_result); |
+ } |
+ |
+ // Always call LargeIconService in case the favicon was updated. |
+ favicon::LargeIconService* large_icon_service = |
+ IOSChromeLargeIconServiceFactory::GetForBrowserState(self.browserState); |
+ CGFloat faviconSize = [UIScreen mainScreen].scale * kFaviconSize; |
+ CGFloat faviconMinSize = [UIScreen mainScreen].scale * kFaviconMinSize; |
+ large_icon_service->GetLargeIconOrFallbackStyle( |
+ URL, faviconMinSize, faviconSize, base::BindBlock(faviconBlock), |
+ &_cancelable_task_tracker); |
+} |
+ |
+- (void)removePlaceholderImage { |
+ [_imageView setBackgroundColor:nil]; |
+ [self setUserInteractionEnabled:NO]; |
+} |
+ |
+#pragma mark - Class methods |
+ |
++ (CGSize)maximumSize { |
+ return CGSizeMake(kMaximumWidth, kMaximumHeight); |
+} |
+ |
+#pragma mark - Private methods |
+ |
+- (void)updateIconLabelWithColor:(UIColor*)textColor |
+ backgroundColor:(UIColor*)backgroundColor |
+ isDefaultBackgroundColor:(BOOL)isDefaultBackgroundColor { |
+ [self setImage:nil]; |
+ [_noIconLabel |
+ setText:base::SysUTF16ToNSString(favicon::GetFallbackIconText(_URL))]; |
+ [_noIconLabel setTextColor:textColor]; |
+ [_imageView setBackgroundColor:backgroundColor]; |
+ _tileType = isDefaultBackgroundColor ? ntp_tiles::metrics::ICON_DEFAULT |
+ : ntp_tiles::metrics::ICON_COLOR; |
+} |
+ |
+- (void)setImage:(UIImage*)image { |
+ [_imageView setBackgroundColor:nil]; |
+ [_noIconLabel setText:nil]; |
+ [_imageView setImage:image]; |
+ _tileType = ntp_tiles::metrics::ICON_REAL; |
+} |
+ |
+@end |