Index: ios/chrome/browser/ui/history/history_entry_item.mm |
diff --git a/ios/chrome/browser/ui/history/history_entry_item.mm b/ios/chrome/browser/ui/history/history_entry_item.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..92a69519df1b727303a9fe8e23f19fc3a9a5e822 |
--- /dev/null |
+++ b/ios/chrome/browser/ui/history/history_entry_item.mm |
@@ -0,0 +1,315 @@ |
+// 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/history/history_entry_item.h" |
+ |
+#include "base/i18n/time_formatting.h" |
+#include "base/ios/weak_nsobject.h" |
+#import "base/mac/foundation_util.h" |
+#import "base/mac/objc_property_releaser.h" |
+#include "base/strings/sys_string_conversions.h" |
+#include "base/strings/utf_string_conversions.h" |
+#include "components/history/core/browser/url_row.h" |
+#include "components/strings/grit/components_strings.h" |
+#include "components/url_formatter/url_formatter.h" |
+#include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h" |
+#import "ios/chrome/browser/ui/history/favicon_view.h" |
+#import "ios/chrome/browser/ui/history/favicon_view_provider.h" |
+#import "ios/chrome/browser/ui/history/history_entry.h" |
+#include "ios/chrome/browser/ui/rtl_geometry.h" |
+#import "ios/chrome/browser/ui/uikit_ui_util.h" |
+#include "ios/chrome/grit/ios_strings.h" |
+#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h" |
+#import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoFontLoader.h" |
+#include "ui/base/l10n/l10n_util.h" |
+ |
+namespace { |
+// Size at which the favicon will be displayed. |
+const CGFloat kFaviconSize = 24.0; |
+// Minimum size at which to fetch favicons. |
+const CGFloat kMinFaviconSize = 16.0; |
+// Horizontal spacing between edge of the cell and the cell content. |
+const CGFloat kMargin = 16.0; |
+// Horizontal spacing between the leading edge of the cell and the text. |
+const CGFloat kHeaderMargin = 56.0; |
+ |
+NSString* FormattedTitle(const base::string16& title, const GURL& url) { |
+ // Use url as title if no title. |
+ bool using_url_as_the_title = false; |
+ base::string16 formatted_title(title); |
+ if (title.empty()) { |
+ using_url_as_the_title = true; |
+ formatted_title = url_formatter::FormatUrl(url); |
+ } |
+ // Since the title can contain BiDi text, mark the text as either RTL or LTR, |
+ // depending on the characters in the string. If the URL is used as the title, |
+ // mark the title as LTR since URLs are always treated as left to right |
+ // strings. |
+ if (base::i18n::IsRTL()) { |
+ if (using_url_as_the_title) |
+ base::i18n::WrapStringWithLTRFormatting(&formatted_title); |
+ else |
+ base::i18n::AdjustStringForLocaleDirection(&formatted_title); |
+ } |
+ return base::SysUTF16ToNSString(formatted_title); |
+} |
+} // namespace |
+ |
+#pragma mark - HistoryEntryItem |
+ |
+@interface HistoryEntryItem ()<FaviconViewProviderDelegate> { |
+ // Property releaser for HistoryEntryItem. |
+ base::mac::ObjCPropertyReleaser _propertyReleaser_HistoryEntryItem; |
+ // Delegate for HistoryEntryItem. |
+ base::WeakNSProtocol<id<HistoryEntryItemDelegate>> _delegate; |
+} |
+ |
+// FaviconViewProvider to fetch the favicon and format the favicon view. |
+@property(nonatomic, retain) FaviconViewProvider* faviconViewProvider; |
+ |
+// Custom accessibility actions for the history entry view. |
+- (NSArray*)accessibilityActions; |
+// Custom accessibility action to delete the history entry. |
+- (BOOL)deleteHistoryEntry; |
+// Custom accessibility action to open the history entry's URL in a new tab. |
+- (BOOL)openInNewTab; |
+// Custom accessibility action to open the history entry's URL in a new |
+// incognito tab. |
+- (BOOL)openInNewIncognitoTab; |
+// Custom accessibility action to copy the history entry's URL to the clipboard. |
+- (BOOL)copyURL; |
+@end |
+ |
+@implementation HistoryEntryItem |
+ |
+@synthesize faviconViewProvider = _faviconViewProvider; |
+@synthesize text = _text; |
+@synthesize detailText = _detailText; |
+@synthesize timeText = _timeText; |
+@synthesize URL = _URL; |
+@synthesize timestamp = _timestamp; |
+ |
+- (instancetype)initWithType:(NSInteger)type |
+ historyEntry:(const history::HistoryEntry&)entry |
+ browserState:(ios::ChromeBrowserState*)browserState |
+ delegate:(id<HistoryEntryItemDelegate>)delegate { |
+ self = [super initWithType:type]; |
+ if (self) { |
+ _propertyReleaser_HistoryEntryItem.Init(self, [HistoryEntryItem class]); |
+ self.cellClass = [HistoryEntryCell class]; |
+ favicon::LargeIconService* largeIconService = |
+ IOSChromeLargeIconServiceFactory::GetForBrowserState(browserState); |
+ _faviconViewProvider = |
+ [[FaviconViewProvider alloc] initWithURL:entry.url |
+ faviconSize:kFaviconSize |
+ minFaviconSize:kMinFaviconSize |
+ largeIconService:largeIconService |
+ delegate:self]; |
+ _text = [FormattedTitle(entry.title, entry.url) copy]; |
+ _detailText = [base::SysUTF8ToNSString(entry.url.spec()) copy]; |
+ _timeText = |
+ [base::SysUTF16ToNSString(base::TimeFormatTimeOfDay(entry.time)) copy]; |
+ _URL = GURL(entry.url); |
+ _timestamp = entry.time; |
+ _delegate.reset(delegate); |
+ } |
+ return self; |
+} |
+ |
+- (instancetype)initWithType:(NSInteger)type { |
+ NOTREACHED(); |
+ return nil; |
+} |
+ |
+- (BOOL)isEqualToHistoryEntryItem:(HistoryEntryItem*)item { |
+ return item && item.URL == _URL && item.timestamp == _timestamp; |
+} |
+ |
+- (BOOL)isEqual:(id)object { |
+ if (self == object) |
+ return YES; |
+ |
+ if (![object isMemberOfClass:[HistoryEntryItem class]]) |
+ return NO; |
+ |
+ return [self isEqualToHistoryEntryItem:object]; |
+} |
+ |
+- (NSUInteger)hash { |
+ return [base::SysUTF8ToNSString(self.URL.spec()) hash] ^ |
+ self.timestamp.ToInternalValue(); |
+} |
+ |
+- (NSArray*)accessibilityActions { |
+ UIAccessibilityCustomAction* deleteAction = |
+ [[[UIAccessibilityCustomAction alloc] |
+ initWithName:l10n_util::GetNSString( |
+ IDS_HISTORY_ENTRY_ACCESSIBILITY_DELETE) |
+ target:self |
+ selector:@selector(deleteHistoryEntry)] autorelease]; |
+ UIAccessibilityCustomAction* openInNewTabAction = |
+ [[[UIAccessibilityCustomAction alloc] |
+ initWithName:l10n_util::GetNSString( |
+ IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWTAB) |
+ target:self |
+ selector:@selector(openInNewTab)] autorelease]; |
+ UIAccessibilityCustomAction* openInNewIncognitoTabAction = |
+ [[[UIAccessibilityCustomAction alloc] |
+ initWithName:l10n_util::GetNSString( |
+ IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWINCOGNITOTAB) |
+ target:self |
+ selector:@selector(openInNewIncognitoTab)] autorelease]; |
+ UIAccessibilityCustomAction* copyURLAction = |
+ [[[UIAccessibilityCustomAction alloc] |
+ initWithName:l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_COPY) |
+ target:self |
+ selector:@selector(copyURL)] autorelease]; |
+ return @[ |
+ deleteAction, openInNewTabAction, openInNewIncognitoTabAction, copyURLAction |
+ ]; |
+} |
+ |
+- (BOOL)deleteHistoryEntry { |
+ [_delegate historyEntryItemDidRequestDelete:self]; |
+ return YES; |
+} |
+ |
+- (BOOL)openInNewTab { |
+ [_delegate historyEntryItemDidRequestOpenInNewTab:self]; |
+ return YES; |
+} |
+ |
+- (BOOL)openInNewIncognitoTab { |
+ [_delegate historyEntryItemDidRequestOpenInNewIncognitoTab:self]; |
+ return YES; |
+} |
+ |
+- (BOOL)copyURL { |
+ [_delegate historyEntryItemDidRequestCopy:self]; |
+ return YES; |
+} |
+ |
+- (void)configureCell:(HistoryEntryCell*)cell { |
+ [super configureCell:cell]; |
+ |
+ // Set favicon view and constraints. |
+ FaviconView* faviconView = self.faviconViewProvider.faviconView; |
+ [cell.faviconViewContainer addSubview:faviconView]; |
+ [faviconView setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ AddSameSizeConstraint(faviconView, cell.faviconViewContainer); |
+ AddSameCenterConstraints(faviconView, cell.faviconViewContainer); |
+ |
+ cell.textLabel.text = self.text; |
+ cell.detailTextLabel.text = self.detailText; |
+ cell.timeLabel.text = self.timeText; |
+ cell.isAccessibilityElement = YES; |
+ cell.accessibilityCustomActions = self.accessibilityActions; |
+ cell.accessibilityLabel = |
+ l10n_util::GetNSStringF(IDS_HISTORY_ENTRY_ACCESSIBILITY_LABEL, |
+ base::SysNSStringToUTF16(self.text), |
+ base::SysNSStringToUTF16(self.detailText), |
+ base::SysNSStringToUTF16(self.timeText)); |
+} |
+ |
+- (void)faviconViewProviderFaviconDidLoad:(FaviconViewProvider*)provider { |
+ [_delegate historyEntryItemShouldUpdateView:self]; |
+} |
+ |
+@end |
+ |
+#pragma mark - HistoryEntryCell |
+ |
+@interface HistoryEntryCell () { |
+ // Property releaser for HistoryEntryCell. |
+ base::mac::ObjCPropertyReleaser _propertyReleaser_HistoryEntryCell; |
+} |
+ |
+// Redeclare as readwrite to allow property releaser to handle these properties. |
+@property(nonatomic, readwrite, retain) UILabel* textLabel; |
+@property(nonatomic, readwrite, retain) UILabel* detailTextLabel; |
+@property(nonatomic, readwrite, retain) UILabel* timeLabel; |
+@end |
+ |
+@implementation HistoryEntryCell |
+ |
+@synthesize faviconViewContainer = _faviconViewContainer; |
+@synthesize textLabel = _textLabel; |
+@synthesize detailTextLabel = _detailTextLabel; |
+@synthesize timeLabel = _timeLabel; |
+ |
+- (id)initWithFrame:(CGRect)frame { |
+ self = [super initWithFrame:frame]; |
+ if (self) { |
+ _propertyReleaser_HistoryEntryCell.Init(self, [HistoryEntryCell class]); |
+ |
+ _faviconViewContainer = [[UIView alloc] initWithFrame:CGRectZero]; |
+ |
+ _textLabel = [[UILabel alloc] initWithFrame:CGRectZero]; |
+ [_textLabel |
+ setFont:[[MDFRobotoFontLoader sharedInstance] mediumFontOfSize:16]]; |
+ [_textLabel setTextColor:[[MDCPalette greyPalette] tint900]]; |
+ |
+ _detailTextLabel = [[UILabel alloc] initWithFrame:CGRectZero]; |
+ [_detailTextLabel |
+ setFont:[[MDFRobotoFontLoader sharedInstance] regularFontOfSize:14]]; |
+ [_detailTextLabel setTextColor:[[MDCPalette greyPalette] tint600]]; |
+ |
+ _timeLabel = [[UILabel alloc] initWithFrame:CGRectZero]; |
+ [_timeLabel |
+ setFont:[[MDFRobotoFontLoader sharedInstance] mediumFontOfSize:14]]; |
+ [_timeLabel setTextColor:[[MDCPalette greyPalette] tint600]]; |
+ _timeLabel.textAlignment = |
+ UseRTLLayout() ? NSTextAlignmentLeft : NSTextAlignmentRight; |
+ |
+ UIView* contentView = self.contentView; |
+ [contentView addSubview:_faviconViewContainer]; |
+ [contentView addSubview:_textLabel]; |
+ [contentView addSubview:_detailTextLabel]; |
+ [contentView addSubview:_timeLabel]; |
+ |
+ [_faviconViewContainer setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ [_textLabel setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ [_detailTextLabel setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ [_timeLabel setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ |
+ [_faviconViewContainer.widthAnchor constraintEqualToConstant:kFaviconSize]; |
+ |
+ NSDictionary* views = @{ |
+ @"title" : _textLabel, |
+ @"URL" : _detailTextLabel, |
+ @"time" : _timeLabel, |
+ @"favicon" : _faviconViewContainer, |
+ }; |
+ NSDictionary* metrics = @{ |
+ @"margin" : @(kMargin), |
+ @"spacing" : @(kHeaderMargin - (kMargin + kFaviconSize)), |
+ }; |
+ NSArray* constraints = @[ |
+ @"H:|-margin-[favicon]-spacing-[title]-[time]-margin-|", |
+ @"H:|-margin-[favicon]-spacing-[URL]-margin-|", |
+ @"V:|-margin-[title][URL]-margin-|", |
+ ]; |
+ ApplyVisualConstraintsWithMetrics(constraints, views, metrics); |
+ AddSameCenterYConstraint(_textLabel, _timeLabel); |
+ AddSameCenterYConstraint(_faviconViewContainer, _textLabel); |
+ |
+ [_timeLabel |
+ setContentCompressionResistancePriority:UILayoutPriorityRequired |
+ forAxis: |
+ UILayoutConstraintAxisHorizontal]; |
+ } |
+ return self; |
+} |
+ |
+- (void)prepareForReuse { |
+ [super prepareForReuse]; |
+ _textLabel.text = nil; |
+ _detailTextLabel.text = nil; |
+ _timeLabel.text = nil; |
+ for (UIView* subview in _faviconViewContainer.subviews) { |
+ [subview removeFromSuperview]; |
+ } |
+} |
+ |
+@end |