Commit 6be377dc authored by Stepan Khapugin's avatar Stepan Khapugin Committed by Commit Bot

[iOS][Shortcuts] Display most visited shortcuts in the zero state.

Creates a collection view, cells, and plumbing for most visited sites
in zero-state of omnibox.

Cq-Include-Trybots: luci.chromium.try:ios-simulator-cronet;luci.chromium.try:ios-simulator-full-configs
Change-Id: Ifcc5af9806178fc6a53af64587086a504f957aca
Reviewed-on: https://chromium-review.googlesource.com/c/1286418
Commit-Queue: Stepan Khapugin <stkhapugin@chromium.org>
Reviewed-by: default avatarGauthier Ambard <gambard@chromium.org>
Cr-Commit-Position: refs/heads/master@{#601568}
parent 24ae0f10
...@@ -81,7 +81,8 @@ ...@@ -81,7 +81,8 @@
_popupView->SetMediator(self.mediator); _popupView->SetMediator(self.mediator);
if (base::FeatureList::IsEnabled( if (base::FeatureList::IsEnabled(
omnibox::kOmniboxPopupShortcutIconsInZeroState)) { omnibox::kOmniboxPopupShortcutIconsInZeroState) &&
!self.browserState->IsOffTheRecord()) {
self.shortcutsCoordinator = [[ShortcutsCoordinator alloc] self.shortcutsCoordinator = [[ShortcutsCoordinator alloc]
initWithBaseViewController:self.popupViewController initWithBaseViewController:self.popupViewController
browserState:self.browserState]; browserState:self.browserState];
...@@ -107,7 +108,8 @@ ...@@ -107,7 +108,8 @@
// are already part of the NTP. // are already part of the NTP.
if (!IsVisibleUrlNewTabPage(self.webStateList->GetActiveWebState()) && if (!IsVisibleUrlNewTabPage(self.webStateList->GetActiveWebState()) &&
base::FeatureList::IsEnabled( base::FeatureList::IsEnabled(
omnibox::kOmniboxPopupShortcutIconsInZeroState)) { omnibox::kOmniboxPopupShortcutIconsInZeroState) &&
!self.browserState->IsOffTheRecord()) {
self.popupViewController.shortcutsEnabled = YES; self.popupViewController.shortcutsEnabled = YES;
} }
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
namespace { namespace {
const int kRowCount = 6; const int kRowCount = 6;
const CGFloat kRowHeight = 48.0; const CGFloat kRowHeight = 48.0;
const CGFloat kShortcutsRowHeight = 100; const CGFloat kShortcutsRowHeight = 320;
const CGFloat kAnswerRowHeight = 64.0; const CGFloat kAnswerRowHeight = 64.0;
const CGFloat kTopAndBottomPadding = 8.0; const CGFloat kTopAndBottomPadding = 8.0;
UIColor* BackgroundColorTablet() { UIColor* BackgroundColorTablet() {
...@@ -628,6 +628,16 @@ UIColor* BackgroundColorIncognito() { ...@@ -628,6 +628,16 @@ UIColor* BackgroundColorIncognito() {
#pragma mark - #pragma mark -
#pragma mark Table view delegate #pragma mark Table view delegate
- (BOOL)tableView:(UITableView*)tableView
shouldHighlightRowAtIndexPath:(NSIndexPath*)indexPath {
if (self.shortcutsEnabled && indexPath.row == 0 &&
_currentResult.count == 0) {
return NO;
}
return YES;
}
- (void)tableView:(UITableView*)tableView - (void)tableView:(UITableView*)tableView
didSelectRowAtIndexPath:(NSIndexPath*)indexPath { didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
DCHECK_EQ(0U, (NSUInteger)indexPath.section); DCHECK_EQ(0U, (NSUInteger)indexPath.section);
......
...@@ -9,10 +9,37 @@ source_set("shortcuts") { ...@@ -9,10 +9,37 @@ source_set("shortcuts") {
"shortcuts_view_controller.h", "shortcuts_view_controller.h",
"shortcuts_view_controller.mm", "shortcuts_view_controller.mm",
] ]
deps = [
":shortcuts_internal",
"//base",
"//components/ntp_tiles",
"//ios/chrome/browser/favicon",
"//ios/chrome/browser/ntp_tiles",
"//ios/chrome/browser/reading_list",
"//ios/chrome/browser/ui/coordinators:chrome_coordinators",
"//ios/chrome/browser/ui/ntp_tile_views",
"//ios/chrome/common/favicon",
"//ios/chrome/common/ui_util",
]
configs += [ "//build/config/compiler:enable_arc" ]
}
source_set("shortcuts_internal") {
sources = [
"shortcuts_consumer.h",
"shortcuts_mediator.h",
"shortcuts_mediator.mm",
"shortcuts_most_visited_item.h",
"shortcuts_most_visited_item.mm",
]
configs += [ "//build/config/compiler:enable_arc" ] configs += [ "//build/config/compiler:enable_arc" ]
deps = [ deps = [
"//components/ntp_tiles",
"//components/reading_list/core",
"//ios/chrome/browser/ntp_tiles",
"//ios/chrome/browser/ui/coordinators:chrome_coordinators", "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
"//ios/chrome/browser/ui/favicon",
] ]
} }
...@@ -25,6 +52,7 @@ source_set("unit_tests") { ...@@ -25,6 +52,7 @@ source_set("unit_tests") {
] ]
deps = [ deps = [
":shortcuts", ":shortcuts",
":shortcuts_internal",
"//base", "//base",
"//ios/chrome/app/strings", "//ios/chrome/app/strings",
"//ios/chrome/browser", "//ios/chrome/browser",
......
// 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.
#ifndef IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_CONSUMER_H_
#define IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_CONSUMER_H_
#import <UIKit/UIKit.h>
#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_most_visited_item.h"
// A protocol defining a consumer of shortcuts data.
@protocol ShortcutsConsumer<NSObject>
// Called immediately when the shortcuts are available for the first time.
- (void)mostVisitedShortcutsAvailable:
(NSArray<ShortcutsMostVisitedItem*>*)items;
// Called when the favicon of a given item has changed or reloaded.
- (void)faviconChangedForItem:(ShortcutsMostVisitedItem*)item;
// Called when the reading list badge count changes.
- (void)readingListBadgeUpdatedWithCount:(NSInteger)count;
@end
#endif // IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_CONSUMER_H_
...@@ -4,6 +4,12 @@ ...@@ -4,6 +4,12 @@
#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_coordinator.h" #import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_coordinator.h"
#include "components/ntp_tiles/most_visited_sites.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/ntp_tiles/ios_most_visited_sites_factory.h"
#include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_mediator.h"
#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.h" #import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
...@@ -14,17 +20,39 @@ ...@@ -14,17 +20,39 @@
// Redefined as readwrite and as ShortcutsViewController. // Redefined as readwrite and as ShortcutsViewController.
@property(nonatomic, strong, readwrite) ShortcutsViewController* viewController; @property(nonatomic, strong, readwrite) ShortcutsViewController* viewController;
// The mediator that pushes the most visited tiles and the reading list badge
// value to the view controller.
@property(nonatomic, strong) ShortcutsMediator* mediator;
@end @end
@implementation ShortcutsCoordinator @implementation ShortcutsCoordinator
- (void)start { - (void)start {
self.viewController = [[ShortcutsViewController alloc] init]; favicon::LargeIconService* largeIconService =
IOSChromeLargeIconServiceFactory::GetForBrowserState(self.browserState);
LargeIconCache* cache =
IOSChromeLargeIconCacheFactory::GetForBrowserState(self.browserState);
std::unique_ptr<ntp_tiles::MostVisitedSites> mostVisitedSites =
IOSMostVisitedSitesFactory::NewForBrowserState(self.browserState);
ReadingListModel* readingListModel =
ReadingListModelFactory::GetForBrowserState(self.browserState);
self.mediator = [[ShortcutsMediator alloc]
initWithLargeIconService:largeIconService
largeIconCache:cache
mostVisitedSite:std::move(mostVisitedSites)
readingListModel:readingListModel];
ShortcutsViewController* shortcutsViewController =
[[ShortcutsViewController alloc] init];
self.viewController = shortcutsViewController;
self.mediator.consumer = shortcutsViewController;
} }
- (void)stop { - (void)stop {
self.viewController = nil; self.viewController = nil;
self.mediator = nil;
} }
@end @end
// 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.
#ifndef IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_MEDIATOR_H_
#define IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_MEDIATOR_H_
#import <UIKit/UIKit.h>
#include <memory>
namespace favicon {
class LargeIconService;
}
namespace ntp_tiles {
class MostVisitedSites;
}
class LargeIconCache;
class ReadingListModel;
@protocol ShortcutsConsumer;
// Coordinator for the Omnibox Popup Shortcuts.
@interface ShortcutsMediator : NSObject
- (instancetype)
initWithLargeIconService:(favicon::LargeIconService*)largeIconService
largeIconCache:(LargeIconCache*)largeIconCache
mostVisitedSite:
(std::unique_ptr<ntp_tiles::MostVisitedSites>)mostVisitedSites
readingListModel:(ReadingListModel*)readingListModel;
@property(nonatomic, weak) id<ShortcutsConsumer> consumer;
@end
#endif // IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_MEDIATOR_H_
// 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_mediator.h"
#include "components/ntp_tiles/most_visited_sites.h"
#include "components/ntp_tiles/ntp_tile.h"
#include "components/reading_list/core/reading_list_model.h"
#include "ios/chrome/browser/ntp_tiles/most_visited_sites_observer_bridge.h"
#import "ios/chrome/browser/ui/favicon/favicon_attributes_provider.h"
#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_consumer.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// Maximum number of most visited tiles fetched.
const NSInteger kMaxNumMostVisitedTiles = 4;
const CGFloat kFaviconSize = 48;
const CGFloat kFaviconMinimalSize = 32;
} // namespace
@interface ShortcutsMediator ()<MostVisitedSitesObserving>
// Most visited items from the MostVisitedSites service currently displayed.
@property(nonatomic, strong)
NSMutableArray<ShortcutsMostVisitedItem*>* mostVisitedItems;
@property(nonatomic, strong)
FaviconAttributesProvider* faviconAttributesProvider;
@end
@implementation ShortcutsMediator {
std::unique_ptr<ntp_tiles::MostVisitedSites> _mostVisitedSites;
std::unique_ptr<ntp_tiles::MostVisitedSitesObserverBridge> _mostVisitedBridge;
}
- (instancetype)
initWithLargeIconService:(favicon::LargeIconService*)largeIconService
largeIconCache:(LargeIconCache*)largeIconCache
mostVisitedSite:
(std::unique_ptr<ntp_tiles::MostVisitedSites>)mostVisitedSites
readingListModel:(ReadingListModel*)readingListModel {
self = [super init];
if (self) {
_mostVisitedSites = std::move(mostVisitedSites);
_mostVisitedBridge =
std::make_unique<ntp_tiles::MostVisitedSitesObserverBridge>(self);
_mostVisitedSites->SetMostVisitedURLsObserver(_mostVisitedBridge.get(),
kMaxNumMostVisitedTiles);
_faviconAttributesProvider = [[FaviconAttributesProvider alloc]
initWithFaviconSize:kFaviconSize
minFaviconSize:kFaviconMinimalSize
largeIconService:largeIconService];
_faviconAttributesProvider.cache = largeIconCache;
}
return self;
}
- (void)setConsumer:(id<ShortcutsConsumer>)consumer {
_consumer = consumer;
[self pushData];
}
#pragma mark - private
- (void)pushData {
[self.consumer mostVisitedShortcutsAvailable:self.mostVisitedItems];
}
- (void)fetchFaviconForItem:(ShortcutsMostVisitedItem*)item {
__weak ShortcutsMediator* weakSelf = self;
[self.faviconAttributesProvider
fetchFaviconAttributesForURL:item.URL
completion:^(FaviconAttributes* attributes) {
item.attributes = attributes;
[weakSelf.consumer faviconChangedForItem:item];
}];
}
#pragma mark - MostVisitedSitesObserving
- (void)onMostVisitedURLsAvailable:
(const ntp_tiles::NTPTilesVector&)mostVisited {
if (self.mostVisitedItems.count) {
// If some content is already displayed to the user, do not update without a
// user action.
return;
}
NSMutableArray* newMostVisited = [NSMutableArray array];
for (const ntp_tiles::NTPTile& tile : mostVisited) {
ShortcutsMostVisitedItem* item =
[ShortcutsMostVisitedItem itemWithNTPTile:tile];
[newMostVisited addObject:item];
[self fetchFaviconForItem:item];
}
self.mostVisitedItems = newMostVisited;
[self pushData];
}
- (void)onIconMadeAvailable:(const GURL&)siteURL {
for (ShortcutsMostVisitedItem* item in self.mostVisitedItems) {
if (item.URL == siteURL) {
[self fetchFaviconForItem:item];
return;
}
}
}
@end
// 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.
#ifndef IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_MOST_VISITED_ITEM_H_
#define IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_MOST_VISITED_ITEM_H_
#import <Foundation/Foundation.h>
class GURL;
namespace ntp_tiles {
struct NTPTile;
} // namespace ntp_tiles
@class FaviconAttributes;
// An item that represents a Most Visited site in omnibox popup zero-state
// shortcuts.
@interface ShortcutsMostVisitedItem : NSObject
+ (instancetype)itemWithNTPTile:(const ntp_tiles::NTPTile&)tile;
@property(nonatomic, copy) NSString* title;
@property(nonatomic, assign) GURL URL;
@property(nonatomic, strong) FaviconAttributes* attributes;
@end
#endif // IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_MOST_VISITED_ITEM_H_
// 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_most_visited_item.h"
#include "base/strings/sys_string_conversions.h"
#include "components/ntp_tiles/ntp_tile.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@implementation ShortcutsMostVisitedItem
+ (instancetype)itemWithNTPTile:(const ntp_tiles::NTPTile&)tile {
ShortcutsMostVisitedItem* item = [[ShortcutsMostVisitedItem alloc] init];
item.title = base::SysUTF16ToNSString(tile.title);
item.URL = tile.url;
return item;
}
@end
...@@ -7,8 +7,10 @@ ...@@ -7,8 +7,10 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_consumer.h"
// The view controller displaying the omnibox shortcuts in the zero state. // The view controller displaying the omnibox shortcuts in the zero state.
@interface ShortcutsViewController : UIViewController @interface ShortcutsViewController : UIViewController<ShortcutsConsumer>
@end @end
......
...@@ -4,17 +4,160 @@ ...@@ -4,17 +4,160 @@
#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.h" #import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.h"
#include "base/logging.h"
#import "ios/chrome/browser/ui/ntp_tile_views/ntp_most_visited_tile_view.h"
#import "ios/chrome/common/favicon/favicon_view.h"
#import "ios/chrome/common/ui_util/constraints_ui_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support." #error "This file requires ARC support."
#endif #endif
namespace {
const NSInteger kNumberOfItemsPerRow = 4;
const CGFloat kLineSpacing = 30;
const CGFloat kItemSpacing = 10;
const CGFloat kTopInset = 10;
const CGSize kItemSize = {73, 100};
} // namespace
#pragma mark - ShortcutCell
// A collection view subclass that contains a most visited tile.
@interface ShortcutCell : UICollectionViewCell
// The tile contained in the cell.
@property(nonatomic, strong) NTPMostVisitedTileView* tile;
@end
@implementation ShortcutCell
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_tile = [[NTPMostVisitedTileView alloc] init];
_tile.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:_tile];
AddSameConstraints(self.contentView, _tile);
}
return self;
}
- (void)prepareForReuse {
[super prepareForReuse];
self.tile.titleLabel.text = nil;
}
@end
#pragma mark - ShortcutsViewController
@interface ShortcutsViewController ()<UICollectionViewDelegate,
UICollectionViewDataSource>
@property(nonatomic, strong) UICollectionViewFlowLayout* layout;
@property(nonatomic, strong) UICollectionView* collectionView;
@property(nonatomic, strong)
NSArray<ShortcutsMostVisitedItem*>* mostVisitedItems;
@end
@implementation ShortcutsViewController @implementation ShortcutsViewController
#pragma mark - UIViewController
- (void)viewDidLoad { - (void)viewDidLoad {
[super viewDidLoad]; [super viewDidLoad];
// Just a placeholder to see if this is really displayed. [self.view addSubview:self.collectionView];
self.view.backgroundColor = self.collectionView.translatesAutoresizingMaskIntoConstraints = NO;
[UIColor colorWithRed:0.5 green:0.9 blue:0.9 alpha:0.7]; AddSameConstraints(self.view, self.collectionView);
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Calculate insets to center the items in the view.
CGFloat widthInsets =
(self.view.bounds.size.width - kItemSize.width * kNumberOfItemsPerRow -
kItemSpacing * (kNumberOfItemsPerRow - 1)) /
2;
self.layout.sectionInset =
UIEdgeInsetsMake(kTopInset, widthInsets, 0, widthInsets);
}
#pragma mark - properties
- (UICollectionView*)collectionView {
if (_collectionView) {
return _collectionView;
}
_collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame
collectionViewLayout:self.layout];
_collectionView.delegate = self;
_collectionView.dataSource = self;
_collectionView.backgroundColor = [UIColor clearColor];
[_collectionView registerClass:[ShortcutCell class]
forCellWithReuseIdentifier:NSStringFromClass([ShortcutCell class])];
return _collectionView;
}
- (UICollectionViewFlowLayout*)layout {
if (_layout) {
return _layout;
}
_layout = [[UICollectionViewFlowLayout alloc] init];
_layout.minimumLineSpacing = kLineSpacing;
_layout.itemSize = kItemSize;
return _layout;
}
#pragma mark - ShortcutsConsumer
- (void)mostVisitedShortcutsAvailable:
(NSArray<ShortcutsMostVisitedItem*>*)items {
self.mostVisitedItems = items;
if (!self.viewLoaded) {
return;
}
[self.collectionView reloadData];
}
- (void)faviconChangedForItem:(ShortcutsMostVisitedItem*)item {
if (!self.viewLoaded) {
return;
}
NSUInteger i = [self.mostVisitedItems indexOfObject:item];
DCHECK(i != NSNotFound);
[self.collectionView
reloadItemsAtIndexPaths:@[ [NSIndexPath indexPathWithIndex:i] ]];
}
- (void)readingListBadgeUpdatedWithCount:(NSInteger)count {
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView*)collectionView
numberOfItemsInSection:(NSInteger)section {
return kNumberOfItemsPerRow;
}
// The cell that is returned must be retrieved from a call to
// -dequeueReusableCellWithReuseIdentifier:forIndexPath:
- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
cellForItemAtIndexPath:(NSIndexPath*)indexPath {
ShortcutCell* cell = [self.collectionView
dequeueReusableCellWithReuseIdentifier:NSStringFromClass(
[ShortcutCell class])
forIndexPath:indexPath];
ShortcutsMostVisitedItem* item = self.mostVisitedItems[indexPath.item];
[cell.tile.faviconView configureWithAttributes:item.attributes];
cell.tile.titleLabel.text = item.title;
return cell;
} }
@end @end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment