Skip to content

Commit

Permalink
[Merge to M111][RDSC] Implement Request Desktop Site per-site setting…
Browse files Browse the repository at this point in the history
…s IPH (arm 2)

- Add feature engagement config for RDS per-site IPH arm 2
- Create DesktopSiteSettingsIPHController to manage blue tooltip implementation of the IPH
- Trigger the IPH in both CTA and CCT
- Add unit tests for DesktopSiteSettingsIPHController

Demo: https://drive.google.com/file/d/1C5VC2Vcm5hOiVlFnaxbd6eEHqqHWY-NE/view?usp=sharing

(cherry picked from commit 961ba13)

Bug: 1402566
Change-Id: I9bac06237b430e8b41360160137f3b04c8d97eaf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4116600
Reviewed-by: Mark Pearson <mpearson@chromium.org>
Reviewed-by: Theresa Sullivan <twellington@chromium.org>
Commit-Queue: Aishwarya Rajesh <aishwaryarj@google.com>
Reviewed-by: Shu Yang <shuyng@google.com>
Cr-Original-Commit-Position: refs/heads/main@{#1097702}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4205667
Owners-Override: Krishna Govind <govind@chromium.org>
Reviewed-by: Krishna Govind <govind@chromium.org>
Reviewed-by: Jesse Doherty <jwd@chromium.org>
Cr-Commit-Position: refs/branch-heads/5563@{#37}
Cr-Branched-From: 3ac59a6-refs/heads/main@{#1097615}
  • Loading branch information
Aishwarya Rajesh authored and Chromium LUCI CQ committed Jan 30, 2023
1 parent f4fe161 commit b96031b
Show file tree
Hide file tree
Showing 21 changed files with 524 additions and 4 deletions.
1 change: 1 addition & 0 deletions chrome/android/chrome_java_sources.gni
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppModule.java",
"java/src/org/chromium/chrome/browser/dependency_injection/ChromeCommonQualifiers.java",
"java/src/org/chromium/chrome/browser/dependency_injection/ModuleFactoryOverrides.java",
"java/src/org/chromium/chrome/browser/desktop_site/DesktopSiteSettingsIPHController.java",
"java/src/org/chromium/chrome/browser/device_dialog/ChromeBluetoothChooserAndroidDelegate.java",
"java/src/org/chromium/chrome/browser/device_dialog/ChromeBluetoothScanningPromptAndroidDelegate.java",
"java/src/org/chromium/chrome/browser/device_dialog/UsbChooserDialog.java",
Expand Down
1 change: 1 addition & 0 deletions chrome/android/chrome_junit_test_java_sources.gni
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ chrome_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/BrandingSecurityButtonAnimationDelegateUnitTest.java",
"junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java",
"junit/src/org/chromium/chrome/browser/customtabs/shadows/ShadowExternalNavigationDelegateImpl.java",
"junit/src/org/chromium/chrome/browser/desktop_site/DesktopSiteSettingsIPHControllerUnitTest.java",
"junit/src/org/chromium/chrome/browser/directactions/FindInPageDirectActionHandlerTest.java",
"junit/src/org/chromium/chrome/browser/directactions/GoBackDirectActionHandlerTest.java",
"junit/src/org/chromium/chrome/browser/display_cutout/DisplayCutoutControllerTest.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2545,6 +2545,8 @@ public boolean onMenuOrKeyboardAction(int id, boolean fromMenu) {
currentTab.reload();
RequestDesktopUtils.maybeShowUserEducationPromptForAppMenuSelection(
profile, this, getModalDialogManager());
TrackerFactory.getTrackerForProfile(profile).notifyEvent(
EventConstants.APP_MENU_DESKTOP_SITE_EXCEPTION_ADDED);
} else {
TabUtils.switchUserAgent(currentTab, usingDesktopUserAgent, /* forcedByUser */ true,
UseDesktopUserAgentCaller.ON_MENU_OR_KEYBOARD_ACTION);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import org.chromium.chrome.browser.customtabs.features.partialcustomtab.PartialCustomTabTabObserver;
import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbar;
import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarCoordinator;
import org.chromium.chrome.browser.desktop_site.DesktopSiteSettingsIPHController;
import org.chromium.chrome.browser.document.ChromeLauncherActivity;
import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
import org.chromium.chrome.browser.flags.ActivityType;
Expand Down Expand Up @@ -90,6 +91,8 @@ public class BaseCustomTabRootUiCoordinator extends RootUiCoordinator {
// TODO(https://crbug.com/1343056): Make it part of the ctor.
private @Nullable BrandingController mBrandingController;

private @Nullable DesktopSiteSettingsIPHController mDesktopSiteSettingsIPHController;

/**
* Construct a new BaseCustomTabRootUiCoordinator.
* @param activity The activity whose UI the coordinator is responsible for.
Expand Down Expand Up @@ -345,6 +348,11 @@ public void onDestroy() {
mBrandingController = null;
}

if (mDesktopSiteSettingsIPHController != null) {
mDesktopSiteSettingsIPHController.destroy();
mDesktopSiteSettingsIPHController = null;
}

mCustomTabHeightStrategy.destroy();
}

Expand All @@ -370,7 +378,13 @@ void handleCloseAnimation(Runnable finishRunnable) {
* Runs a set of deferred startup tasks.
*/
void onDeferredStartup() {
RequestDesktopUtils.maybeShowDefaultEnableGlobalSettingMessage(
boolean didShowPrompt = RequestDesktopUtils.maybeShowDefaultEnableGlobalSettingMessage(
Profile.getLastUsedRegularProfile(), mMessageDispatcher, mActivity);
if (!didShowPrompt && mAppMenuCoordinator != null) {
mDesktopSiteSettingsIPHController = DesktopSiteSettingsIPHController.create(mActivity,
mWindowAndroid, mActivityTabProvider, Profile.getLastUsedRegularProfile(),
getToolbarManager().getMenuButtonView(),
mAppMenuCoordinator.getAppMenuHandler());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// Copyright 2023 The Chromium Authors
// 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.desktop_site;

import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import org.chromium.chrome.R;
import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.ActivityTabProvider.ActivityTabTabObserver;
import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.ui.appmenu.AppMenuHandler;
import org.chromium.chrome.browser.user_education.IPHCommandBuilder;
import org.chromium.chrome.browser.user_education.UserEducationHelper;
import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge;
import org.chromium.components.content_settings.ContentSettingsType;
import org.chromium.components.embedder_support.util.UrlUtilities;
import org.chromium.components.feature_engagement.FeatureConstants;
import org.chromium.components.feature_engagement.Tracker;
import org.chromium.ui.base.DeviceFormFactor;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.url.GURL;

/**
* Controller to manage desktop site settings in-product-help messages to users.
*/
public class DesktopSiteSettingsIPHController {
@VisibleForTesting
static final String PARAM_IPH_TYPE_GENERIC = "iph_type_generic";

private final UserEducationHelper mUserEducationHelper;
private final WindowAndroid mWindowAndroid;
private final AppMenuHandler mAppMenuHandler;
private final View mToolbarMenuButton;
private final ActivityTabProvider mActivityTabProvider;
private ActivityTabTabObserver mActivityTabTabObserver;
private WebsitePreferenceBridge mWebsitePreferenceBridge;

/**
* Creates and initializes the controller.
* Registers an {@link ActivityTabTabObserver} that will attempt to show the desktop site
* per-site settings IPH on an eligible tab in one of the following cases:
* 1. On a specific site included in a pre-defined list (arm 1).
* 2. On any site on a tablet device (arm 2).
*
* @param activity The current activity.
* @param windowAndroid The window associated with the activity.
* @param activityTabProvider The provider of the current activity tab.
* @param profile The current {@link Profile}.
* @param toolbarMenuButton The toolbar menu button to which the IPH will be anchored.
* @param appMenuHandler The app menu handler.
*/
public static @Nullable DesktopSiteSettingsIPHController create(Activity activity,
WindowAndroid windowAndroid, ActivityTabProvider activityTabProvider, Profile profile,
View toolbarMenuButton, AppMenuHandler appMenuHandler) {
if (!ChromeFeatureList.isEnabled(ChromeFeatureList.REQUEST_DESKTOP_SITE_PER_SITE_IPH)) {
return null;
}

return new DesktopSiteSettingsIPHController(windowAndroid, activityTabProvider, profile,
toolbarMenuButton, appMenuHandler,
new UserEducationHelper(activity, new Handler(Looper.getMainLooper())),
new WebsitePreferenceBridge());
}

DesktopSiteSettingsIPHController(WindowAndroid windowAndroid,
ActivityTabProvider activityTabProvider, Profile profile, View toolbarMenuButton,
AppMenuHandler appMenuHandler, UserEducationHelper userEducationHelper,
WebsitePreferenceBridge websitePreferenceBridge) {
mWindowAndroid = windowAndroid;
mToolbarMenuButton = toolbarMenuButton;
mAppMenuHandler = appMenuHandler;
mUserEducationHelper = userEducationHelper;
mActivityTabProvider = activityTabProvider;
mWebsitePreferenceBridge = websitePreferenceBridge;

registerTabObserverForPerSiteIPH(profile);
}

public void destroy() {
if (mActivityTabTabObserver != null) mActivityTabTabObserver.destroy();
}

@VisibleForTesting
void showGenericIPH(@NonNull Tab tab, Profile profile) {
// Return early if the device is not a tablet.
if (!DeviceFormFactor.isWindowOnTablet(mWindowAndroid)) {
return;
}

Tracker tracker = TrackerFactory.getTrackerForProfile(profile);
// Return early when the IPH triggering criteria is not satisfied.
if (!tracker.wouldTriggerHelpUI(
FeatureConstants.REQUEST_DESKTOP_SITE_EXCEPTIONS_GENERIC_FEATURE)) {
return;
}

// Do not trigger the IPH on an incognito tab since the setting does not persist.
if (tab.isIncognito()) {
return;
}

GURL url = tab.getUrl();
// Do not trigger the IPH on a chrome:// or a chrome-native:// page.
if (UrlUtilities.isInternalScheme(url) || tab.getWebContents() == null) {
return;
}

var siteExceptions = mWebsitePreferenceBridge.getContentSettingsExceptions(
profile, ContentSettingsType.REQUEST_DESKTOP_SITE);
// Do not trigger the IPH if the user has already added any site-level exceptions. By
// default, `siteExceptions` will hold one entry representing the wildcard for all sites,
// for the default content setting.
if (siteExceptions.size() > 1) {
return;
}

boolean isTabUsingDesktopUserAgent =
tab.getWebContents().getNavigationController().getUseDesktopUserAgent();
int textId = isTabUsingDesktopUserAgent
? R.string.rds_site_settings_generic_iph_text_mobile
: R.string.rds_site_settings_generic_iph_text_desktop;

mUserEducationHelper.requestShowIPH(
new IPHCommandBuilder(mToolbarMenuButton.getContext().getResources(),
FeatureConstants.REQUEST_DESKTOP_SITE_EXCEPTIONS_GENERIC_FEATURE, textId,
new Object[] {url.getHost()}, textId, new Object[] {url.getHost()})
.setAnchorView(mToolbarMenuButton)
.setOnShowCallback(
() -> turnOnHighlightForMenuItem(R.id.request_desktop_site_id))
.setOnDismissCallback(this::turnOffHighlightForMenuItem)
.build());
}

@VisibleForTesting
ActivityTabTabObserver getActiveTabObserverForTesting() {
return mActivityTabTabObserver;
}

private void registerTabObserverForPerSiteIPH(Profile profile) {
boolean showGenericIPH = ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
ChromeFeatureList.REQUEST_DESKTOP_SITE_PER_SITE_IPH, PARAM_IPH_TYPE_GENERIC, false);

mActivityTabTabObserver = new ActivityTabTabObserver(mActivityTabProvider) {
@Override
protected void onObservingDifferentTab(Tab tab, boolean hint) {
if (showGenericIPH) {
showGenericIPH(tab, profile);
}
}

@Override
public void onPageLoadFinished(Tab tab, GURL url) {
if (showGenericIPH) {
showGenericIPH(tab, profile);
}
}
};
}

private void turnOnHighlightForMenuItem(int highlightMenuItemId) {
mAppMenuHandler.setMenuHighlight(highlightMenuItemId);
}

private void turnOffHighlightForMenuItem() {
mAppMenuHandler.clearMenuHighlight();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
skavuluru@google.com
shuyng@google.com
aishwaryarj@google.com
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.chromium.chrome.browser.compositor.layouts.LayoutManagerImpl;
import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager;
import org.chromium.chrome.browser.desktop_site.DesktopSiteSettingsIPHController;
import org.chromium.chrome.browser.feature_guide.notifications.FeatureNotificationUtils;
import org.chromium.chrome.browser.feature_guide.notifications.FeatureType;
import org.chromium.chrome.browser.feed.webfeed.WebFeedFollowIntroController;
Expand Down Expand Up @@ -147,6 +148,7 @@ public class TabbedRootUiCoordinator extends RootUiCoordinator {
private OfflineIndicatorControllerV2 mOfflineIndicatorController;
private OfflineIndicatorInProductHelpController mOfflineIndicatorInProductHelpController;
private ReadLaterIPHController mReadLaterIPHController;
private DesktopSiteSettingsIPHController mDesktopSiteSettingsIPHController;
private WebFeedFollowIntroController mWebFeedFollowIntroController;
private UrlFocusChangeListener mUrlFocusChangeListener;
private @Nullable ToolbarButtonInProductHelpController mToolbarButtonInProductHelpController;
Expand Down Expand Up @@ -404,6 +406,11 @@ public void onDestroy() {
mTabSwitcherCustomViewManagerCallbackController.destroy();
}

if (mDesktopSiteSettingsIPHController != null) {
mDesktopSiteSettingsIPHController.destroy();
mDesktopSiteSettingsIPHController = null;
}

super.onDestroy();
}

Expand Down Expand Up @@ -731,6 +738,9 @@ mActivity, new SettingsLauncherImpl(),
getToolbarManager().getMenuButtonView(),
mAppMenuCoordinator.getAppMenuHandler(), R.id.manage_all_windows_menu_id);
}
DesktopSiteSettingsIPHController.create(mActivity, mWindowAndroid, mActivityTabProvider,
Profile.getLastUsedRegularProfile(), getToolbarManager().getMenuButtonView(),
mAppMenuCoordinator.getAppMenuHandler());
}
mPromoShownOneshotSupplier.set(didTriggerPromo);

Expand Down
Loading

0 comments on commit b96031b

Please sign in to comment.