OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #import "ios/chrome/browser/ui/history/history_entry_item.h" |
| 6 |
| 7 #include "base/i18n/time_formatting.h" |
| 8 #include "base/ios/weak_nsobject.h" |
| 9 #import "base/mac/foundation_util.h" |
| 10 #import "base/mac/objc_property_releaser.h" |
| 11 #include "base/strings/sys_string_conversions.h" |
| 12 #include "base/strings/utf_string_conversions.h" |
| 13 #include "components/history/core/browser/url_row.h" |
| 14 #include "components/strings/grit/components_strings.h" |
| 15 #include "components/url_formatter/url_formatter.h" |
| 16 #include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h" |
| 17 #import "ios/chrome/browser/ui/history/favicon_view.h" |
| 18 #import "ios/chrome/browser/ui/history/favicon_view_provider.h" |
| 19 #import "ios/chrome/browser/ui/history/history_entry.h" |
| 20 #include "ios/chrome/browser/ui/rtl_geometry.h" |
| 21 #import "ios/chrome/browser/ui/uikit_ui_util.h" |
| 22 #include "ios/chrome/grit/ios_strings.h" |
| 23 #import "ios/third_party/material_components_ios/src/components/Palettes/src/Mat
erialPalettes.h" |
| 24 #import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoF
ontLoader.h" |
| 25 #include "ui/base/l10n/l10n_util.h" |
| 26 |
| 27 namespace { |
| 28 // Size at which the favicon will be displayed. |
| 29 const CGFloat kFaviconSize = 24.0; |
| 30 // Minimum size at which to fetch favicons. |
| 31 const CGFloat kMinFaviconSize = 16.0; |
| 32 // Horizontal spacing between edge of the cell and the cell content. |
| 33 const CGFloat kMargin = 16.0; |
| 34 // Horizontal spacing between the leading edge of the cell and the text. |
| 35 const CGFloat kHeaderMargin = 56.0; |
| 36 |
| 37 NSString* FormattedTitle(const base::string16& title, const GURL& url) { |
| 38 // Use url as title if no title. |
| 39 bool using_url_as_the_title = false; |
| 40 base::string16 formatted_title(title); |
| 41 if (title.empty()) { |
| 42 using_url_as_the_title = true; |
| 43 formatted_title = url_formatter::FormatUrl(url); |
| 44 } |
| 45 // Since the title can contain BiDi text, mark the text as either RTL or LTR, |
| 46 // depending on the characters in the string. If the URL is used as the title, |
| 47 // mark the title as LTR since URLs are always treated as left to right |
| 48 // strings. |
| 49 if (base::i18n::IsRTL()) { |
| 50 if (using_url_as_the_title) |
| 51 base::i18n::WrapStringWithLTRFormatting(&formatted_title); |
| 52 else |
| 53 base::i18n::AdjustStringForLocaleDirection(&formatted_title); |
| 54 } |
| 55 return base::SysUTF16ToNSString(formatted_title); |
| 56 } |
| 57 } // namespace |
| 58 |
| 59 #pragma mark - HistoryEntryItem |
| 60 |
| 61 @interface HistoryEntryItem ()<FaviconViewProviderDelegate> { |
| 62 // Property releaser for HistoryEntryItem. |
| 63 base::mac::ObjCPropertyReleaser _propertyReleaser_HistoryEntryItem; |
| 64 // Delegate for HistoryEntryItem. |
| 65 base::WeakNSProtocol<id<HistoryEntryItemDelegate>> _delegate; |
| 66 } |
| 67 |
| 68 // FaviconViewProvider to fetch the favicon and format the favicon view. |
| 69 @property(nonatomic, retain) FaviconViewProvider* faviconViewProvider; |
| 70 |
| 71 // Custom accessibility actions for the history entry view. |
| 72 - (NSArray*)accessibilityActions; |
| 73 // Custom accessibility action to delete the history entry. |
| 74 - (BOOL)deleteHistoryEntry; |
| 75 // Custom accessibility action to open the history entry's URL in a new tab. |
| 76 - (BOOL)openInNewTab; |
| 77 // Custom accessibility action to open the history entry's URL in a new |
| 78 // incognito tab. |
| 79 - (BOOL)openInNewIncognitoTab; |
| 80 // Custom accessibility action to copy the history entry's URL to the clipboard. |
| 81 - (BOOL)copyURL; |
| 82 @end |
| 83 |
| 84 @implementation HistoryEntryItem |
| 85 |
| 86 @synthesize faviconViewProvider = _faviconViewProvider; |
| 87 @synthesize text = _text; |
| 88 @synthesize detailText = _detailText; |
| 89 @synthesize timeText = _timeText; |
| 90 @synthesize URL = _URL; |
| 91 @synthesize timestamp = _timestamp; |
| 92 |
| 93 - (instancetype)initWithType:(NSInteger)type |
| 94 historyEntry:(const history::HistoryEntry&)entry |
| 95 browserState:(ios::ChromeBrowserState*)browserState |
| 96 delegate:(id<HistoryEntryItemDelegate>)delegate { |
| 97 self = [super initWithType:type]; |
| 98 if (self) { |
| 99 _propertyReleaser_HistoryEntryItem.Init(self, [HistoryEntryItem class]); |
| 100 self.cellClass = [HistoryEntryCell class]; |
| 101 favicon::LargeIconService* largeIconService = |
| 102 IOSChromeLargeIconServiceFactory::GetForBrowserState(browserState); |
| 103 _faviconViewProvider = |
| 104 [[FaviconViewProvider alloc] initWithURL:entry.url |
| 105 faviconSize:kFaviconSize |
| 106 minFaviconSize:kMinFaviconSize |
| 107 largeIconService:largeIconService |
| 108 delegate:self]; |
| 109 _text = [FormattedTitle(entry.title, entry.url) copy]; |
| 110 _detailText = [base::SysUTF8ToNSString(entry.url.spec()) copy]; |
| 111 _timeText = |
| 112 [base::SysUTF16ToNSString(base::TimeFormatTimeOfDay(entry.time)) copy]; |
| 113 _URL = GURL(entry.url); |
| 114 _timestamp = entry.time; |
| 115 _delegate.reset(delegate); |
| 116 } |
| 117 return self; |
| 118 } |
| 119 |
| 120 - (instancetype)initWithType:(NSInteger)type { |
| 121 NOTREACHED(); |
| 122 return nil; |
| 123 } |
| 124 |
| 125 - (BOOL)isEqualToHistoryEntryItem:(HistoryEntryItem*)item { |
| 126 return item && item.URL == _URL && item.timestamp == _timestamp; |
| 127 } |
| 128 |
| 129 - (BOOL)isEqual:(id)object { |
| 130 if (self == object) |
| 131 return YES; |
| 132 |
| 133 if (![object isMemberOfClass:[HistoryEntryItem class]]) |
| 134 return NO; |
| 135 |
| 136 return [self isEqualToHistoryEntryItem:object]; |
| 137 } |
| 138 |
| 139 - (NSUInteger)hash { |
| 140 return [base::SysUTF8ToNSString(self.URL.spec()) hash] ^ |
| 141 self.timestamp.ToInternalValue(); |
| 142 } |
| 143 |
| 144 - (NSArray*)accessibilityActions { |
| 145 UIAccessibilityCustomAction* deleteAction = |
| 146 [[[UIAccessibilityCustomAction alloc] |
| 147 initWithName:l10n_util::GetNSString( |
| 148 IDS_HISTORY_ENTRY_ACCESSIBILITY_DELETE) |
| 149 target:self |
| 150 selector:@selector(deleteHistoryEntry)] autorelease]; |
| 151 UIAccessibilityCustomAction* openInNewTabAction = |
| 152 [[[UIAccessibilityCustomAction alloc] |
| 153 initWithName:l10n_util::GetNSString( |
| 154 IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWTAB) |
| 155 target:self |
| 156 selector:@selector(openInNewTab)] autorelease]; |
| 157 UIAccessibilityCustomAction* openInNewIncognitoTabAction = |
| 158 [[[UIAccessibilityCustomAction alloc] |
| 159 initWithName:l10n_util::GetNSString( |
| 160 IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWINCOGNITOTAB) |
| 161 target:self |
| 162 selector:@selector(openInNewIncognitoTab)] autorelease]; |
| 163 UIAccessibilityCustomAction* copyURLAction = |
| 164 [[[UIAccessibilityCustomAction alloc] |
| 165 initWithName:l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_COPY) |
| 166 target:self |
| 167 selector:@selector(copyURL)] autorelease]; |
| 168 return @[ |
| 169 deleteAction, openInNewTabAction, openInNewIncognitoTabAction, copyURLAction |
| 170 ]; |
| 171 } |
| 172 |
| 173 - (BOOL)deleteHistoryEntry { |
| 174 [_delegate historyEntryItemDidRequestDelete:self]; |
| 175 return YES; |
| 176 } |
| 177 |
| 178 - (BOOL)openInNewTab { |
| 179 [_delegate historyEntryItemDidRequestOpenInNewTab:self]; |
| 180 return YES; |
| 181 } |
| 182 |
| 183 - (BOOL)openInNewIncognitoTab { |
| 184 [_delegate historyEntryItemDidRequestOpenInNewIncognitoTab:self]; |
| 185 return YES; |
| 186 } |
| 187 |
| 188 - (BOOL)copyURL { |
| 189 [_delegate historyEntryItemDidRequestCopy:self]; |
| 190 return YES; |
| 191 } |
| 192 |
| 193 - (void)configureCell:(HistoryEntryCell*)cell { |
| 194 [super configureCell:cell]; |
| 195 |
| 196 // Set favicon view and constraints. |
| 197 FaviconView* faviconView = self.faviconViewProvider.faviconView; |
| 198 [cell.faviconViewContainer addSubview:faviconView]; |
| 199 [faviconView setTranslatesAutoresizingMaskIntoConstraints:NO]; |
| 200 AddSameSizeConstraint(faviconView, cell.faviconViewContainer); |
| 201 AddSameCenterConstraints(faviconView, cell.faviconViewContainer); |
| 202 |
| 203 cell.textLabel.text = self.text; |
| 204 cell.detailTextLabel.text = self.detailText; |
| 205 cell.timeLabel.text = self.timeText; |
| 206 cell.isAccessibilityElement = YES; |
| 207 cell.accessibilityCustomActions = self.accessibilityActions; |
| 208 cell.accessibilityLabel = |
| 209 l10n_util::GetNSStringF(IDS_HISTORY_ENTRY_ACCESSIBILITY_LABEL, |
| 210 base::SysNSStringToUTF16(self.text), |
| 211 base::SysNSStringToUTF16(self.detailText), |
| 212 base::SysNSStringToUTF16(self.timeText)); |
| 213 } |
| 214 |
| 215 - (void)faviconViewProviderFaviconDidLoad:(FaviconViewProvider*)provider { |
| 216 [_delegate historyEntryItemShouldUpdateView:self]; |
| 217 } |
| 218 |
| 219 @end |
| 220 |
| 221 #pragma mark - HistoryEntryCell |
| 222 |
| 223 @interface HistoryEntryCell () { |
| 224 // Property releaser for HistoryEntryCell. |
| 225 base::mac::ObjCPropertyReleaser _propertyReleaser_HistoryEntryCell; |
| 226 } |
| 227 |
| 228 // Redeclare as readwrite to allow property releaser to handle these properties. |
| 229 @property(nonatomic, readwrite, retain) UILabel* textLabel; |
| 230 @property(nonatomic, readwrite, retain) UILabel* detailTextLabel; |
| 231 @property(nonatomic, readwrite, retain) UILabel* timeLabel; |
| 232 @end |
| 233 |
| 234 @implementation HistoryEntryCell |
| 235 |
| 236 @synthesize faviconViewContainer = _faviconViewContainer; |
| 237 @synthesize textLabel = _textLabel; |
| 238 @synthesize detailTextLabel = _detailTextLabel; |
| 239 @synthesize timeLabel = _timeLabel; |
| 240 |
| 241 - (id)initWithFrame:(CGRect)frame { |
| 242 self = [super initWithFrame:frame]; |
| 243 if (self) { |
| 244 _propertyReleaser_HistoryEntryCell.Init(self, [HistoryEntryCell class]); |
| 245 |
| 246 _faviconViewContainer = [[UIView alloc] initWithFrame:CGRectZero]; |
| 247 |
| 248 _textLabel = [[UILabel alloc] initWithFrame:CGRectZero]; |
| 249 [_textLabel |
| 250 setFont:[[MDFRobotoFontLoader sharedInstance] mediumFontOfSize:16]]; |
| 251 [_textLabel setTextColor:[[MDCPalette greyPalette] tint900]]; |
| 252 |
| 253 _detailTextLabel = [[UILabel alloc] initWithFrame:CGRectZero]; |
| 254 [_detailTextLabel |
| 255 setFont:[[MDFRobotoFontLoader sharedInstance] regularFontOfSize:14]]; |
| 256 [_detailTextLabel setTextColor:[[MDCPalette greyPalette] tint600]]; |
| 257 |
| 258 _timeLabel = [[UILabel alloc] initWithFrame:CGRectZero]; |
| 259 [_timeLabel |
| 260 setFont:[[MDFRobotoFontLoader sharedInstance] mediumFontOfSize:14]]; |
| 261 [_timeLabel setTextColor:[[MDCPalette greyPalette] tint600]]; |
| 262 _timeLabel.textAlignment = |
| 263 UseRTLLayout() ? NSTextAlignmentLeft : NSTextAlignmentRight; |
| 264 |
| 265 UIView* contentView = self.contentView; |
| 266 [contentView addSubview:_faviconViewContainer]; |
| 267 [contentView addSubview:_textLabel]; |
| 268 [contentView addSubview:_detailTextLabel]; |
| 269 [contentView addSubview:_timeLabel]; |
| 270 |
| 271 [_faviconViewContainer setTranslatesAutoresizingMaskIntoConstraints:NO]; |
| 272 [_textLabel setTranslatesAutoresizingMaskIntoConstraints:NO]; |
| 273 [_detailTextLabel setTranslatesAutoresizingMaskIntoConstraints:NO]; |
| 274 [_timeLabel setTranslatesAutoresizingMaskIntoConstraints:NO]; |
| 275 |
| 276 [_faviconViewContainer.widthAnchor constraintEqualToConstant:kFaviconSize]; |
| 277 |
| 278 NSDictionary* views = @{ |
| 279 @"title" : _textLabel, |
| 280 @"URL" : _detailTextLabel, |
| 281 @"time" : _timeLabel, |
| 282 @"favicon" : _faviconViewContainer, |
| 283 }; |
| 284 NSDictionary* metrics = @{ |
| 285 @"margin" : @(kMargin), |
| 286 @"spacing" : @(kHeaderMargin - (kMargin + kFaviconSize)), |
| 287 }; |
| 288 NSArray* constraints = @[ |
| 289 @"H:|-margin-[favicon]-spacing-[title]-[time]-margin-|", |
| 290 @"H:|-margin-[favicon]-spacing-[URL]-margin-|", |
| 291 @"V:|-margin-[title][URL]-margin-|", |
| 292 ]; |
| 293 ApplyVisualConstraintsWithMetrics(constraints, views, metrics); |
| 294 AddSameCenterYConstraint(_textLabel, _timeLabel); |
| 295 AddSameCenterYConstraint(_faviconViewContainer, _textLabel); |
| 296 |
| 297 [_timeLabel |
| 298 setContentCompressionResistancePriority:UILayoutPriorityRequired |
| 299 forAxis: |
| 300 UILayoutConstraintAxisHorizontal]; |
| 301 } |
| 302 return self; |
| 303 } |
| 304 |
| 305 - (void)prepareForReuse { |
| 306 [super prepareForReuse]; |
| 307 _textLabel.text = nil; |
| 308 _detailTextLabel.text = nil; |
| 309 _timeLabel.text = nil; |
| 310 for (UIView* subview in _faviconViewContainer.subviews) { |
| 311 [subview removeFromSuperview]; |
| 312 } |
| 313 } |
| 314 |
| 315 @end |
OLD | NEW |