0

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:
Mike Dougherty
2021-06-25 16:47:53 +00:00
committed by Chromium LUCI CQ
parent 2cd04ef076
commit 54763f7886
18 changed files with 157 additions and 22 deletions

@ -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>