Implement sharing of multiple tabs from tab grid
Add a GridCommand to share multiple items. Extend the tab grid UI to
trigger sharing of multiple selected tabs from selection mode when the
sharing button on the bottom toolbar is tapped.
Additionally, extend ActivityServiceCoordinator and SharingCoordinator
to allow presentation of the activity sharing sheet from a
UIBarButtonItem.
Bug: 1196940, 1196941
Change-Id: I5a1a81d01463b830a2751b164167d36b483107dd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2976108
Commit-Queue: Mike Dougherty <michaeldo@chromium.org>
Reviewed-by: Mark Cogan <marq@chromium.org>
Reviewed-by: Tomasz Wiszkowski <ender@google.com>
Cr-Commit-Position: refs/heads/master@{#896092}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
2cd04ef076
commit
54763f7886
ios/chrome/browser/ui
activity_services
browser_view
sharing
tab_switcher
tools/metrics/histograms
@ -18,8 +18,9 @@ enum class ActivityScenario {
|
||||
RecentTabsEntry = 6,
|
||||
SharedHighlight = 7,
|
||||
TabGridItem = 8,
|
||||
TabGridSelectionMode = 9,
|
||||
// Highest enumerator. Recommended by Histogram metrics best practices.
|
||||
kMaxValue = TabGridItem
|
||||
kMaxValue = TabGridSelectionMode
|
||||
};
|
||||
|
||||
#endif // IOS_CHROME_BROWSER_UI_ACTIVITY_SERVICES_ACTIVITY_SCENARIO_H_
|
||||
|
@ -127,10 +127,16 @@
|
||||
|
||||
// Set-up popover positioning (for iPad).
|
||||
DCHECK(self.positionProvider);
|
||||
self.viewController.popoverPresentationController.sourceView =
|
||||
self.positionProvider.sourceView;
|
||||
self.viewController.popoverPresentationController.sourceRect =
|
||||
self.positionProvider.sourceRect;
|
||||
if ([self.positionProvider respondsToSelector:@selector(barButtonItem)] &&
|
||||
self.positionProvider.barButtonItem) {
|
||||
self.viewController.popoverPresentationController.barButtonItem =
|
||||
self.positionProvider.barButtonItem;
|
||||
} else {
|
||||
self.viewController.popoverPresentationController.sourceView =
|
||||
self.positionProvider.sourceView;
|
||||
self.viewController.popoverPresentationController.sourceRect =
|
||||
self.positionProvider.sourceRect;
|
||||
}
|
||||
|
||||
// Set completion callback.
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
|
@ -34,6 +34,8 @@ const char kShareSharedHighlightActionsHistogram[] =
|
||||
"Mobile.Share.SharedHighlight.Actions";
|
||||
const char kShareTabGridItemActionsHistogram[] =
|
||||
"Mobile.Share.TabGridItem.Actions";
|
||||
const char kShareTabGridSelectionModeActionsHistogram[] =
|
||||
"Mobile.Share.TabGridSelectionMode.Actions";
|
||||
|
||||
// Enum representing an aggregation of the |ActivityType| enum values in a way
|
||||
// that is relevant for metric collection. Current values should not
|
||||
@ -163,6 +165,9 @@ void RecordActionForScenario(ShareActionType actionType,
|
||||
case ActivityScenario::TabGridItem:
|
||||
histogramName = kShareTabGridItemActionsHistogram;
|
||||
break;
|
||||
case ActivityScenario::TabGridSelectionMode:
|
||||
histogramName = kShareTabGridSelectionModeActionsHistogram;
|
||||
break;
|
||||
}
|
||||
base::UmaHistogramEnumeration(histogramName, actionType);
|
||||
}
|
||||
|
@ -9,16 +9,23 @@
|
||||
|
||||
// ActivityServicePositioner contains methods that are used to position the
|
||||
// activity services menu on the screen.
|
||||
@protocol ActivityServicePositioner
|
||||
@protocol ActivityServicePositioner <NSObject>
|
||||
|
||||
// Returns the view where the UIActivityViewController
|
||||
// should be presented.
|
||||
// should be presented. This property is ignored if a barButtonItem is set.
|
||||
- (UIView*)sourceView;
|
||||
|
||||
// Returns the bounds where the UIActivityViewController's popover should be
|
||||
// presented.
|
||||
// presented. This property is ignored if a barButtonItem is set.
|
||||
- (CGRect)sourceRect;
|
||||
|
||||
@optional
|
||||
|
||||
// Returns the bar button item where the UIActivityViewController should be
|
||||
// presented from. If a non null value is returned, |sourceView| and
|
||||
// |sourceRect| are not used.
|
||||
- (UIBarButtonItem*)barButtonItem;
|
||||
|
||||
@end
|
||||
|
||||
#endif // IOS_CHROME_BROWSER_UI_ACTIVITY_SERVICES_REQUIREMENTS_ACTIVITY_SERVICE_POSITIONER_H_
|
||||
|
@ -558,6 +558,12 @@
|
||||
// Exit fullscreen if needed to make sure that share button is visible.
|
||||
FullscreenController::FromBrowser(self.browser)->ExitFullscreen();
|
||||
|
||||
UIBarButtonItem* anchor = nil;
|
||||
if ([self.viewController.activityServicePositioner
|
||||
respondsToSelector:@selector(barButtonItem)]) {
|
||||
anchor = self.viewController.activityServicePositioner.barButtonItem;
|
||||
}
|
||||
|
||||
self.sharingCoordinator = [[SharingCoordinator alloc]
|
||||
initWithBaseViewController:self.viewController
|
||||
browser:self.browser
|
||||
@ -565,7 +571,8 @@
|
||||
originView:self.viewController.activityServicePositioner
|
||||
.sourceView
|
||||
originRect:self.viewController.activityServicePositioner
|
||||
.sourceRect];
|
||||
.sourceRect
|
||||
anchor:anchor];
|
||||
[self.sharingCoordinator start];
|
||||
}
|
||||
|
||||
@ -576,12 +583,13 @@
|
||||
additionalText:command.selectedText
|
||||
scenario:ActivityScenario::SharedHighlight];
|
||||
|
||||
self.sharingCoordinator = [[SharingCoordinator alloc]
|
||||
initWithBaseViewController:self.viewController
|
||||
browser:self.browser
|
||||
params:params
|
||||
originView:command.sourceView
|
||||
originRect:command.sourceRect];
|
||||
self.sharingCoordinator =
|
||||
[[SharingCoordinator alloc] initWithBaseViewController:self.viewController
|
||||
browser:self.browser
|
||||
params:params
|
||||
originView:command.sourceView
|
||||
originRect:command.sourceRect
|
||||
anchor:nil];
|
||||
[self.sharingCoordinator start];
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,8 @@ TEST_F(BrowserCoordinatorTest, SharePage) {
|
||||
browser:browser_.get()
|
||||
params:[OCMArg any]
|
||||
originView:[OCMArg any]
|
||||
originRect:CGRectZero])
|
||||
originRect:CGRectZero
|
||||
anchor:[OCMArg any]])
|
||||
.andReturn(mockSharingCoordinator);
|
||||
OCMExpect([mockSharingCoordinator start]);
|
||||
|
||||
|
@ -27,16 +27,25 @@ class Browser;
|
||||
params:(ActivityParams*)params
|
||||
originView:(UIView*)originView;
|
||||
|
||||
// Creates a coordinator configured to share the URLs specified in |params|.
|
||||
// This initializer uses |barButtonItem| to position the activity view popover
|
||||
// on iPad.
|
||||
- (instancetype)initWithBaseViewController:(UIViewController*)viewController
|
||||
browser:(Browser*)browser
|
||||
params:(ActivityParams*)params
|
||||
anchor:(UIBarButtonItem*)barButtonItem;
|
||||
|
||||
// Creates a coordinator configured to share the current tab's URL using the
|
||||
// base |viewController|, a |browser|, |params| with all the necessary values
|
||||
// to drive the scenario, and an |originView| from which the scenario was
|
||||
// triggered. This initializer also uses the |originRect| to position the
|
||||
// activity view popover on iPad.
|
||||
// to drive the scenario. If |barButtonItem| is non-null, it will be used
|
||||
// to present the activity view popover on iPad. Otherwise, |originView| and
|
||||
// |originRect| will be used to position the activity view popover on iPad.
|
||||
- (instancetype)initWithBaseViewController:(UIViewController*)viewController
|
||||
browser:(Browser*)browser
|
||||
params:(ActivityParams*)params
|
||||
originView:(UIView*)originView
|
||||
originRect:(CGRect)originRect
|
||||
anchor:(UIBarButtonItem*)barButtonItem
|
||||
NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
@ -48,6 +48,8 @@ using bookmarks::BookmarkNode;
|
||||
|
||||
@property(nonatomic, assign) CGRect originRect;
|
||||
|
||||
@property(nonatomic, weak) UIBarButtonItem* anchor;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SharingCoordinator
|
||||
@ -56,13 +58,27 @@ using bookmarks::BookmarkNode;
|
||||
browser:(Browser*)browser
|
||||
params:(ActivityParams*)params
|
||||
originView:(UIView*)originView {
|
||||
DCHECK(params);
|
||||
DCHECK(originView);
|
||||
self = [self initWithBaseViewController:viewController
|
||||
browser:browser
|
||||
params:params
|
||||
originView:originView
|
||||
originRect:originView.bounds];
|
||||
originRect:originView.bounds
|
||||
anchor:nil];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBaseViewController:(UIViewController*)viewController
|
||||
browser:(Browser*)browser
|
||||
params:(ActivityParams*)params
|
||||
anchor:(UIBarButtonItem*)anchor {
|
||||
DCHECK(anchor);
|
||||
self = [self initWithBaseViewController:viewController
|
||||
browser:browser
|
||||
params:params
|
||||
originView:nil
|
||||
originRect:CGRectZero
|
||||
anchor:anchor];
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -70,13 +86,15 @@ using bookmarks::BookmarkNode;
|
||||
browser:(Browser*)browser
|
||||
params:(ActivityParams*)params
|
||||
originView:(UIView*)originView
|
||||
originRect:(CGRect)originRect {
|
||||
originRect:(CGRect)originRect
|
||||
anchor:(UIBarButtonItem*)anchor {
|
||||
DCHECK(params);
|
||||
if (self = [super initWithBaseViewController:viewController
|
||||
browser:browser]) {
|
||||
_params = params;
|
||||
_originView = originView;
|
||||
_originRect = originRect;
|
||||
_anchor = anchor;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@ -113,6 +131,10 @@ using bookmarks::BookmarkNode;
|
||||
return self.originRect;
|
||||
}
|
||||
|
||||
- (UIBarButtonItem*)barButtonItem {
|
||||
return self.anchor;
|
||||
}
|
||||
|
||||
#pragma mark - ActivityServicePresentation
|
||||
|
||||
- (void)activityServiceDidEndPresenting {
|
||||
|
@ -40,6 +40,7 @@ source_set("tab_grid") {
|
||||
"//ios/chrome/browser/tabs",
|
||||
"//ios/chrome/browser/ui:feature_flags",
|
||||
"//ios/chrome/browser/ui/activity_services",
|
||||
"//ios/chrome/browser/ui/activity_services/data",
|
||||
"//ios/chrome/browser/ui/alert_coordinator",
|
||||
"//ios/chrome/browser/ui/bookmarks",
|
||||
"//ios/chrome/browser/ui/commands",
|
||||
|
@ -51,6 +51,9 @@
|
||||
showCloseItemsConfirmationActionSheetWithItems:(NSArray<NSString*>*)items
|
||||
anchor:
|
||||
(UIBarButtonItem*)buttonAnchor;
|
||||
// Shows a share sheet to share |items|, anchored to the |buttonAnchor|.
|
||||
- (void)shareItems:(NSArray<NSString*>*)items
|
||||
anchor:(UIBarButtonItem*)buttonAnchor;
|
||||
@end
|
||||
|
||||
#endif // IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_GRID_GRID_COMMANDS_H_
|
||||
|
@ -58,6 +58,11 @@
|
||||
// Set |enabled| on the close tabs button.
|
||||
- (void)setCloseTabsButtonEnabled:(BOOL)enabled;
|
||||
|
||||
// Sets target/action for tapping event on share tabs button.
|
||||
- (void)setShareTabsButtonTarget:(id)target action:(SEL)action;
|
||||
// Set |enabled| on the close tabs button.
|
||||
- (void)setShareTabsButtonEnabled:(BOOL)enabled;
|
||||
|
||||
// Hides components and uses a black background color for tab grid transition
|
||||
// animation.
|
||||
- (void)hide;
|
||||
|
@ -180,6 +180,16 @@
|
||||
_closeTabsButton.enabled = enabled;
|
||||
}
|
||||
|
||||
#pragma mark Share Tabs
|
||||
|
||||
- (void)setShareTabsButtonTarget:(id)target action:(SEL)action {
|
||||
_shareButton.target = target;
|
||||
_shareButton.action = action;
|
||||
}
|
||||
- (void)setShareTabsButtonEnabled:(BOOL)enabled {
|
||||
_shareButton.enabled = enabled;
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)setupViews {
|
||||
|
@ -50,6 +50,7 @@
|
||||
#import "ios/chrome/browser/ui/snackbar/snackbar_coordinator.h"
|
||||
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_commands.h"
|
||||
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_context_menu_helper.h"
|
||||
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_item.h"
|
||||
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator_delegate.h"
|
||||
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator.h"
|
||||
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_paging.h"
|
||||
@ -860,6 +861,21 @@
|
||||
[self.actionSheetCoordinator start];
|
||||
}
|
||||
|
||||
- (void)tabGridMediator:(TabGridMediator*)tabGridMediator
|
||||
shareURLs:(NSArray<URLWithTitle*>*)URLs
|
||||
anchor:(UIBarButtonItem*)buttonAnchor {
|
||||
ActivityParams* params = [[ActivityParams alloc]
|
||||
initWithURLs:URLs
|
||||
scenario:ActivityScenario::TabGridSelectionMode];
|
||||
|
||||
self.sharingCoordinator = [[SharingCoordinator alloc]
|
||||
initWithBaseViewController:self.baseViewController
|
||||
browser:self.regularBrowser
|
||||
params:params
|
||||
anchor:buttonAnchor];
|
||||
[self.sharingCoordinator start];
|
||||
}
|
||||
|
||||
#pragma mark - TabGridViewControllerDelegate
|
||||
|
||||
- (TabGridPage)activePageForTabGridViewController:
|
||||
|
@ -15,6 +15,7 @@
|
||||
class Browser;
|
||||
@protocol GridConsumer;
|
||||
@class TabGridMediator;
|
||||
@class URLWithTitle;
|
||||
|
||||
namespace sessions {
|
||||
class TabRestoreService;
|
||||
@ -42,6 +43,10 @@ class TabRestoreService;
|
||||
anchor:(UIBarButtonItem*)
|
||||
buttonAnchor;
|
||||
|
||||
- (void)tabGridMediator:(TabGridMediator*)tabGridMediator
|
||||
shareURLs:(NSArray<URLWithTitle*>*)items
|
||||
anchor:(UIBarButtonItem*)buttonAnchor;
|
||||
|
||||
@end
|
||||
|
||||
// Mediates between model layer and tab grid UI layer.
|
||||
|
@ -30,6 +30,7 @@
|
||||
#import "ios/chrome/browser/snapshots/snapshot_tab_helper.h"
|
||||
#include "ios/chrome/browser/system_flags.h"
|
||||
#import "ios/chrome/browser/tabs/tab_title_util.h"
|
||||
#import "ios/chrome/browser/ui/activity_services/data/url_with_title.h"
|
||||
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_consumer.h"
|
||||
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_item.h"
|
||||
#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_item.h"
|
||||
@ -441,6 +442,19 @@ web::WebState* GetWebStateWithId(WebStateList* web_state_list,
|
||||
anchor:buttonAnchor];
|
||||
}
|
||||
|
||||
- (void)shareItems:(NSArray<NSString*>*)items
|
||||
anchor:(UIBarButtonItem*)buttonAnchor {
|
||||
NSMutableArray<URLWithTitle*>* URLs = [[NSMutableArray alloc] init];
|
||||
for (NSString* itemIdentifier in items) {
|
||||
GridItem* item = [self gridItemForCellIdentifier:itemIdentifier];
|
||||
URLWithTitle* URL = [[URLWithTitle alloc] initWithURL:item.URL
|
||||
title:item.title];
|
||||
[URLs addObject:URL];
|
||||
}
|
||||
|
||||
[self.delegate tabGridMediator:self shareURLs:URLs anchor:buttonAnchor];
|
||||
}
|
||||
|
||||
#pragma mark GridCommands helpers
|
||||
|
||||
- (void)insertNewItemAtIndex:(NSUInteger)index withURL:(const GURL&)newTabURL {
|
||||
|
@ -1236,6 +1236,8 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) {
|
||||
action:@selector(newTabButtonTapped:)];
|
||||
[bottomToolbar setCloseTabsButtonTarget:self
|
||||
action:@selector(closeSelectedTabs:)];
|
||||
[bottomToolbar setShareTabsButtonTarget:self
|
||||
action:@selector(shareSelectedTabs:)];
|
||||
|
||||
NamedGuide* guide =
|
||||
[[NamedGuide alloc] initWithName:kTabGridBottomToolbarGuide];
|
||||
@ -1947,6 +1949,24 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) {
|
||||
}
|
||||
}
|
||||
|
||||
- (void)shareSelectedTabs:(id)sender {
|
||||
GridViewController* gridViewController =
|
||||
[self gridViewControllerForPage:self.currentPage];
|
||||
NSArray<NSString*>* items = gridViewController.selectedItemIDsForEditing;
|
||||
|
||||
switch (self.currentPage) {
|
||||
case TabGridPageIncognitoTabs:
|
||||
[self.incognitoTabsDelegate shareItems:items anchor:sender];
|
||||
break;
|
||||
case TabGridPageRegularTabs:
|
||||
[self.regularTabsDelegate shareItems:items anchor:sender];
|
||||
break;
|
||||
case TabGridPageRemoteTabs:
|
||||
NOTREACHED() << "Multiple tab selection invalid on remote tabs.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pageControlChangedValue:(id)sender {
|
||||
// Map the page control slider position (in the range 0.0-1.0) to an
|
||||
// x-offset for the scroll view.
|
||||
|
@ -42002,6 +42002,7 @@ Called by update_gpu_driver_bug_workaround_entries.py.-->
|
||||
<int value="6" label="Recent Tabs Entry"/>
|
||||
<int value="7" label="Shared Highlight"/>
|
||||
<int value="8" label="Tab Grid Item"/>
|
||||
<int value="9" label="Tab Grid Selection Mode"/>
|
||||
</enum>
|
||||
|
||||
<enum name="IOSAppState">
|
||||
|
@ -387,6 +387,7 @@ reviews. Googlers can read more about this at go/gwsq-gerrit.
|
||||
<variant name="RecentTabsEntry" summary="Recent Tabs entry"/>
|
||||
<variant name="SharedHighlight" summary="highlighted fragment"/>
|
||||
<variant name="TabGridItem" summary="tab grid item"/>
|
||||
<variant name="TabGridSelectionMode" summary="tab grid selection mode"/>
|
||||
<variant name="TabShareButton" summary="current tab"/>
|
||||
</token>
|
||||
</histogram>
|
||||
|
Reference in New Issue
Block a user