Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(398)

Unified Diff: ios/chrome/browser/ui/reading_list/reading_list_view_controller_container.mm

Issue 2659693004: Add context menu when long press on a reading list entry (Closed)
Patch Set: Address comments Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ios/chrome/browser/ui/reading_list/reading_list_view_controller_container.mm
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_view_controller_container.mm b/ios/chrome/browser/ui/reading_list/reading_list_view_controller_container.mm
index 5da64ccf4d6905a3260a8ef48439e1b92413bfbe..216de19dc6f9506075bf21a502f0f66a8c214b32 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_view_controller_container.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_view_controller_container.mm
@@ -4,16 +4,49 @@
#import "ios/chrome/browser/ui/reading_list/reading_list_view_controller_container.h"
-#import "ios/chrome/browser/ui/uikit_ui_util.h"
+#import <MobileCoreServices/MobileCoreServices.h>
+
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
+#include "components/reading_list/ios/reading_list_model.h"
+#include "ios/chrome/browser/reading_list/offline_url_utils.h"
+#import "ios/chrome/browser/ui/alert_coordinator/action_sheet_coordinator.h"
+#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
#import "ios/chrome/browser/ui/keyboard/UIKeyCommand+Chrome.h"
+#import "ios/chrome/browser/ui/reading_list/reading_list_collection_view_item.h"
#import "ios/chrome/browser/ui/reading_list/reading_list_toolbar.h"
#import "ios/chrome/browser/ui/reading_list/reading_list_view_controller.h"
+#import "ios/chrome/browser/ui/uikit_ui_util.h"
+#import "ios/chrome/browser/ui/url_loader.h"
+#import "ios/chrome/browser/ui/util/pasteboard_util.h"
+#include "ios/chrome/grit/ios_strings.h"
+#include "ios/web/public/navigation_manager.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/strings/grit/ui_strings.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
+// Action chosen by the user in the context menu, for UMA report.
+// These match tools/metrics/histograms/histograms.xml.
+enum UMAContextMenuAction {
+ // The user opened the entry in a new tab.
+ NEW_TAB = 0,
+ // The user opened the entry in a new incognito tab.
+ NEW_INCOGNITO_TAB = 1,
+ // The user copied the url of the entry.
+ COPY_LINK = 2,
+ // The user chose to view the offline version of the entry.
+ VIEW_OFFLINE = 3,
+ // The user cancelled the context menu.
+ CANCEL = 4,
+ // Add new enum above ENUM_MAX.
+ ENUM_MAX
+};
+
// Height of the toolbar in normal state.
const int kToolbarNormalHeight = 48;
// Height of the expanded toolbar (buttons on multiple lines).
@@ -30,31 +63,51 @@ typedef NS_ENUM(NSInteger, LayoutPriority) {
ReadingListToolbarHeightDelegate> {
// Toolbar with the actions.
ReadingListToolbar* _toolbar;
- ReadingListViewController* _collectionController;
// This constraint control the expanded mode of the toolbar.
NSLayoutConstraint* _expandedToolbarConstraint;
+ // Coordinator for the alert displayed when the user long presses an entry.
+ AlertCoordinator* _alertCoordinator;
}
+// UrlLoader for navigating to entries.
+@property(nonatomic, weak, readonly) id<UrlLoader> URLLoader;
+@property(nonatomic, strong, readonly)
+ ReadingListViewController* readingListViewController;
+
+// Closes the ReadingList view.
+- (void)dismiss;
+// Opens |URL| in a new tab |incognito| or not.
+- (void)openNewTabWithURL:(const GURL&)URL incognito:(BOOL)incognito;
+// Opens the offline url |offlineURL| of the entry saved in the reading list
+// model with the |entryURL| url.
+- (void)openOfflineURL:(const GURL&)offlineURL
+ correspondingEntryURL:(const GURL&)entryURL
+ fromReadingListViewController:
+ (ReadingListViewController*)readingListViewController;
+
@end
@implementation ReadingListViewControllerContainer
+@synthesize readingListViewController = _readingListViewController;
+@synthesize URLLoader = _URLLoader;
+
- (instancetype)initWithModel:(ReadingListModel*)model
- tabModel:(TabModel*)tabModel
+ loader:(id<UrlLoader>)loader
largeIconService:(favicon::LargeIconService*)largeIconService
readingListDownloadService:
(ReadingListDownloadService*)readingListDownloadService {
self = [super initWithNibName:nil bundle:nil];
if (self) {
+ _URLLoader = loader;
_toolbar = [[ReadingListToolbar alloc] initWithFrame:CGRectZero];
_toolbar.heightDelegate = self;
- _collectionController = [[ReadingListViewController alloc]
+ _readingListViewController = [[ReadingListViewController alloc]
initWithModel:model
- tabModel:tabModel
largeIconService:largeIconService
readingListDownloadService:readingListDownloadService
toolbar:_toolbar];
- _collectionController.audience = self;
+ _readingListViewController.delegate = self;
// Configure modal presentation.
[self setModalPresentationStyle:UIModalPresentationFormSheet];
@@ -64,22 +117,24 @@ typedef NS_ENUM(NSInteger, LayoutPriority) {
}
- (void)viewDidLoad {
- [self addChildViewController:_collectionController];
- [self.view addSubview:[_collectionController view]];
- [_collectionController didMoveToParentViewController:self];
+ [self addChildViewController:self.readingListViewController];
+ [self.view addSubview:self.readingListViewController.view];
+ [self.readingListViewController didMoveToParentViewController:self];
[_toolbar setTranslatesAutoresizingMaskIntoConstraints:NO];
- [[_collectionController view]
+ [self.readingListViewController.view
setTranslatesAutoresizingMaskIntoConstraints:NO];
- NSDictionary* views = @{ @"collection" : [_collectionController view] };
+ NSDictionary* views =
+ @{ @"collection" : self.readingListViewController.view };
NSArray* constraints = @[ @"V:|[collection]", @"H:|[collection]|" ];
ApplyVisualConstraints(constraints, views);
- // This constraints is not required. It will be activated only when the
- // toolbar is not present, allowing the collection to take the whole page.
- NSLayoutConstraint* constraint = [[_collectionController view].bottomAnchor
- constraintEqualToAnchor:[self view].bottomAnchor];
+ // This constraint has a low priority so it will only take effect when the
+ // toolbar isn't present, allowing the collection to take the whole page.
+ NSLayoutConstraint* constraint =
+ [self.readingListViewController.view.bottomAnchor
+ constraintEqualToAnchor:self.view.bottomAnchor];
constraint.priority = LayoutPriorityLow;
constraint.active = YES;
}
@@ -95,15 +150,17 @@ typedef NS_ENUM(NSInteger, LayoutPriority) {
return YES;
}
-#pragma mark - ReadingListViewControllerAudience
+#pragma mark - ReadingListViewControllerDelegate
-- (void)setCollectionHasItems:(BOOL)hasItems {
+- (void)readingListViewController:
+ (ReadingListViewController*)readingListViewController
+ hasItems:(BOOL)hasItems {
if (hasItems) {
// If there are items, add the toolbar.
[self.view addSubview:_toolbar];
NSDictionary* views = @{
@"toolbar" : _toolbar,
- @"collection" : [_collectionController view]
+ @"collection" : readingListViewController.view
};
NSArray* constraints = @[ @"V:[collection][toolbar]|", @"H:|[toolbar]|" ];
ApplyVisualConstraints(constraints, views);
@@ -122,27 +179,137 @@ typedef NS_ENUM(NSInteger, LayoutPriority) {
}
}
-- (void)dismiss {
- [self.presentingViewController dismissViewControllerAnimated:YES
- completion:nil];
+- (void)dismissReadingListViewController:
+ (ReadingListViewController*)readingListViewController {
+ [readingListViewController willBeDismissed];
+ [self dismiss];
+}
+
+- (void)
+readingListViewController:(ReadingListViewController*)readingListViewController
+displayContextMenuForItem:(ReadingListCollectionViewItem*)readingListItem
+ atPoint:(CGPoint)menuLocation {
+ const ReadingListEntry* entry =
+ readingListViewController.readingListModel->GetEntryByURL(
+ readingListItem.url);
+
+ if (!entry) {
+ [readingListViewController reloadData];
+ return;
+ }
+ const GURL entryURL = entry->URL();
+
+ __weak ReadingListViewControllerContainer* weakSelf = self;
+
+ _alertCoordinator = [[ActionSheetCoordinator alloc]
+ initWithBaseViewController:self
+ title:readingListItem.text
+ message:readingListItem.detailText
+ rect:CGRectMake(menuLocation.x, menuLocation.y, 0,
+ 0)
+ view:readingListViewController.collectionView];
+
+ NSString* openInNewTabTitle =
+ l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWTAB);
+ [_alertCoordinator
+ addItemWithTitle:openInNewTabTitle
+ action:^{
+ [weakSelf openNewTabWithURL:entryURL incognito:NO];
+ UMA_HISTOGRAM_ENUMERATION("ReadingList.ContextMenu", NEW_TAB,
+ ENUM_MAX);
+
+ }
+ style:UIAlertActionStyleDefault];
+
+ NSString* openInNewTabIncognitoTitle =
+ l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWINCOGNITOTAB);
+ [_alertCoordinator
+ addItemWithTitle:openInNewTabIncognitoTitle
+ action:^{
+ UMA_HISTOGRAM_ENUMERATION("ReadingList.ContextMenu",
+ NEW_INCOGNITO_TAB, ENUM_MAX);
+ [weakSelf openNewTabWithURL:entryURL incognito:YES];
+ }
+ style:UIAlertActionStyleDefault];
+
+ NSString* copyLinkTitle =
+ l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_COPY);
+ [_alertCoordinator
+ addItemWithTitle:copyLinkTitle
+ action:^{
+ UMA_HISTOGRAM_ENUMERATION("ReadingList.ContextMenu",
+ COPY_LINK, ENUM_MAX);
+ StoreURLInPasteboard(entryURL);
+ }
+ style:UIAlertActionStyleDefault];
+
+ if (entry->DistilledState() == ReadingListEntry::PROCESSED) {
+ GURL offlineURL = reading_list::OfflineURLForPath(
+ entry->DistilledPath(), entryURL, entry->DistilledURL());
+ NSString* viewOfflineVersionTitle =
+ l10n_util::GetNSString(IDS_IOS_READING_LIST_CONTENT_CONTEXT_OFFLINE);
+ [_alertCoordinator
+ addItemWithTitle:viewOfflineVersionTitle
+ action:^{
+ UMA_HISTOGRAM_ENUMERATION("ReadingList.ContextMenu",
+ VIEW_OFFLINE, ENUM_MAX);
+ [weakSelf openOfflineURL:offlineURL
+ correspondingEntryURL:entryURL
+ fromReadingListViewController:
+ readingListViewController];
+ }
+ style:UIAlertActionStyleDefault];
+ }
+
+ [_alertCoordinator
+ addItemWithTitle:l10n_util::GetNSString(IDS_APP_CANCEL)
+ action:^{
+ UMA_HISTOGRAM_ENUMERATION("ReadingList.ContextMenu", CANCEL,
+ ENUM_MAX);
+ }
+ style:UIAlertActionStyleCancel];
+
+ [_alertCoordinator start];
+}
+
+- (void)
+readingListViewController:(ReadingListViewController*)readingListViewController
+ openItem:(ReadingListCollectionViewItem*)readingListItem {
+ const ReadingListEntry* entry =
+ readingListViewController.readingListModel->GetEntryByURL(
+ readingListItem.url);
+
+ if (!entry) {
+ [readingListViewController reloadData];
+ return;
+ }
+
+ base::RecordAction(base::UserMetricsAction("MobileReadingListOpen"));
+
+ [self.URLLoader loadURL:entry->URL()
+ referrer:web::Referrer()
+ transition:ui::PAGE_TRANSITION_AUTO_BOOKMARK
+ rendererInitiated:NO];
+
+ [self dismiss];
}
#pragma mark - ReadingListToolbarActionTarget
- (void)markPressed {
- [_collectionController markPressed];
+ [self.readingListViewController markPressed];
}
- (void)deletePressed {
- [_collectionController deletePressed];
+ [self.readingListViewController deletePressed];
}
- (void)enterEditingModePressed {
- [_collectionController enterEditingModePressed];
+ [self.readingListViewController enterEditingModePressed];
}
- (void)exitEditingModePressed {
- [_collectionController exitEditingModePressed];
+ [self.readingListViewController exitEditingModePressed];
}
#pragma mark - ReadingListToolbarHeightDelegate
@@ -161,6 +328,39 @@ typedef NS_ENUM(NSInteger, LayoutPriority) {
});
}
+#pragma mark - Private
+
+- (void)dismiss {
+ [self.presentingViewController dismissViewControllerAnimated:YES
+ completion:nil];
+}
+
+- (void)openNewTabWithURL:(const GURL&)URL incognito:(BOOL)incognito {
+ base::RecordAction(base::UserMetricsAction("MobileReadingListOpen"));
+
+ [self.URLLoader webPageOrderedOpen:URL
+ referrer:web::Referrer()
+ windowName:nil
+ inIncognito:incognito
+ inBackground:NO
+ appendTo:kLastTab];
+
+ [self dismiss];
+}
+
+- (void)openOfflineURL:(const GURL&)offlineURL
+ correspondingEntryURL:(const GURL&)entryURL
+ fromReadingListViewController:
+ (ReadingListViewController*)readingListViewController {
+ [readingListViewController willBeDismissed];
+
+ [self openNewTabWithURL:offlineURL incognito:NO];
+
+ UMA_HISTOGRAM_BOOLEAN("ReadingList.OfflineVersionDisplayed", true);
+ const GURL updateURL = entryURL;
+ readingListViewController.readingListModel->SetReadStatus(updateURL, true);
+}
+
#pragma mark - UIResponder
- (NSArray*)keyCommands {

Powered by Google App Engine
This is Rietveld 408576698