Commit db8b48d4 authored by Kyle Milka's avatar Kyle Milka Committed by Commit Bot

[SharingHub] Refactor existing share/ code

Refactor existing code for handling share actions (ShareHelper and
ShareMenuActionHandler) to better fit the MVC model. This is a starting
point for work on the Chrome sharing hub/custom share sheet.

go/chrome-sharing-hub-dd

Bug: 1009124
Change-Id: I6f3825fed98d896507f44d033d953b54cd32131f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1850367
Commit-Queue: Kyle Milka <kmilka@chromium.org>
Reviewed-by: default avatarDavid Trainor <dtrainor@chromium.org>
Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#710020}
parent 9981a89a
......@@ -1454,8 +1454,9 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/share/ShareActivity.java",
"java/src/org/chromium/chrome/browser/share/ShareDialogAdapter.java",
"java/src/org/chromium/chrome/browser/share/ShareHelper.java",
"java/src/org/chromium/chrome/browser/share/ShareMenuActionHandler.java",
"java/src/org/chromium/chrome/browser/share/ShareParams.java",
"java/src/org/chromium/chrome/browser/share/ShareSheetCoordinator.java",
"java/src/org/chromium/chrome/browser/share/ShareSheetMediator.java",
"java/src/org/chromium/chrome/browser/sharing/SharingAdapter.java",
"java/src/org/chromium/chrome/browser/sharing/SharingNotificationUtil.java",
"java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandler.java",
......
......@@ -421,8 +421,8 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/services/GoogleServicesManagerIntegrationTest.java",
"javatests/src/org/chromium/chrome/browser/shape_detection/ShapeDetectionTest.java",
"javatests/src/org/chromium/chrome/browser/share/LensUtilsTest.java",
"javatests/src/org/chromium/chrome/browser/share/ShareMenuActionHandlerIntegrationTest.java",
"javatests/src/org/chromium/chrome/browser/share/ShareMenuActionHandlerTest.java",
"javatests/src/org/chromium/chrome/browser/share/ShareSheetMediatorIntegrationTest.java",
"javatests/src/org/chromium/chrome/browser/share/ShareSheetMediatorTest.java",
"javatests/src/org/chromium/chrome/browser/share/ShareUrlTest.java",
"javatests/src/org/chromium/chrome/browser/signin/IdentityManagerIntegrationTest.java",
"javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceTest.java",
......
......@@ -145,7 +145,7 @@ def _CheckCompatibleAlertDialogBuilder(input_api, output_api):
BROWSER_ROOT + 'externalnav/ExternalNavigationDelegateImpl.java',
BROWSER_ROOT + 'payments/AndroidPaymentApp.java',
BROWSER_ROOT + 'permissions/AndroidPermissionRequester.java',
BROWSER_ROOT + 'share/ShareHelper.java',
BROWSER_ROOT + 'share/ShareSheetMediator.java',
BROWSER_ROOT + 'util/AccessibilityUtil.java',
BROWSER_ROOT + 'webapps/AddToHomescreenDialog.java',
BROWSER_ROOT + 'webapps/WebappOfflineDialog.java',
......
......@@ -124,7 +124,6 @@ import org.chromium.chrome.browser.preferences.PrefServiceBridge;
import org.chromium.chrome.browser.preferences.PreferencesLauncher;
import org.chromium.chrome.browser.printing.TabPrinter;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.share.ShareMenuActionHandler;
import org.chromium.chrome.browser.snackbar.BottomContainer;
import org.chromium.chrome.browser.snackbar.SnackbarManager;
import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarManageable;
......@@ -1559,19 +1558,6 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent>
return super.onOptionsItemSelected(item);
}
/**
* Triggered when the share menu item is selected.
* This creates and shows a share intent picker dialog or starts a share intent directly.
* @param shareDirectly Whether it should share directly with the activity that was most
* recently used to share.
* @param isIncognito Whether currentTab is incognito.
*/
@VisibleForTesting
public void onShareMenuItemSelected(final boolean shareDirectly, final boolean isIncognito) {
ShareMenuActionHandler.getInstance().onShareMenuItemSelected(
this, getActivityTab(), shareDirectly, isIncognito);
}
/**
* @return Whether the activity is in overview mode.
*/
......@@ -2197,9 +2183,6 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent>
Tracker tracker = TrackerFactory.getTrackerForProfile(getActivityTab().getProfile());
tracker.notifyEvent(EventConstants.TRANSLATE_MENU_BUTTON_CLICKED);
TranslateBridge.translateTabWhenReady(getActivityTab());
} else if (id == R.id.share_menu_id || id == R.id.direct_share_menu_id) {
onShareMenuItemSelected(id == R.id.direct_share_menu_id,
getCurrentTabModel().isIncognito());
} else if (id == R.id.print_id) {
PrintingController printingController = PrintingControllerImpl.getInstance();
if (printingController != null && !printingController.isBusy()
......
......@@ -25,8 +25,8 @@ import org.chromium.chrome.browser.locale.LocaleManager;
import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
import org.chromium.chrome.browser.share.LensUtils;
import org.chromium.chrome.browser.share.ShareHelper;
import org.chromium.chrome.browser.share.ShareParams;
import org.chromium.chrome.browser.share.ShareSheetCoordinator;
import org.chromium.chrome.browser.util.UrlUtilities;
import org.chromium.components.search_engines.TemplateUrlService;
import org.chromium.components.url_formatter.UrlFormatter;
......@@ -543,7 +543,7 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator {
.setShareDirectly(false)
.setSaveLastUsed(true)
.build();
ShareHelper.share(linkShareParams);
ShareSheetCoordinator.create().share(linkShareParams);
} else if (itemId == R.id.contextmenu_search_with_google_lens) {
ContextMenuUma.record(params, ContextMenuUma.Action.SEARCH_WITH_GOOGLE_LENS);
helper.searchWithGoogleLens(mDelegate.isIncognito());
......
......@@ -233,7 +233,7 @@ public class RevampedContextMenuCoordinator implements ContextMenuUi {
.setShareDirectly(true)
.setSaveLastUsed(false)
.build();
ShareHelper.share(shareParams);
ShareHelper.shareDirectly(shareParams);
} else {
mOnShareImageDirectly.run();
}
......
......@@ -28,7 +28,7 @@ import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
import org.chromium.chrome.browser.omnibox.suggestions.SuggestionProcessor;
import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.share.ShareMenuActionHandler;
import org.chromium.chrome.browser.share.ShareSheetCoordinator;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.ui.base.Clipboard;
import org.chromium.ui.modelutil.PropertyModel;
......@@ -305,7 +305,7 @@ public class EditUrlSuggestionProcessor implements OnClickListener, SuggestionPr
mLocationBarDelegate.clearOmniboxFocus();
// TODO(mdjones): This should only share the displayed URL instead of the background
// tab.
ShareMenuActionHandler.getInstance().onShareMenuItemSelected(
ShareSheetCoordinator.create().onShareSelected(
activityTab.getActivity(), activityTab, false, activityTab.isIncognito());
} else if (R.id.url_edit_icon == view.getId()) {
ENUMERATED_SUGGESTION_ACTION.record(SuggestionAction.EDIT);
......
......@@ -323,27 +323,15 @@ public class ShareHelper {
}
/**
* Creates and shows a share intent picker dialog or starts a share intent directly with the
* activity that was most recently used to share based on shareDirectly value.
*
* This function will save |screenshot| under {app's root}/files/images/screenshot (or
* /sdcard/DCIM/browser-images/screenshot if ADK is lower than JB MR2).
* Cleaning up doesn't happen automatically, and so an app should call clearSharedScreenshots()
* explicitly when needed.
*
* Share directly with the last used share target.
* @param params The container holding the share parameters.
*/
public static void share(ShareParams params) {
if (params.shareDirectly()) {
ComponentName component = getLastShareComponentName(params.getSourcePackageName());
if (component == null) return;
assert params.getCallback() == null;
makeIntentAndShare(params, component);
} else if (TargetChosenReceiver.isSupported()) {
makeIntentAndShare(params, null);
} else {
showShareDialog(params);
}
public static void shareDirectly(ShareParams params) {
assert params.shareDirectly();
ComponentName component = getLastShareComponentName(params.getSourcePackageName());
if (component == null) return;
assert params.getCallback() == null;
makeIntentAndShare(params, component);
}
/**
......@@ -512,7 +500,7 @@ public class ShareHelper {
*
* @param params The container holding the share parameters.
*/
private static void showShareDialog(final ShareParams params) {
static void showShareDialog(final ShareParams params) {
Activity activity = params.getWindow().getActivity().get();
final TargetChosenCallback callback = params.getCallback();
Intent intent = getShareLinkAppCompatibilityIntent();
......@@ -572,7 +560,7 @@ public class ShareHelper {
}
}
private static void makeIntentAndShare(ShareParams params, @Nullable ComponentName component) {
static void makeIntentAndShare(ShareParams params, @Nullable ComponentName component) {
Intent intent = getShareLinkIntent(params);
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
intent.setComponent(component);
......
// Copyright 2019 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.
package org.chromium.chrome.browser.share;
import android.app.Activity;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.browser.tab.Tab;
/**
* Coordinator for displaying the share sheet.
*/
public class ShareSheetCoordinator {
private ShareSheetMediator mMediator;
/**
* Create and return a new ShareSheetCoordinator.
*/
public static ShareSheetCoordinator create() {
return new ShareSheetCoordinator();
}
/**
* Constructs a new ShareSheetCoordinator.
*/
private ShareSheetCoordinator() {
mMediator = new ShareSheetMediator(new ShareSheetMediator.ShareSheetDelegate());
}
/**
* Triggered when the share menu item is selected.
* This creates and shows a share intent picker dialog or starts a share intent directly.
* @param activity The activity that triggered this share action.
* @param tab The tab containing the content to be shared.
* @param shareDirectly Whether it should share directly with the activity that was most
* recently used to share.
* @param isIncognito Whether currentTab is incognito.
*/
public void onShareSelected(
Activity activity, Tab currentTab, boolean shareDirectly, boolean isIncognito) {
mMediator.onShareSelected(activity, currentTab, shareDirectly, isIncognito);
}
/**
* Triggers a share based on the provided {@link ShareParams}.
* @param params The container holding the share parameters.
*/
public void share(ShareParams params) {
mMediator.share(params);
}
@VisibleForTesting
public static void setScreenshotCaptureSkippedForTesting(boolean value) {
ShareSheetMediator.setScreenshotCaptureSkippedForTesting(value);
}
}
// Copyright 2017 The Chromium Authors. All rights reserved.
// Copyright 2019 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.
......@@ -12,6 +12,7 @@ import org.chromium.base.Callback;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.feature_engagement.ScreenshotTabObserver;
import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
import org.chromium.chrome.browser.printing.PrintShareActivity;
......@@ -30,36 +31,22 @@ import java.util.ArrayList;
import java.util.List;
/**
* Handles the action of selecting the share item in the menu.
* Handles displaying the share sheet. The version used depends on several
* conditions.
* Android K and below: custom share dialog
* Android L+: system share sheet
* #chrome-sharing-hub enabled: custom share sheet
*/
public class ShareMenuActionHandler {
private static boolean sScreenshotCaptureSkippedForTesting;
private static ShareMenuActionHandler sInstance;
private final ShareMenuActionDelegate mDelegate;
class ShareSheetMediator {
static final String CANONICAL_URL_RESULT_HISTOGRAM = "Mobile.CanonicalURLResult";
/**
* @return The singleton share menu handler.
*/
public static ShareMenuActionHandler getInstance() {
if (sInstance == null) {
sInstance = new ShareMenuActionHandler(new ShareMenuActionDelegate());
}
return sInstance;
}
private static boolean sScreenshotCaptureSkippedForTesting;
private final ShareSheetDelegate mDelegate;
@VisibleForTesting
ShareMenuActionHandler(ShareMenuActionDelegate delegate) {
ShareSheetMediator(ShareSheetDelegate delegate) {
mDelegate = delegate;
}
@VisibleForTesting
public static void setScreenshotCaptureSkippedForTesting(boolean value) {
sScreenshotCaptureSkippedForTesting = value;
}
/**
* Triggered when the share menu item is selected.
* This creates and shows a share intent picker dialog or starts a share intent directly.
......@@ -67,7 +54,7 @@ public class ShareMenuActionHandler {
* recently used to share.
* @param isIncognito Whether currentTab is incognito.
*/
public void onShareMenuItemSelected(
public void onShareSelected(
Activity activity, Tab currentTab, boolean shareDirectly, boolean isIncognito) {
if (currentTab == null) return;
......@@ -78,7 +65,7 @@ public class ShareMenuActionHandler {
}
if (SendTabToSelfShareActivity.featureIsAvailable(currentTab)) {
classesToEnable.add(SendTabToSelfShareActivity.class);
classesToEnable.add(SendTabToSelfShareActivity.class);
}
if (QrCodeShareActivity.featureIsAvailable()) {
......@@ -94,67 +81,22 @@ public class ShareMenuActionHandler {
triggerShare(currentTab, shareDirectly, isIncognito);
}
@VisibleForTesting
static boolean shouldFetchCanonicalUrl(final Tab currentTab) {
WebContents webContents = currentTab.getWebContents();
if (webContents == null) return false;
if (webContents.getMainFrame() == null) return false;
String url = currentTab.getUrl();
if (TextUtils.isEmpty(url)) return false;
if (currentTab.isShowingErrorPage() || currentTab.isShowingInterstitialPage()
|| SadTab.isShowing(currentTab)) {
return false;
}
return true;
}
@VisibleForTesting
static String getUrlToShare(String visibleUrl, String canonicalUrl) {
if (TextUtils.isEmpty(canonicalUrl)) return visibleUrl;
// TODO(tedchoc): Can we replace GURLUtils.getScheme with Uri.parse(...).getScheme()
// https://crbug.com/783819
if (!UrlConstants.HTTPS_SCHEME.equals(GURLUtils.getScheme(visibleUrl))) {
return visibleUrl;
}
String canonicalScheme = GURLUtils.getScheme(canonicalUrl);
if (!UrlConstants.HTTP_SCHEME.equals(canonicalScheme)
&& !UrlConstants.HTTPS_SCHEME.equals(canonicalScheme)) {
return visibleUrl;
}
return canonicalUrl;
}
private void logCanonicalUrlResult(String visibleUrl, String canonicalUrl) {
@CanonicalURLResult
int result = getCanonicalUrlResult(visibleUrl, canonicalUrl);
RecordHistogram.recordEnumeratedHistogram(CANONICAL_URL_RESULT_HISTOGRAM, result,
CanonicalURLResult.CANONICAL_URL_RESULT_COUNT);
}
@CanonicalURLResult
private int getCanonicalUrlResult(String visibleUrl, String canonicalUrl) {
if (!UrlConstants.HTTPS_SCHEME.equals(GURLUtils.getScheme(visibleUrl))) {
return CanonicalURLResult.FAILED_VISIBLE_URL_NOT_HTTPS;
}
if (TextUtils.isEmpty(canonicalUrl)) {
return CanonicalURLResult.FAILED_NO_CANONICAL_URL_DEFINED;
}
String canonicalScheme = GURLUtils.getScheme(canonicalUrl);
if (!UrlConstants.HTTPS_SCHEME.equals(canonicalScheme)) {
if (!UrlConstants.HTTP_SCHEME.equals(canonicalScheme)) {
return CanonicalURLResult.FAILED_CANONICAL_URL_INVALID;
} else {
return CanonicalURLResult.SUCCESS_CANONICAL_URL_NOT_HTTPS;
}
}
if (TextUtils.equals(visibleUrl, canonicalUrl)) {
return CanonicalURLResult.SUCCESS_CANONICAL_URL_SAME_AS_VISIBLE;
} else {
return CanonicalURLResult.SUCCESS_CANONICAL_URL_DIFFERENT_FROM_VISIBLE;
}
/**
* Creates and shows a share intent picker dialog or starts a share intent directly with the
* activity that was most recently used to share based on shareDirectly value.
*
* This function will save |screenshot| under {app's root}/files/images/screenshot (or
* /sdcard/DCIM/browser-images/screenshot if ADK is lower than JB MR2).
* Cleaning up doesn't happen automatically, and so an app should call clearSharedScreenshots()
* explicitly when needed.
*
* @param params The container holding the share parameters.
*/
public void share(ShareParams params) {
mDelegate.share(params);
}
private void triggerShare(
protected void triggerShare(
final Tab currentTab, final boolean shareDirectly, boolean isIncognito) {
ScreenshotTabObserver tabObserver = ScreenshotTabObserver.from(currentTab);
if (tabObserver != null) {
......@@ -164,7 +106,7 @@ public class ShareMenuActionHandler {
OfflinePageUtils.maybeShareOfflinePage(currentTab, (ShareParams p) -> {
if (p != null) {
mDelegate.share(p);
share(p);
} else {
WindowAndroid window = currentTab.getWindowAndroid();
// Could not share as an offline page.
......@@ -203,7 +145,7 @@ public class ShareMenuActionHandler {
.setShareDirectly(shareDirectly)
.setSaveLastUsed(!shareDirectly)
.setScreenshotUri(blockingUri);
mDelegate.share(builder.build());
share(builder.build());
if (shareDirectly) {
RecordUserAction.record("MobileMenuDirectShare");
} else {
......@@ -224,15 +166,90 @@ public class ShareMenuActionHandler {
}
}
@VisibleForTesting
static boolean shouldFetchCanonicalUrl(final Tab currentTab) {
WebContents webContents = currentTab.getWebContents();
if (webContents == null) return false;
if (webContents.getMainFrame() == null) return false;
String url = currentTab.getUrl();
if (TextUtils.isEmpty(url)) return false;
if (currentTab.isShowingErrorPage() || currentTab.isShowingInterstitialPage()
|| SadTab.isShowing(currentTab)) {
return false;
}
return true;
}
private static void logCanonicalUrlResult(String visibleUrl, String canonicalUrl) {
@CanonicalURLResult
int result = getCanonicalUrlResult(visibleUrl, canonicalUrl);
RecordHistogram.recordEnumeratedHistogram(CANONICAL_URL_RESULT_HISTOGRAM, result,
CanonicalURLResult.CANONICAL_URL_RESULT_COUNT);
}
@VisibleForTesting
public static void setScreenshotCaptureSkippedForTesting(boolean value) {
sScreenshotCaptureSkippedForTesting = value;
}
@VisibleForTesting
static String getUrlToShare(String visibleUrl, String canonicalUrl) {
if (TextUtils.isEmpty(canonicalUrl)) return visibleUrl;
// TODO(tedchoc): Can we replace GURLUtils.getScheme with Uri.parse(...).getScheme()
// https://crbug.com/783819
if (!UrlConstants.HTTPS_SCHEME.equals(GURLUtils.getScheme(visibleUrl))) {
return visibleUrl;
}
String canonicalScheme = GURLUtils.getScheme(canonicalUrl);
if (!UrlConstants.HTTP_SCHEME.equals(canonicalScheme)
&& !UrlConstants.HTTPS_SCHEME.equals(canonicalScheme)) {
return visibleUrl;
}
return canonicalUrl;
}
@CanonicalURLResult
private static int getCanonicalUrlResult(String visibleUrl, String canonicalUrl) {
if (!UrlConstants.HTTPS_SCHEME.equals(GURLUtils.getScheme(visibleUrl))) {
return CanonicalURLResult.FAILED_VISIBLE_URL_NOT_HTTPS;
}
if (TextUtils.isEmpty(canonicalUrl)) {
return CanonicalURLResult.FAILED_NO_CANONICAL_URL_DEFINED;
}
String canonicalScheme = GURLUtils.getScheme(canonicalUrl);
if (!UrlConstants.HTTPS_SCHEME.equals(canonicalScheme)) {
if (!UrlConstants.HTTP_SCHEME.equals(canonicalScheme)) {
return CanonicalURLResult.FAILED_CANONICAL_URL_INVALID;
} else {
return CanonicalURLResult.SUCCESS_CANONICAL_URL_NOT_HTTPS;
}
}
if (TextUtils.equals(visibleUrl, canonicalUrl)) {
return CanonicalURLResult.SUCCESS_CANONICAL_URL_SAME_AS_VISIBLE;
} else {
return CanonicalURLResult.SUCCESS_CANONICAL_URL_DIFFERENT_FROM_VISIBLE;
}
}
/**
* Delegate for share handling.
*/
static class ShareMenuActionDelegate {
static class ShareSheetDelegate {
/**
* Trigger the share action for the specified params.
*/
void share(ShareParams params) {
ShareHelper.share(params);
if (params.shareDirectly()) {
ShareHelper.shareDirectly(params);
} else if (ChromeFeatureList.isEnabled(ChromeFeatureList.CHROME_SHARING_HUB)) {
// TODO(crbug/1009124): open custom share sheet.
} else if (ShareHelper.TargetChosenReceiver.isSupported()) {
// On L+ open system share sheet.
ShareHelper.makeIntentAndShare(params, null);
} else {
// On K and below open custom share dialog.
ShareHelper.showShareDialog(params);
}
}
}
}
......@@ -4,6 +4,7 @@
package org.chromium.chrome.browser.toolbar;
import android.app.Activity;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
......@@ -76,6 +77,7 @@ import org.chromium.chrome.browser.previews.PreviewsAndroidBridge;
import org.chromium.chrome.browser.previews.PreviewsUma;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
import org.chromium.chrome.browser.share.ShareSheetCoordinator;
import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.SadTab;
import org.chromium.chrome.browser.tab.Tab;
......@@ -760,11 +762,15 @@ public class ToolbarManager implements ScrimObserver, ToolbarTabController, UrlF
final OnClickListener shareButtonListener = v -> {
recordBottomToolbarUseForIPH();
RecordUserAction.record("MobileBottomToolbarShareButton");
Tab tab = null;
Activity activity = null;
boolean isIncognito = false;
if (mTabModelSelector != null) {
isIncognito = mTabModelSelector.getCurrentTab().isIncognito();
tab = mTabModelSelector.getCurrentTab();
activity = tab.getActivity();
isIncognito = tab.isIncognito();
}
mActivity.onShareMenuItemSelected(false, isIncognito);
ShareSheetCoordinator.create().onShareSelected(activity, tab, false, isIncognito);
};
mBottomControlsCoordinator = new BottomControlsCoordinator(mActivity.getFullscreenManager(),
......
......@@ -10,6 +10,7 @@ import org.chromium.base.Callback;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.MenuOrKeyboardActionController;
import org.chromium.chrome.browser.appmenu.AppMenuBlocker;
......@@ -25,6 +26,7 @@ import org.chromium.chrome.browser.findinpage.FindToolbarObserver;
import org.chromium.chrome.browser.lifecycle.Destroyable;
import org.chromium.chrome.browser.lifecycle.InflationObserver;
import org.chromium.chrome.browser.metrics.UkmRecorder;
import org.chromium.chrome.browser.share.ShareSheetCoordinator;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.vr.VrModeObserver;
import org.chromium.chrome.browser.vr.VrModuleProvider;
......@@ -43,6 +45,7 @@ public class RootUiCoordinator
protected ChromeActivity mActivity;
protected @Nullable AppMenuCoordinator mAppMenuCoordinator;
private final MenuOrKeyboardActionController mMenuOrKeyboardActionController;
private ActivityTabProvider mActivityTabProvider;
protected @Nullable FindToolbarManager mFindToolbarManager;
private @Nullable FindToolbarObserver mFindToolbarObserver;
......@@ -68,6 +71,7 @@ public class RootUiCoordinator
mMenuOrKeyboardActionController = mActivity.getMenuOrKeyboardActionController();
mMenuOrKeyboardActionController.registerMenuOrKeyboardActionHandler(this);
mActivityTabProvider = mActivity.getActivityTabProvider();
mLayoutManagerSupplierCallback = this::onLayoutManagerAvailable;
mActivity.getLayoutManagerSupplier().addObserver(mLayoutManagerSupplierCallback);
......@@ -126,6 +130,19 @@ public class RootUiCoordinator
VrModuleProvider.registerVrModeObserver(mVrModeObserver);
}
/**
* Triggered when the share menu item is selected.
* This creates and shows a share intent picker dialog or starts a share intent directly.
* @param shareDirectly Whether it should share directly with the activity that was most
* recently used to share.
* @param isIncognito Whether currentTab is incognito.
*/
@VisibleForTesting
public void onShareMenuItemSelected(final boolean shareDirectly, final boolean isIncognito) {
ShareSheetCoordinator.create().onShareSelected(
mActivity, mActivityTabProvider.get(), shareDirectly, isIncognito);
}
// MenuOrKeyboardActionHandler implementation
@Override
......@@ -146,6 +163,9 @@ public class RootUiCoordinator
RecordUserAction.record("MobileShortcutFindInPage");
}
return true;
} else if (id == R.id.share_menu_id || id == R.id.direct_share_menu_id) {
onShareMenuItemSelected(id == R.id.direct_share_menu_id,
mActivity.getTabModelSelector().isIncognitoSelected());
}
return false;
......
......@@ -23,6 +23,7 @@ import org.chromium.chrome.browser.notifications.NotificationMetadata;
import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
import org.chromium.chrome.browser.notifications.PendingIntentProvider;
import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions;
import org.chromium.chrome.browser.share.ShareSheetCoordinator;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.util.IntentUtils;
import org.chromium.ui.base.Clipboard;
......@@ -131,8 +132,9 @@ class WebappActionsNotificationManager {
if (ACTION_SHARE.equals(intent.getAction())) {
// Not routing through onMenuOrKeyboardAction to control UMA String.
webappActivity.onShareMenuItemSelected(
false /* share directly */, webappActivity.getCurrentTabModel().isIncognito());
Tab tab = webappActivity.getActivityTab();
boolean isIncognito = tab.isIncognito();
ShareSheetCoordinator.create().onShareSelected(webappActivity, tab, false, isIncognito);
RecordUserAction.record("Webapp.NotificationShare");
return true;
} else if (ACTION_OPEN_IN_CHROME.equals(intent.getAction())) {
......
......@@ -22,6 +22,7 @@ import org.chromium.base.task.TaskTraits;
import org.chromium.chrome.browser.safe_browsing.FileTypePolicies;
import org.chromium.chrome.browser.share.ShareHelper;
import org.chromium.chrome.browser.share.ShareParams;
import org.chromium.chrome.browser.share.ShareSheetCoordinator;
import org.chromium.content_public.browser.WebContents;
import org.chromium.mojo.system.MojoException;
import org.chromium.ui.base.WindowAndroid;
......@@ -185,7 +186,7 @@ public class ShareServiceImpl implements ShareService {
.setCallback(innerCallback);
if (files == null || files.length == 0) {
ShareHelper.share(paramsBuilder.build());
ShareSheetCoordinator.create().share(paramsBuilder.build());
return;
}
......
......@@ -6,8 +6,8 @@ package org.chromium.chrome.browser.webshare;
import org.chromium.base.Callback;
import org.chromium.base.task.PostTask;
import org.chromium.chrome.browser.share.ShareHelper;
import org.chromium.chrome.browser.share.ShareParams;
import org.chromium.chrome.browser.share.ShareSheetCoordinator;
import org.chromium.content_public.browser.UiThreadTaskTraits;
import org.chromium.mojo.system.MojoResult;
import org.chromium.webshare.mojom.ShareError;
......@@ -54,7 +54,7 @@ public class SharedFileCollator implements Callback<Integer> {
PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> {
if (result == MojoResult.OK) {
ShareHelper.share(mParams);
ShareSheetCoordinator.create().share(mParams);
} else {
callback.call(ShareError.INTERNAL_ERROR);
}
......
......@@ -24,8 +24,9 @@ import org.chromium.base.task.TaskTraits;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.RetryOnFailure;
import org.chromium.chrome.browser.share.ShareHelper;
import org.chromium.chrome.browser.share.ShareMenuActionHandler;
import org.chromium.chrome.browser.share.ShareSheetCoordinator;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.ui.RootUiCoordinator;
import org.chromium.chrome.browser.util.ChromeFileProvider;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
......@@ -131,6 +132,11 @@ public class ShareIntentTest {
public Window getWindow() {
return mActivity.getWindow();
}
@Override
public ActivityTabProvider getActivityTabProvider() {
return mActivity.getActivityTabProvider();
}
}
@Test
......@@ -143,10 +149,12 @@ public class ShareIntentTest {
// package and class names do not matter.
return new MockChromeActivity(mActivityTestRule.getActivity());
});
RootUiCoordinator rootUiCoordinator = TestThreadUtils.runOnUiThreadBlocking(
() -> { return new RootUiCoordinator(mockActivity); });
ShareHelper.setLastShareComponentName(
new ComponentName("test.package", "test.activity"), null);
// Skips the capture of screenshot and notifies with an empty file.
ShareMenuActionHandler.setScreenshotCaptureSkippedForTesting(true);
ShareSheetCoordinator.setScreenshotCaptureSkippedForTesting(true);
WindowAndroid window = TestThreadUtils.runOnUiThreadBlocking(() -> {
return new WindowAndroid(mActivityTestRule.getActivity()) {
......@@ -160,7 +168,8 @@ public class ShareIntentTest {
() -> { mockActivity.getActivityTab().updateWindowAndroid(window); });
TestThreadUtils.runOnUiThreadBlocking(
() -> mockActivity.onShareMenuItemSelected(
()
-> rootUiCoordinator.onShareMenuItemSelected(
true /* shareDirectly */, false /* isIncognito */));
mockActivity.waitForFileCheck();
......@@ -175,6 +184,6 @@ public class ShareIntentTest {
@After
public void tearDown() {
ShareMenuActionHandler.setScreenshotCaptureSkippedForTesting(false);
ShareSheetCoordinator.setScreenshotCaptureSkippedForTesting(false);
}
}
......@@ -18,7 +18,7 @@ import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.MetricsUtils.HistogramDelta;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.share.ShareMenuActionHandler.ShareMenuActionDelegate;
import org.chromium.chrome.browser.share.ShareSheetMediator.ShareSheetDelegate;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.components.ui_metrics.CanonicalURLResult;
......@@ -34,7 +34,7 @@ import java.util.concurrent.atomic.AtomicReference;
*/
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class ShareMenuActionHandlerIntegrationTest {
public class ShareSheetMediatorIntegrationTest {
private static final String PAGE_WITH_HTTPS_CANONICAL_URL =
"/chrome/test/data/android/share/link_share_https_canonical.html";
private static final String PAGE_WITH_HTTP_CANONICAL_URL =
......@@ -99,7 +99,7 @@ public class ShareMenuActionHandlerIntegrationTest {
throws IllegalArgumentException, TimeoutException {
mActivityTestRule.loadUrl(pageUrl);
HistogramDelta urlResultDelta = new HistogramDelta(
ShareMenuActionHandler.CANONICAL_URL_RESULT_HISTOGRAM, expectedUrlResult);
ShareSheetMediator.CANONICAL_URL_RESULT_HISTOGRAM, expectedUrlResult);
ShareParams params = triggerShare();
Assert.assertEquals(expectedShareUrl, params.getUrl());
Assert.assertEquals(1, urlResultDelta.getDelta());
......@@ -109,7 +109,7 @@ public class ShareMenuActionHandlerIntegrationTest {
final CallbackHelper helper = new CallbackHelper();
final AtomicReference<ShareParams> paramsRef = new AtomicReference<>();
TestThreadUtils.runOnUiThreadBlocking(() -> {
ShareMenuActionDelegate delegate = new ShareMenuActionDelegate() {
ShareSheetDelegate delegate = new ShareSheetDelegate() {
@Override
void share(ShareParams params) {
paramsRef.set(params);
......@@ -117,8 +117,7 @@ public class ShareMenuActionHandlerIntegrationTest {
}
};
new ShareMenuActionHandler(delegate).onShareMenuItemSelected(
mActivityTestRule.getActivity(),
new ShareSheetMediator(delegate).onShareSelected(mActivityTestRule.getActivity(),
mActivityTestRule.getActivity().getActivityTab(), false, false);
});
helper.waitForCallback(0);
......
......@@ -23,10 +23,10 @@ import org.chromium.content_public.browser.test.util.TestThreadUtils;
import java.util.concurrent.ExecutionException;
/**
* Tests (requiring native) of the ShareMenuActionHandler.
* Tests (requiring native) of the ShareSheetMediator.
*/
@RunWith(BaseJUnit4ClassRunner.class)
public class ShareMenuActionHandlerTest {
public class ShareSheetMediatorTest {
@Rule
public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule();
......@@ -43,37 +43,37 @@ public class ShareMenuActionHandlerTest {
mSadTabRule.setTab(mockTab);
// Null webContents:
Assert.assertFalse(ShareMenuActionHandler.shouldFetchCanonicalUrl(mockTab));
Assert.assertFalse(ShareSheetMediator.shouldFetchCanonicalUrl(mockTab));
// Null render frame:
mockTab.webContents = mockWebContents;
Assert.assertFalse(ShareMenuActionHandler.shouldFetchCanonicalUrl(mockTab));
Assert.assertFalse(ShareSheetMediator.shouldFetchCanonicalUrl(mockTab));
// Invalid/empty URL:
mockWebContents.renderFrameHost = mockRenderFrameHost;
Assert.assertFalse(ShareMenuActionHandler.shouldFetchCanonicalUrl(mockTab));
Assert.assertFalse(ShareSheetMediator.shouldFetchCanonicalUrl(mockTab));
// Disabled if showing error page.
mockTab.isShowingErrorPage = true;
Assert.assertFalse(ShareMenuActionHandler.shouldFetchCanonicalUrl(mockTab));
Assert.assertFalse(ShareSheetMediator.shouldFetchCanonicalUrl(mockTab));
mockTab.isShowingErrorPage = false;
// Disabled if showing interstitial page.
mockTab.isShowingInterstitialPage = true;
Assert.assertFalse(ShareMenuActionHandler.shouldFetchCanonicalUrl(mockTab));
Assert.assertFalse(ShareSheetMediator.shouldFetchCanonicalUrl(mockTab));
mockTab.isShowingInterstitialPage = false;
// Disabled if showing sad tab page.
mSadTabRule.show(true);
Assert.assertFalse(ShareMenuActionHandler.shouldFetchCanonicalUrl(mockTab));
Assert.assertFalse(ShareSheetMediator.shouldFetchCanonicalUrl(mockTab));
mSadTabRule.show(false);
}
@Test
@SmallTest
public void testGetUrlToShare() {
Assert.assertNull(ShareMenuActionHandler.getUrlToShare(null, null));
Assert.assertEquals("", ShareMenuActionHandler.getUrlToShare("", null));
Assert.assertNull(ShareSheetMediator.getUrlToShare(null, null));
Assert.assertEquals("", ShareSheetMediator.getUrlToShare("", null));
final String httpUrl = "http://blah.com";
final String otherHttpUrl = "http://eek.com";
......@@ -83,21 +83,21 @@ public class ShareMenuActionHandlerTest {
final String contentUrl = "content://eek.com";
// HTTP Display URL, HTTP Canonical URL -> HTTP Display URL
Assert.assertEquals(httpUrl, ShareMenuActionHandler.getUrlToShare(httpUrl, otherHttpUrl));
Assert.assertEquals(httpUrl, ShareSheetMediator.getUrlToShare(httpUrl, otherHttpUrl));
// HTTP Display URL, HTTPS Canonical URL -> HTTP Display URL
Assert.assertEquals(httpUrl, ShareMenuActionHandler.getUrlToShare(httpUrl, httpsUrl));
Assert.assertEquals(httpUrl, ShareSheetMediator.getUrlToShare(httpUrl, httpsUrl));
// HTTPS Display URL, HTTP Canonical URL -> HTTP Canonical URL
Assert.assertEquals(httpUrl, ShareMenuActionHandler.getUrlToShare(httpsUrl, httpUrl));
Assert.assertEquals(httpUrl, ShareSheetMediator.getUrlToShare(httpsUrl, httpUrl));
// HTTPS Display URL, HTTPS Canonical URL -> HTTPS Canonical URL
Assert.assertEquals(httpsUrl, ShareMenuActionHandler.getUrlToShare(httpsUrl, httpsUrl));
Assert.assertEquals(httpsUrl, ShareSheetMediator.getUrlToShare(httpsUrl, httpsUrl));
Assert.assertEquals(
otherHttpsUrl, ShareMenuActionHandler.getUrlToShare(httpsUrl, otherHttpsUrl));
otherHttpsUrl, ShareSheetMediator.getUrlToShare(httpsUrl, otherHttpsUrl));
// HTTPS Display URL, FTP URL -> HTTPS Display URL
Assert.assertEquals(httpsUrl, ShareMenuActionHandler.getUrlToShare(httpsUrl, ftpUrl));
Assert.assertEquals(httpsUrl, ShareSheetMediator.getUrlToShare(httpsUrl, ftpUrl));
// HTTPS Display URL, Content URL -> HTTPS Display URL
Assert.assertEquals(httpsUrl, ShareMenuActionHandler.getUrlToShare(httpsUrl, contentUrl));
Assert.assertEquals(httpsUrl, ShareSheetMediator.getUrlToShare(httpsUrl, contentUrl));
}
private static class MockUrlTab extends MockTab {
......
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