blob: 735b034e40e9cbf109de07d6cbebed95e046bbab [file] [log] [blame]
// Copyright 2018 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/omnibox/popup/shortcuts/shortcuts_view_controller.h"
#include "base/logging.h"
#include "base/mac/foundation_util.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#import "ios/chrome/browser/ui/ntp_tile_views/ntp_most_visited_tile_view.h"
#import "ios/chrome/browser/ui/ntp_tile_views/ntp_shortcut_tile_view.h"
#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.h"
#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h"
#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/collection_shortcut_cell.h"
#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/most_visited_shortcut_cell.h"
#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller_delegate.h"
#import "ios/chrome/common/favicon/favicon_view.h"
#import "ios/chrome/common/ui_util/constraints_ui_util.h"
#include "url/gurl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
const CGFloat kTopInset = 10;
} // namespace
@interface ShortcutsViewController ()
// Latest most visited items. Updated directly from the consumer calls.
@property(nonatomic, strong)
NSArray<ShortcutsMostVisitedItem*>* latestMostVisitedItems;
// Currently displayed most visited items. Will be set to nil when the view
// disappears, and set to |latestMostVisitedItems| when the view appears. This
// prevents the updates when the user sees the tiles.
@property(nonatomic, strong)
NSArray<ShortcutsMostVisitedItem*>* displayedMostVisitedItems;
@property(nonatomic, assign) NSInteger readingListBadgeValue;
@end
@implementation ShortcutsViewController
#pragma mark - initializers
- (instancetype)init {
UICollectionViewFlowLayout* layout =
[[UICollectionViewFlowLayout alloc] init];
layout.sectionInset = UIEdgeInsetsMake(kTopInset, 0, 0, 0);
self = [super initWithCollectionViewLayout:layout];
if (self) {
self.collectionView.backgroundColor = [UIColor clearColor];
[self.collectionView registerClass:[MostVisitedShortcutCell class]
forCellWithReuseIdentifier:NSStringFromClass(
[MostVisitedShortcutCell class])];
[self.collectionView registerClass:[CollectionShortcutCell class]
forCellWithReuseIdentifier:NSStringFromClass(
[CollectionShortcutCell class])];
}
return self;
}
#pragma mark - UIViewController
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Promote the latest most visited items to the displayed ones and reload the
// collection view data.
self.displayedMostVisitedItems = self.latestMostVisitedItems;
[self configureLayout:base::mac::ObjCCastStrict<UICollectionViewFlowLayout>(
self.collectionViewLayout)];
}
- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
[super traitCollectionDidChange:previousTraitCollection];
[self configureLayout:base::mac::ObjCCastStrict<UICollectionViewFlowLayout>(
self.collectionViewLayout)];
}
#pragma mark - ShortcutsConsumer
- (void)mostVisitedShortcutsAvailable:
(NSArray<ShortcutsMostVisitedItem*>*)items {
self.latestMostVisitedItems = items;
// Normally, the most visited tiles should not change when the user sees them.
// However, in case there were no items, and now they're available, it is
// better to show something, even if this means reloading the view.
if (self.displayedMostVisitedItems.count == 0 && self.viewLoaded) {
self.displayedMostVisitedItems = self.latestMostVisitedItems;
[self.collectionView reloadData];
}
}
- (void)faviconChangedForURL:(const GURL&)URL {
if (!self.viewLoaded) {
return;
}
for (ShortcutsMostVisitedItem* item in self.displayedMostVisitedItems) {
if (item.URL == URL) {
NSUInteger i = [self.displayedMostVisitedItems indexOfObject:item];
NSIndexPath* indexPath = [NSIndexPath indexPathForItem:i inSection:0];
MostVisitedShortcutCell* cell =
base::mac::ObjCCastStrict<MostVisitedShortcutCell>(
[self.collectionView cellForItemAtIndexPath:indexPath]);
[self configureMostVisitedCell:cell
withItem:self.displayedMostVisitedItems[i]];
}
}
}
- (void)readingListBadgeUpdatedWithCount:(NSInteger)count {
self.readingListBadgeValue = count;
if (!self.viewLoaded) {
return;
}
NSIndexPath* readingListShortcutIndexPath =
[NSIndexPath indexPathForItem:NTPCollectionShortcutTypeReadingList +
self.displayedMostVisitedItems.count
inSection:0];
[self.collectionView
reloadItemsAtIndexPaths:@[ readingListShortcutIndexPath ]];
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)numberOfSectionsInCollectionView:
(UICollectionView*)collectionView {
return 1;
}
- (NSInteger)collectionView:(UICollectionView*)collectionView
numberOfItemsInSection:(NSInteger)section {
DCHECK(section == 0);
return self.displayedMostVisitedItems.count + NTPCollectionShortcutTypeCount;
}
// The cell that is returned must be retrieved from a call to
// -dequeueReusableCellWithReuseIdentifier:forIndexPath:
- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
cellForItemAtIndexPath:(NSIndexPath*)indexPath {
if (static_cast<NSUInteger>(indexPath.row) <
self.displayedMostVisitedItems.count) {
MostVisitedShortcutCell* cell = [self.collectionView
dequeueReusableCellWithReuseIdentifier:
NSStringFromClass([MostVisitedShortcutCell class])
forIndexPath:indexPath];
ShortcutsMostVisitedItem* item =
self.displayedMostVisitedItems[indexPath.item];
[self configureMostVisitedCell:cell withItem:item];
cell.accessibilityTraits = UIAccessibilityTraitButton;
return cell;
} else {
CollectionShortcutCell* cell = [self.collectionView
dequeueReusableCellWithReuseIdentifier:
NSStringFromClass([CollectionShortcutCell class])
forIndexPath:indexPath];
DCHECK(static_cast<NSUInteger>(indexPath.row) <
self.displayedMostVisitedItems.count +
NTPCollectionShortcutTypeCount)
<< "Only four collection shortcuts described in "
"NTPCollectionShortcutType are supported";
NTPCollectionShortcutType type = (NTPCollectionShortcutType)(
indexPath.item - self.displayedMostVisitedItems.count);
[self configureCollectionShortcutCell:cell withCollection:type];
cell.accessibilityTraits = UIAccessibilityTraitButton;
return cell;
}
return nil;
}
- (void)configureMostVisitedCell:(MostVisitedShortcutCell*)cell
withItem:(ShortcutsMostVisitedItem*)item {
[cell.tile.faviconView configureWithAttributes:item.attributes];
cell.tile.titleLabel.text = item.title;
cell.accessibilityLabel = cell.tile.titleLabel.text;
}
- (void)configureCollectionShortcutCell:(CollectionShortcutCell*)cell
withCollection:(NTPCollectionShortcutType)type {
cell.tile.titleLabel.text = TitleForCollectionShortcutType(type);
cell.tile.iconView.image = ImageForCollectionShortcutType(type);
cell.accessibilityLabel = cell.tile.titleLabel.text;
if (@available(iOS 13, *)) {
// The accessibilityUserInputLabel should just be the title, with nothing
// extra for the reading list tile.
cell.accessibilityUserInputLabels = @[ cell.tile.titleLabel.text ];
}
if (type == NTPCollectionShortcutTypeReadingList) {
if (self.readingListBadgeValue > 0) {
cell.tile.countLabel.text = [@(self.readingListBadgeValue) stringValue];
cell.tile.countContainer.hidden = NO;
cell.accessibilityLabel = [NSString
stringWithFormat:@"%@, %@", cell.accessibilityLabel,
AccessibilityLabelForReadingListCellWithCount(
self.readingListBadgeValue)];
}
}
}
#pragma mark - UICollectionViewDelegate
- (void)collectionView:(UICollectionView*)collectionView
didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
if (static_cast<NSUInteger>(indexPath.row) <
self.displayedMostVisitedItems.count) {
ShortcutsMostVisitedItem* item =
self.displayedMostVisitedItems[indexPath.item];
DCHECK(item);
[self.commandHandler openMostVisitedItem:item];
base::RecordAction(
base::UserMetricsAction("MobileOmniboxShortcutsOpenMostVisitedItem"));
} else {
NTPCollectionShortcutType type = (NTPCollectionShortcutType)(
indexPath.item - self.displayedMostVisitedItems.count);
switch (type) {
case NTPCollectionShortcutTypeBookmark:
[self.commandHandler openBookmarks];
base::RecordAction(
base::UserMetricsAction("MobileOmniboxShortcutsOpenBookmarks"));
break;
case NTPCollectionShortcutTypeRecentTabs:
[self.commandHandler openRecentTabs];
base::RecordAction(
base::UserMetricsAction("MobileOmniboxShortcutsOpenRecentTabs"));
break;
case NTPCollectionShortcutTypeReadingList:
[self.commandHandler openReadingList];
base::RecordAction(
base::UserMetricsAction("MobileOmniboxShortcutsOpenReadingList"));
break;
case NTPCollectionShortcutTypeHistory:
[self.commandHandler openHistory];
base::RecordAction(
base::UserMetricsAction("MobileOmniboxShortcutsOpenHistory"));
break;
case NTPCollectionShortcutTypeCount:
NOTREACHED();
break;
}
}
}
#pragma mark - Private
- (void)configureLayout:(UICollectionViewFlowLayout*)layout {
layout.minimumLineSpacing = kNtpTilesVerticalSpacing;
layout.minimumInteritemSpacing =
NtpTilesHorizontalSpacing(self.traitCollection);
layout.itemSize =
MostVisitedCellSize(self.traitCollection.preferredContentSizeCategory);
}
@end