Commit 119eb5ad authored by Shakti Sahu's avatar Shakti Sahu Committed by Commit Bot

Read later : Added IPH for bookmark star icon and reading list folder

This CL adds various IPHs for the read later feature.
1 - IPH to educate user about using the bookmark button on the app
menu for saving articles to reading list.
2 - IPH to educate user about location of bookmarks in app menu.
3 - Notify IPH backend whenever read later option is selected in
context menu.
4 - Notify IPH backend whenever user navigates to the read later folder
in bookmarks.

Bug: 1139076
Change-Id: Ib81e43a9746c3493bdd6f299e6a9e117aacc472c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2537390Reviewed-by: default avatarShakti Sahu <shaktisahu@chromium.org>
Reviewed-by: default avatarDavid Trainor <dtrainor@chromium.org>
Reviewed-by: default avatarSky Malice <skym@chromium.org>
Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Commit-Queue: Shakti Sahu <shaktisahu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#829055}
parent eaa15403
...@@ -1212,6 +1212,7 @@ chrome_java_sources = [ ...@@ -1212,6 +1212,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/query_tiles/QueryTileUtils.java", "java/src/org/chromium/chrome/browser/query_tiles/QueryTileUtils.java",
"java/src/org/chromium/chrome/browser/query_tiles/TileProviderFactory.java", "java/src/org/chromium/chrome/browser/query_tiles/TileProviderFactory.java",
"java/src/org/chromium/chrome/browser/rappor/RapporServiceBridge.java", "java/src/org/chromium/chrome/browser/rappor/RapporServiceBridge.java",
"java/src/org/chromium/chrome/browser/read_later/ReadLaterIPHController.java",
"java/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationController.java", "java/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationController.java",
"java/src/org/chromium/chrome/browser/resources/ResourceMapper.java", "java/src/org/chromium/chrome/browser/resources/ResourceMapper.java",
"java/src/org/chromium/chrome/browser/rlz/RevenueStats.java", "java/src/org/chromium/chrome/browser/rlz/RevenueStats.java",
......
...@@ -483,6 +483,7 @@ chrome_test_java_sources = [ ...@@ -483,6 +483,7 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/query_tiles/QueryTileSectionToOmniboxTest.java", "javatests/src/org/chromium/chrome/browser/query_tiles/QueryTileSectionToOmniboxTest.java",
"javatests/src/org/chromium/chrome/browser/query_tiles/TileMatchers.java", "javatests/src/org/chromium/chrome/browser/query_tiles/TileMatchers.java",
"javatests/src/org/chromium/chrome/browser/query_tiles/ViewActions.java", "javatests/src/org/chromium/chrome/browser/query_tiles/ViewActions.java",
"javatests/src/org/chromium/chrome/browser/read_later/ReadLaterIphTest.java",
"javatests/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerIntegrationTest.java", "javatests/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerIntegrationTest.java",
"javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java", "javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java",
"javatests/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettingsTest.java", "javatests/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettingsTest.java",
......
...@@ -1656,6 +1656,7 @@ public class ChromeTabbedActivity extends ChromeActivity<ChromeActivityComponent ...@@ -1656,6 +1656,7 @@ public class ChromeTabbedActivity extends ChromeActivity<ChromeActivityComponent
Supplier<TabDelegateFactory> tabDelegateFactorySupplier = () -> { Supplier<TabDelegateFactory> tabDelegateFactorySupplier = () -> {
return new TabbedModeTabDelegateFactory(this, getAppBrowserControlsVisibilityDelegate(), return new TabbedModeTabDelegateFactory(this, getAppBrowserControlsVisibilityDelegate(),
getShareDelegateSupplier(), mEphemeralTabCoordinatorSupplier, getShareDelegateSupplier(), mEphemeralTabCoordinatorSupplier,
((TabbedRootUiCoordinator) mRootUiCoordinator)::onContextMenuCopyLink,
mRootUiCoordinator.getBottomSheetController()); mRootUiCoordinator.getBottomSheetController());
}; };
......
...@@ -34,6 +34,7 @@ public class TabbedModeTabDelegateFactory implements TabDelegateFactory { ...@@ -34,6 +34,7 @@ public class TabbedModeTabDelegateFactory implements TabDelegateFactory {
private final BrowserControlsVisibilityDelegate mAppBrowserControlsVisibilityDelegate; private final BrowserControlsVisibilityDelegate mAppBrowserControlsVisibilityDelegate;
private final Supplier<ShareDelegate> mShareDelegateSupplier; private final Supplier<ShareDelegate> mShareDelegateSupplier;
private final Supplier<EphemeralTabCoordinator> mEphemeralTabCoordinatorSupplier; private final Supplier<EphemeralTabCoordinator> mEphemeralTabCoordinatorSupplier;
private final Runnable mContextMenuCopyLinkObserver;
private final BottomSheetController mBottomSheetController; private final BottomSheetController mBottomSheetController;
private NativePageFactory mNativePageFactory; private NativePageFactory mNativePageFactory;
...@@ -41,11 +42,12 @@ public class TabbedModeTabDelegateFactory implements TabDelegateFactory { ...@@ -41,11 +42,12 @@ public class TabbedModeTabDelegateFactory implements TabDelegateFactory {
BrowserControlsVisibilityDelegate appBrowserControlsVisibilityDelegate, BrowserControlsVisibilityDelegate appBrowserControlsVisibilityDelegate,
Supplier<ShareDelegate> shareDelegateSupplier, Supplier<ShareDelegate> shareDelegateSupplier,
Supplier<EphemeralTabCoordinator> ephemeralTabCoordinatorSupplier, Supplier<EphemeralTabCoordinator> ephemeralTabCoordinatorSupplier,
BottomSheetController sheetController) { Runnable contextMenuCopyLinkObserver, BottomSheetController sheetController) {
mActivity = activity; mActivity = activity;
mAppBrowserControlsVisibilityDelegate = appBrowserControlsVisibilityDelegate; mAppBrowserControlsVisibilityDelegate = appBrowserControlsVisibilityDelegate;
mShareDelegateSupplier = shareDelegateSupplier; mShareDelegateSupplier = shareDelegateSupplier;
mEphemeralTabCoordinatorSupplier = ephemeralTabCoordinatorSupplier; mEphemeralTabCoordinatorSupplier = ephemeralTabCoordinatorSupplier;
mContextMenuCopyLinkObserver = contextMenuCopyLinkObserver;
mBottomSheetController = sheetController; mBottomSheetController = sheetController;
} }
...@@ -63,7 +65,8 @@ public class TabbedModeTabDelegateFactory implements TabDelegateFactory { ...@@ -63,7 +65,8 @@ public class TabbedModeTabDelegateFactory implements TabDelegateFactory {
public ContextMenuPopulatorFactory createContextMenuPopulatorFactory(Tab tab) { public ContextMenuPopulatorFactory createContextMenuPopulatorFactory(Tab tab) {
return new ChromeContextMenuPopulatorFactory( return new ChromeContextMenuPopulatorFactory(
new TabContextMenuItemDelegate(tab, mActivity.getTabModelSelector(), new TabContextMenuItemDelegate(tab, mActivity.getTabModelSelector(),
mEphemeralTabCoordinatorSupplier, mActivity::getSnackbarManager), mEphemeralTabCoordinatorSupplier, mContextMenuCopyLinkObserver,
mActivity::getSnackbarManager),
mShareDelegateSupplier, ChromeContextMenuPopulator.ContextMenuMode.NORMAL, mShareDelegateSupplier, ChromeContextMenuPopulator.ContextMenuMode.NORMAL,
AppHooks.get().getExternalAuthUtils()); AppHooks.get().getExternalAuthUtils());
} }
......
...@@ -62,7 +62,6 @@ import org.chromium.chrome.browser.IntentHandler; ...@@ -62,7 +62,6 @@ import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.IntentHandler.IntentHandlerDelegate; import org.chromium.chrome.browser.IntentHandler.IntentHandlerDelegate;
import org.chromium.chrome.browser.IntentHandler.TabOpenType; import org.chromium.chrome.browser.IntentHandler.TabOpenType;
import org.chromium.chrome.browser.PlayServicesVersionInfo; import org.chromium.chrome.browser.PlayServicesVersionInfo;
import org.chromium.chrome.browser.TabbedModeTabDelegateFactory;
import org.chromium.chrome.browser.WarmupManager; import org.chromium.chrome.browser.WarmupManager;
import org.chromium.chrome.browser.accessibility.FontSizePrefs; import org.chromium.chrome.browser.accessibility.FontSizePrefs;
import org.chromium.chrome.browser.app.appmenu.AppMenuPropertiesDelegateImpl; import org.chromium.chrome.browser.app.appmenu.AppMenuPropertiesDelegateImpl;
...@@ -137,7 +136,6 @@ import org.chromium.chrome.browser.sync.ProfileSyncService; ...@@ -137,7 +136,6 @@ import org.chromium.chrome.browser.sync.ProfileSyncService;
import org.chromium.chrome.browser.sync.SyncController; import org.chromium.chrome.browser.sync.SyncController;
import org.chromium.chrome.browser.tab.AccessibilityVisibilityHandler; import org.chromium.chrome.browser.tab.AccessibilityVisibilityHandler;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabDelegateFactory;
import org.chromium.chrome.browser.tab.TabHidingType; import org.chromium.chrome.browser.tab.TabHidingType;
import org.chromium.chrome.browser.tab.TabLaunchType; import org.chromium.chrome.browser.tab.TabLaunchType;
import org.chromium.chrome.browser.tab.TabSelectionType; import org.chromium.chrome.browser.tab.TabSelectionType;
...@@ -172,7 +170,6 @@ import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; ...@@ -172,7 +170,6 @@ import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.components.browser_ui.modaldialog.AppModalPresenter; import org.chromium.components.browser_ui.modaldialog.AppModalPresenter;
import org.chromium.components.browser_ui.notifications.NotificationManagerProxy; import org.chromium.components.browser_ui.notifications.NotificationManagerProxy;
import org.chromium.components.browser_ui.notifications.NotificationManagerProxyImpl; import org.chromium.components.browser_ui.notifications.NotificationManagerProxyImpl;
import org.chromium.components.browser_ui.util.ComposedBrowserControlsVisibilityDelegate;
import org.chromium.components.browser_ui.widget.InsetObserverView; import org.chromium.components.browser_ui.widget.InsetObserverView;
import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController; import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController;
import org.chromium.components.browser_ui.widget.textbubble.TextBubble; import org.chromium.components.browser_ui.widget.textbubble.TextBubble;
...@@ -1561,12 +1558,6 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent> ...@@ -1561,12 +1558,6 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent>
return mActivityTabProvider; return mActivityTabProvider;
} }
public TabDelegateFactory getTabDelegateFactory() {
return new TabbedModeTabDelegateFactory(this,
new ComposedBrowserControlsVisibilityDelegate(), getShareDelegateSupplier(), null,
mRootUiCoordinator.getBottomSheetController());
}
/** /**
* Returns the {@link InsetObserverView} that has the current system window * Returns the {@link InsetObserverView} that has the current system window
* insets information. * insets information.
......
...@@ -23,12 +23,15 @@ import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem; ...@@ -23,12 +23,15 @@ import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserver; import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserver;
import org.chromium.chrome.browser.bookmarks.BookmarkListEntry.ViewType; import org.chromium.chrome.browser.bookmarks.BookmarkListEntry.ViewType;
import org.chromium.chrome.browser.bookmarks.BookmarkRow.Location; import org.chromium.chrome.browser.bookmarks.BookmarkRow.Location;
import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.signin.PersonalizedSigninPromoView; import org.chromium.chrome.browser.signin.PersonalizedSigninPromoView;
import org.chromium.chrome.browser.sync.ProfileSyncService; import org.chromium.chrome.browser.sync.ProfileSyncService;
import org.chromium.components.bookmarks.BookmarkId; import org.chromium.components.bookmarks.BookmarkId;
import org.chromium.components.bookmarks.BookmarkType; import org.chromium.components.bookmarks.BookmarkType;
import org.chromium.components.browser_ui.widget.dragreorder.DragReorderableListAdapter; import org.chromium.components.browser_ui.widget.dragreorder.DragReorderableListAdapter;
import org.chromium.components.browser_ui.widget.highlight.ViewHighlighter; import org.chromium.components.browser_ui.widget.highlight.ViewHighlighter;
import org.chromium.components.feature_engagement.EventConstants;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
...@@ -310,6 +313,11 @@ class BookmarkItemsAdapter extends DragReorderableListAdapter<BookmarkListEntry> ...@@ -310,6 +313,11 @@ class BookmarkItemsAdapter extends DragReorderableListAdapter<BookmarkListEntry>
} else { } else {
setBookmarks(mDelegate.getModel().getChildIDs(folder)); setBookmarks(mDelegate.getModel().getChildIDs(folder));
} }
if (folder.getType() == BookmarkType.READING_LIST) {
TrackerFactory.getTrackerForProfile(Profile.getLastUsedRegularProfile())
.notifyEvent(EventConstants.READ_LATER_BOOKMARK_FOLDER_OPENED);
}
} }
@Override @Override
......
...@@ -24,8 +24,10 @@ import org.chromium.chrome.browser.ChromeTabbedActivity; ...@@ -24,8 +24,10 @@ import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.IntentHandler; import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem; import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
import org.chromium.chrome.browser.document.ChromeLauncherActivity; import org.chromium.chrome.browser.document.ChromeLauncherActivity;
import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
import org.chromium.chrome.browser.preferences.SharedPreferencesManager; import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar; import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
...@@ -34,6 +36,7 @@ import org.chromium.components.bookmarks.BookmarkId; ...@@ -34,6 +36,7 @@ import org.chromium.components.bookmarks.BookmarkId;
import org.chromium.components.bookmarks.BookmarkType; import org.chromium.components.bookmarks.BookmarkType;
import org.chromium.components.browser_ui.widget.TintedDrawable; import org.chromium.components.browser_ui.widget.TintedDrawable;
import org.chromium.components.embedder_support.util.UrlConstants; import org.chromium.components.embedder_support.util.UrlConstants;
import org.chromium.components.feature_engagement.EventConstants;
import org.chromium.ui.base.DeviceFormFactor; import org.chromium.ui.base.DeviceFormFactor;
import org.chromium.ui.base.PageTransition; import org.chromium.ui.base.PageTransition;
...@@ -135,6 +138,9 @@ public class BookmarkUtils { ...@@ -135,6 +138,9 @@ public class BookmarkUtils {
new SnackbarController() {}, Snackbar.TYPE_ACTION, new SnackbarController() {}, Snackbar.TYPE_ACTION,
Snackbar.UMA_READING_LIST_BOOKMARK_ADDED); Snackbar.UMA_READING_LIST_BOOKMARK_ADDED);
snackbarManager.showSnackbar(snackbar); snackbarManager.showSnackbar(snackbar);
TrackerFactory.getTrackerForProfile(Profile.getLastUsedRegularProfile())
.notifyEvent(EventConstants.READ_LATER_ARTICLE_SAVED);
} }
}); });
} }
......
...@@ -477,7 +477,7 @@ public class CustomTabDelegateFactory implements TabDelegateFactory { ...@@ -477,7 +477,7 @@ public class CustomTabDelegateFactory implements TabDelegateFactory {
new TabContextMenuItemDelegate(tab, tabModelSelector, new TabContextMenuItemDelegate(tab, tabModelSelector,
EphemeralTabCoordinator.isSupported() ? mEphemeralTabCoordinator::get : () EphemeralTabCoordinator.isSupported() ? mEphemeralTabCoordinator::get : ()
-> null, -> null,
mActivity == null ? null : mActivity::getSnackbarManager), () -> {}, mActivity == null ? null : mActivity::getSnackbarManager),
shareDelegateSupplier, contextMenuMode, AppHooks.get().getExternalAuthUtils()); shareDelegateSupplier, contextMenuMode, AppHooks.get().getExternalAuthUtils());
} }
......
// Copyright 2020 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.read_later;
import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
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.feature_engagement.FeatureConstants;
/**
* Controller to manage when and how we show read later in-product-help messages to users.
*/
public class ReadLaterIPHController {
private final UserEducationHelper mUserEducationHelper;
private final AppMenuHandler mAppMenuHandler;
private final View mToolbarMenuButton;
/**
* Constructor.
* @param activity The current activity.
* @param toolbarMenuButton The toolbar menu button to which IPH will be anchored.
* @param appMenuHandler The app menu handler
*/
public ReadLaterIPHController(
Activity activity, View toolbarMenuButton, AppMenuHandler appMenuHandler) {
mToolbarMenuButton = toolbarMenuButton;
mAppMenuHandler = appMenuHandler;
mUserEducationHelper = new UserEducationHelper(activity,
new Handler(Looper.getMainLooper()), TrackerFactory::getTrackerForProfile);
}
/**
* Attempts to show an IPH text bubble about the read later option in app menu.
*/
public void onCopyContextMenuItemClicked() {
if (!ChromeFeatureList.isEnabled(ChromeFeatureList.READ_LATER)) return;
mUserEducationHelper.requestShowIPH(
new IPHCommandBuilder(mToolbarMenuButton.getContext().getResources(),
FeatureConstants.READ_LATER_APP_MENU_BOOKMARK_THIS_PAGE_FEATURE,
R.string.reading_list_save_pages_for_later,
R.string.reading_list_save_pages_for_later)
.setAnchorView(mToolbarMenuButton)
.setOnShowCallback(
() -> turnOnHighlightForMenuItem(R.id.bookmark_this_page_id))
.setOnDismissCallback(this::turnOffHighlightForMenuItem)
.build());
}
/**
* Attempts to show an IPH text bubble after a cold start.
*/
public void showColdStartIPH() {
showReadLaterAppMenuBookmarksIPH();
}
private void showReadLaterAppMenuBookmarksIPH() {
if (!ChromeFeatureList.isEnabled(ChromeFeatureList.READ_LATER)) return;
mUserEducationHelper.requestShowIPH(
new IPHCommandBuilder(mToolbarMenuButton.getContext().getResources(),
FeatureConstants.READ_LATER_APP_MENU_BOOKMARKS_FEATURE,
R.string.reading_list_find_in_bookmarks,
R.string.reading_list_find_in_bookmarks)
.setAnchorView(mToolbarMenuButton)
.setOnShowCallback(
() -> turnOnHighlightForMenuItem(R.id.all_bookmarks_menu_id))
.setOnDismissCallback(this::turnOffHighlightForMenuItem)
.build());
}
private void turnOnHighlightForMenuItem(int highlightMenuItemId) {
mAppMenuHandler.setMenuHighlight(highlightMenuItemId);
}
private void turnOffHighlightForMenuItem() {
mAppMenuHandler.clearMenuHighlight();
}
}
...@@ -7,6 +7,7 @@ include_rules = [ ...@@ -7,6 +7,7 @@ include_rules = [
"+chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java", "+chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java",
"+chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java", "+chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java",
"+chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java", "+chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java",
"+chrome/android/java/src/org/chromium/chrome/browser/feature_engagement/TrackerFactory.java",
"+chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoUtils.java", "+chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoUtils.java",
"+chrome/android/java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationServiceImpl.java", "+chrome/android/java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationServiceImpl.java",
"+chrome/android/java/src/org/chromium/chrome/browser/paint_preview/PaintPreviewTabHelper.java", "+chrome/android/java/src/org/chromium/chrome/browser/paint_preview/PaintPreviewTabHelper.java",
......
...@@ -25,15 +25,18 @@ import org.chromium.chrome.browser.compositor.bottombar.ephemeraltab.EphemeralTa ...@@ -25,15 +25,18 @@ import org.chromium.chrome.browser.compositor.bottombar.ephemeraltab.EphemeralTa
import org.chromium.chrome.browser.contextmenu.ContextMenuItemDelegate; import org.chromium.chrome.browser.contextmenu.ContextMenuItemDelegate;
import org.chromium.chrome.browser.document.ChromeLauncherActivity; import org.chromium.chrome.browser.document.ChromeLauncherActivity;
import org.chromium.chrome.browser.download.ChromeDownloadDelegate; import org.chromium.chrome.browser.download.ChromeDownloadDelegate;
import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
import org.chromium.chrome.browser.incognito.IncognitoUtils; import org.chromium.chrome.browser.incognito.IncognitoUtils;
import org.chromium.chrome.browser.multiwindow.MultiWindowUtils; import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings; import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab.state.CriticalPersistedTabData; import org.chromium.chrome.browser.tab.state.CriticalPersistedTabData;
import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.tabmodel.document.TabDelegate; import org.chromium.chrome.browser.tabmodel.document.TabDelegate;
import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
import org.chromium.components.embedder_support.util.UrlConstants; import org.chromium.components.embedder_support.util.UrlConstants;
import org.chromium.components.embedder_support.util.UrlUtilities; import org.chromium.components.embedder_support.util.UrlUtilities;
import org.chromium.components.feature_engagement.EventConstants;
import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.common.Referrer; import org.chromium.content_public.common.Referrer;
...@@ -52,6 +55,7 @@ public class TabContextMenuItemDelegate implements ContextMenuItemDelegate { ...@@ -52,6 +55,7 @@ public class TabContextMenuItemDelegate implements ContextMenuItemDelegate {
private boolean mLoadOriginalImageRequestedForPageLoad; private boolean mLoadOriginalImageRequestedForPageLoad;
private EmptyTabObserver mDataReductionProxyContextMenuTabObserver; private EmptyTabObserver mDataReductionProxyContextMenuTabObserver;
private final Supplier<EphemeralTabCoordinator> mEphemeralTabCoordinatorSupplier; private final Supplier<EphemeralTabCoordinator> mEphemeralTabCoordinatorSupplier;
private final Runnable mContextMenuCopyLinkObserver;
private final Supplier<SnackbarManager> mSnackbarManagerSupplier; private final Supplier<SnackbarManager> mSnackbarManagerSupplier;
/** /**
...@@ -59,10 +63,12 @@ public class TabContextMenuItemDelegate implements ContextMenuItemDelegate { ...@@ -59,10 +63,12 @@ public class TabContextMenuItemDelegate implements ContextMenuItemDelegate {
*/ */
public TabContextMenuItemDelegate(Tab tab, TabModelSelector tabModelSelector, public TabContextMenuItemDelegate(Tab tab, TabModelSelector tabModelSelector,
Supplier<EphemeralTabCoordinator> ephemeralTabCoordinatorSupplier, Supplier<EphemeralTabCoordinator> ephemeralTabCoordinatorSupplier,
Runnable contextMenuCopyLinkObserver,
Supplier<SnackbarManager> snackbarManagerSupplier) { Supplier<SnackbarManager> snackbarManagerSupplier) {
mTab = (TabImpl) tab; mTab = (TabImpl) tab;
mTabModelSelector = tabModelSelector; mTabModelSelector = tabModelSelector;
mEphemeralTabCoordinatorSupplier = ephemeralTabCoordinatorSupplier; mEphemeralTabCoordinatorSupplier = ephemeralTabCoordinatorSupplier;
mContextMenuCopyLinkObserver = contextMenuCopyLinkObserver;
mSnackbarManagerSupplier = snackbarManagerSupplier; mSnackbarManagerSupplier = snackbarManagerSupplier;
mDataReductionProxyContextMenuTabObserver = new EmptyTabObserver() { mDataReductionProxyContextMenuTabObserver = new EmptyTabObserver() {
...@@ -119,6 +125,10 @@ public class TabContextMenuItemDelegate implements ContextMenuItemDelegate { ...@@ -119,6 +125,10 @@ public class TabContextMenuItemDelegate implements ContextMenuItemDelegate {
@Override @Override
public void onSaveToClipboard(String text, int clipboardType) { public void onSaveToClipboard(String text, int clipboardType) {
Clipboard.getInstance().setText(text); Clipboard.getInstance().setText(text);
if (clipboardType == ClipboardType.LINK_URL) {
// TODO(crbug/1150090): Find a better way of passing event for IPH.
mContextMenuCopyLinkObserver.run();
}
} }
@Override @Override
...@@ -262,6 +272,8 @@ public class TabContextMenuItemDelegate implements ContextMenuItemDelegate { ...@@ -262,6 +272,8 @@ public class TabContextMenuItemDelegate implements ContextMenuItemDelegate {
public void onReadLater(String url, String title) { public void onReadLater(String url, String title) {
BookmarkUtils.addToReadingList( BookmarkUtils.addToReadingList(
url, title, mSnackbarManagerSupplier.get(), mTab.getContext()); url, title, mSnackbarManagerSupplier.get(), mTab.getContext());
TrackerFactory.getTrackerForProfile(Profile.getLastUsedRegularProfile())
.notifyEvent(EventConstants.READ_LATER_CONTEXT_MENU_TAPPED);
} }
@Override @Override
......
...@@ -49,6 +49,7 @@ import org.chromium.chrome.browser.omnibox.UrlFocusChangeListener; ...@@ -49,6 +49,7 @@ import org.chromium.chrome.browser.omnibox.UrlFocusChangeListener;
import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
import org.chromium.chrome.browser.preferences.SharedPreferencesManager; import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.read_later.ReadLaterIPHController;
import org.chromium.chrome.browser.share.ShareDelegate; import org.chromium.chrome.browser.share.ShareDelegate;
import org.chromium.chrome.browser.signin.SigninPromoUtil; import org.chromium.chrome.browser.signin.SigninPromoUtil;
import org.chromium.chrome.browser.status_indicator.StatusIndicatorCoordinator; import org.chromium.chrome.browser.status_indicator.StatusIndicatorCoordinator;
...@@ -81,6 +82,7 @@ public class TabbedRootUiCoordinator extends RootUiCoordinator { ...@@ -81,6 +82,7 @@ public class TabbedRootUiCoordinator extends RootUiCoordinator {
private StatusIndicatorCoordinator.StatusIndicatorObserver mStatusIndicatorObserver; private StatusIndicatorCoordinator.StatusIndicatorObserver mStatusIndicatorObserver;
private OfflineIndicatorControllerV2 mOfflineIndicatorController; private OfflineIndicatorControllerV2 mOfflineIndicatorController;
private OfflineIndicatorInProductHelpController mOfflineIndicatorInProductHelpController; private OfflineIndicatorInProductHelpController mOfflineIndicatorInProductHelpController;
private ReadLaterIPHController mReadLaterIPHController;
private UrlFocusChangeListener mUrlFocusChangeListener; private UrlFocusChangeListener mUrlFocusChangeListener;
private @Nullable ToolbarButtonInProductHelpController mToolbarButtonInProductHelpController; private @Nullable ToolbarButtonInProductHelpController mToolbarButtonInProductHelpController;
private AppBannerInProductHelpController mAppBannerInProductHelpController; private AppBannerInProductHelpController mAppBannerInProductHelpController;
...@@ -326,10 +328,13 @@ public class TabbedRootUiCoordinator extends RootUiCoordinator { ...@@ -326,10 +328,13 @@ public class TabbedRootUiCoordinator extends RootUiCoordinator {
} }
}; };
mToolbarButtonIphTabSupplier.set(activityTabProvider.get()); mToolbarButtonIphTabSupplier.set(activityTabProvider.get());
mReadLaterIPHController = new ReadLaterIPHController(mActivity,
getToolbarManager().getMenuButtonView(), mAppMenuCoordinator.getAppMenuHandler());
boolean didTriggerPromo = triggerPromo(intentWithEffect); boolean didTriggerPromo = triggerPromo(intentWithEffect);
if (!didTriggerPromo) { if (!didTriggerPromo) {
mToolbarButtonInProductHelpController.showColdStartIPH(); mToolbarButtonInProductHelpController.showColdStartIPH();
mReadLaterIPHController.showColdStartIPH();
} }
if (ChromeFeatureList.isEnabled(ChromeFeatureList.TOOLBAR_IPH_ANDROID)) { if (ChromeFeatureList.isEnabled(ChromeFeatureList.TOOLBAR_IPH_ANDROID)) {
mPromoShownOneshotSupplier.set(didTriggerPromo); mPromoShownOneshotSupplier.set(didTriggerPromo);
...@@ -459,6 +464,12 @@ public class TabbedRootUiCoordinator extends RootUiCoordinator { ...@@ -459,6 +464,12 @@ public class TabbedRootUiCoordinator extends RootUiCoordinator {
return mNavigationSheet; return mNavigationSheet;
} }
/** Called when a link is copied through context menu. */
public void onContextMenuCopyLink() {
// TODO(crbug/1150090): Find a better way of passing event for IPH.
mReadLaterIPHController.onCopyContextMenuItemClicked();
}
/** /**
* Triggers the display of an appropriate promo, if any, returning true if a promo is actually * Triggers the display of an appropriate promo, if any, returning true if a promo is actually
* displayed. * displayed.
......
// Copyright 2020 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.read_later;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.chromium.chrome.browser.toolbar.top.ButtonHighlightMatcher.withHighlight;
import static org.chromium.chrome.test.util.ViewUtils.waitForView;
import android.os.Looper;
import android.support.test.InstrumentationRegistry;
import android.view.View;
import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.matcher.RootMatchers;
import androidx.test.filters.MediumTest;
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.chromium.base.Callback;
import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Restriction;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.app.ChromeActivity;
import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.util.browser.Features;
import org.chromium.chrome.test.util.browser.contextmenu.RevampedContextMenuUtils;
import org.chromium.components.feature_engagement.EventConstants;
import org.chromium.components.feature_engagement.FeatureConstants;
import org.chromium.components.feature_engagement.Tracker;
import org.chromium.net.test.EmbeddedTestServerRule;
import org.chromium.ui.test.util.UiRestriction;
/** Integration tests for showing IPH bubbles for read later. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
@Features.EnableFeatures(ChromeFeatureList.READ_LATER)
@Batch(Batch.PER_CLASS)
public class ReadLaterIphTest {
@Rule
public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
@Rule
public EmbeddedTestServerRule mTestServer = new EmbeddedTestServerRule();
@Rule
public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock
private Tracker mTracker;
private static final String CONTEXT_MENU_TEST_URL =
"/chrome/test/data/android/contextmenu/context_menu_test.html";
private static final String CONTEXT_MENU_LINK_DOM_ID = "testLink";
@Before
public void setUp() {
Looper.prepare();
// Pretend the feature engagement feature is already initialized. Otherwise
// UserEducationHelper#requestShowIPH() calls get dropped during test.
doAnswer(invocation -> {
invocation.<Callback<Boolean>>getArgument(0).onResult(true);
return null;
})
.when(mTracker)
.addOnInitializedCallback(any());
TrackerFactory.setTrackerForTests(mTracker);
mActivityTestRule.startMainActivityOnBlankPage();
}
@After
public void tearDown() throws Exception {
TrackerFactory.setTrackerForTests(null);
}
@Test
@MediumTest
@Restriction({UiRestriction.RESTRICTION_TYPE_PHONE})
public void testShowIPHOnContextMenuLinkCopied() throws Throwable {
when(mTracker.shouldTriggerHelpUI(
FeatureConstants.READ_LATER_APP_MENU_BOOKMARK_THIS_PAGE_FEATURE))
.thenReturn(true);
mActivityTestRule.loadUrlInNewTab(mTestServer.getServer().getURL(CONTEXT_MENU_TEST_URL));
ChromeActivity activity = mActivityTestRule.getActivity();
Tab tab = activity.getActivityTab();
RevampedContextMenuUtils.selectContextMenuItem(InstrumentationRegistry.getInstrumentation(),
activity, tab, CONTEXT_MENU_LINK_DOM_ID, R.id.contextmenu_copy_link_address);
onView(withId(R.id.menu_button_wrapper)).check(matches(withHighlight(true)));
waitForHelpBubble(withText(R.string.reading_list_save_pages_for_later));
}
@Test
@MediumTest
@Restriction({UiRestriction.RESTRICTION_TYPE_PHONE})
public void testShowBookmarksReadLaterIPH() throws Throwable {
mActivityTestRule.loadUrl(mTestServer.getServer().getURL(CONTEXT_MENU_TEST_URL));
ChromeActivity activity = mActivityTestRule.getActivity();
Tab tab = activity.getActivityTab();
RevampedContextMenuUtils.selectContextMenuItem(InstrumentationRegistry.getInstrumentation(),
activity, tab, CONTEXT_MENU_LINK_DOM_ID, R.id.contextmenu_read_later);
verify(mTracker, times(1)).notifyEvent(EventConstants.READ_LATER_CONTEXT_MENU_TAPPED);
}
private ViewInteraction waitForHelpBubble(Matcher<View> matcher) {
View mainDecorView = mActivityTestRule.getActivity().getWindow().getDecorView();
return onView(isRoot())
.inRoot(RootMatchers.withDecorView(not(is(mainDecorView))))
.check(waitForView(matcher));
}
}
...@@ -72,11 +72,13 @@ public class TabUmaTest { ...@@ -72,11 +72,13 @@ public class TabUmaTest {
private TabbedModeTabDelegateFactory createTabDelegateFactory() { private TabbedModeTabDelegateFactory createTabDelegateFactory() {
BrowserControlsVisibilityDelegate visibilityDelegate = BrowserControlsVisibilityDelegate visibilityDelegate =
new BrowserControlsVisibilityDelegate(BrowserControlsState.BOTH) {}; new BrowserControlsVisibilityDelegate(BrowserControlsState.BOTH) {};
// clang-format off
return new TabbedModeTabDelegateFactory(mActivityTestRule.getActivity(), visibilityDelegate, return new TabbedModeTabDelegateFactory(mActivityTestRule.getActivity(), visibilityDelegate,
new ObservableSupplierImpl<ShareDelegate>(), null, new ObservableSupplierImpl<ShareDelegate>(), null,
mActivityTestRule.getActivity() () -> {}, mActivityTestRule.getActivity()
.getRootUiCoordinatorForTesting() .getRootUiCoordinatorForTesting()
.getBottomSheetController()); .getBottomSheetController());
// clang-format on
} }
private Tab createLazilyLoadedTab(boolean show) throws ExecutionException { private Tab createLazilyLoadedTab(boolean show) throws ExecutionException {
......
dtrainor@chromium.org dtrainor@chromium.org
shaktisahu@chromium.org
xingliu@chromium.org xingliu@chromium.org
# Backup reviewers: # Backup reviewers:
......
...@@ -180,6 +180,12 @@ public final class EventConstants { ...@@ -180,6 +180,12 @@ public final class EventConstants {
public static final String TAB_SWITCHER_BUTTON_CLICKED = "tab_switcher_button_clicked"; public static final String TAB_SWITCHER_BUTTON_CLICKED = "tab_switcher_button_clicked";
/** Read later related events. */
public static final String READ_LATER_CONTEXT_MENU_TAPPED = "read_later_context_menu_tapped";
public static final String READ_LATER_ARTICLE_SAVED = "read_later_article_saved";
public static final String READ_LATER_BOOKMARK_FOLDER_OPENED =
"read_later_bookmark_folder_opened";
/** Video tutorial related events. */ /** Video tutorial related events. */
public static final String VIDEO_TUTORIAL_DISMISSED_SUMMARY = public static final String VIDEO_TUTORIAL_DISMISSED_SUMMARY =
"video_tutorial_iph_dismissed_summary"; "video_tutorial_iph_dismissed_summary";
......
...@@ -65,6 +65,8 @@ public @interface FeatureConstants { ...@@ -65,6 +65,8 @@ public @interface FeatureConstants {
String TRANSLATE_MENU_BUTTON_FEATURE = "IPH_TranslateMenuButton"; String TRANSLATE_MENU_BUTTON_FEATURE = "IPH_TranslateMenuButton";
String EXPLORE_SITES_TILE_FEATURE = "IPH_ExploreSitesTile"; String EXPLORE_SITES_TILE_FEATURE = "IPH_ExploreSitesTile";
String READ_LATER_CONTEXT_MENU_FEATURE = "IPH_ReadLaterContextMenu"; String READ_LATER_CONTEXT_MENU_FEATURE = "IPH_ReadLaterContextMenu";
String READ_LATER_APP_MENU_BOOKMARK_THIS_PAGE_FEATURE = "IPH_ReadLaterAppMenuBookmarkThisPage";
String READ_LATER_APP_MENU_BOOKMARKS_FEATURE = "IPH_ReadLaterAppMenuBookmarks";
/** /**
* An IPH feature that encourages users to get better translations by enabling access to page * An IPH feature that encourages users to get better translations by enabling access to page
......
...@@ -77,6 +77,10 @@ const base::Feature kIPHQuietNotificationPromptsFeature{ ...@@ -77,6 +77,10 @@ const base::Feature kIPHQuietNotificationPromptsFeature{
"IPH_QuietNotificationPrompts", base::FEATURE_DISABLED_BY_DEFAULT}; "IPH_QuietNotificationPrompts", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kIPHReadLaterContextMenuFeature{ const base::Feature kIPHReadLaterContextMenuFeature{
"IPH_ReadLaterContextMenu", base::FEATURE_DISABLED_BY_DEFAULT}; "IPH_ReadLaterContextMenu", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kIPHReadLaterAppMenuBookmarkThisPageFeature{
"IPH_ReadLaterAppMenuBookmarkThisPage", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kIPHReadLaterAppMenuBookmarksFeature{
"IPH_ReadLaterAppMenuBookmarks", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kIPHEphemeralTabFeature{"IPH_EphemeralTab", const base::Feature kIPHEphemeralTabFeature{"IPH_EphemeralTab",
base::FEATURE_DISABLED_BY_DEFAULT}; base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kIPHFeedCardMenuFeature{"IPH_FeedCardMenu", const base::Feature kIPHFeedCardMenuFeature{"IPH_FeedCardMenu",
......
...@@ -66,6 +66,8 @@ extern const base::Feature kIPHNewTabPageHomeButtonFeature; ...@@ -66,6 +66,8 @@ extern const base::Feature kIPHNewTabPageHomeButtonFeature;
extern const base::Feature kIPHPreviewsOmniboxUIFeature; extern const base::Feature kIPHPreviewsOmniboxUIFeature;
extern const base::Feature kIPHQuietNotificationPromptsFeature; extern const base::Feature kIPHQuietNotificationPromptsFeature;
extern const base::Feature kIPHReadLaterContextMenuFeature; extern const base::Feature kIPHReadLaterContextMenuFeature;
extern const base::Feature kIPHReadLaterAppMenuBookmarkThisPageFeature;
extern const base::Feature kIPHReadLaterAppMenuBookmarksFeature;
extern const base::Feature kIPHTabGroupsQuicklyComparePagesFeature; extern const base::Feature kIPHTabGroupsQuicklyComparePagesFeature;
extern const base::Feature kIPHTabGroupsTapToSeeAnotherTabFeature; extern const base::Feature kIPHTabGroupsTapToSeeAnotherTabFeature;
extern const base::Feature kIPHTabGroupsYourTabsAreTogetherFeature; extern const base::Feature kIPHTabGroupsYourTabsAreTogetherFeature;
......
...@@ -50,6 +50,8 @@ const base::Feature* const kAllFeatures[] = { ...@@ -50,6 +50,8 @@ const base::Feature* const kAllFeatures[] = {
&kIPHPwaInstallAvailableFeature, &kIPHPwaInstallAvailableFeature,
&kIPHQuietNotificationPromptsFeature, &kIPHQuietNotificationPromptsFeature,
&kIPHReadLaterContextMenuFeature, &kIPHReadLaterContextMenuFeature,
&kIPHReadLaterAppMenuBookmarkThisPageFeature,
&kIPHReadLaterAppMenuBookmarksFeature,
&kIPHTabGroupsQuicklyComparePagesFeature, &kIPHTabGroupsQuicklyComparePagesFeature,
&kIPHTabGroupsTapToSeeAnotherTabFeature, &kIPHTabGroupsTapToSeeAnotherTabFeature,
&kIPHTabGroupsYourTabsAreTogetherFeature, &kIPHTabGroupsYourTabsAreTogetherFeature,
......
...@@ -101,6 +101,10 @@ DEFINE_VARIATION_PARAM(kIPHQuietNotificationPromptsFeature, ...@@ -101,6 +101,10 @@ DEFINE_VARIATION_PARAM(kIPHQuietNotificationPromptsFeature,
"IPH_QuietNotificationPrompts"); "IPH_QuietNotificationPrompts");
DEFINE_VARIATION_PARAM(kIPHReadLaterContextMenuFeature, DEFINE_VARIATION_PARAM(kIPHReadLaterContextMenuFeature,
"IPH_ReadLaterContextMenu"); "IPH_ReadLaterContextMenu");
DEFINE_VARIATION_PARAM(kIPHReadLaterAppMenuBookmarkThisPageFeature,
"IPH_ReadLaterAppMenuBookmarkThisPage");
DEFINE_VARIATION_PARAM(kIPHReadLaterAppMenuBookmarksFeature,
"IPH_ReadLaterAppMenuBookmarks");
DEFINE_VARIATION_PARAM(kIPHTabGroupsQuicklyComparePagesFeature, DEFINE_VARIATION_PARAM(kIPHTabGroupsQuicklyComparePagesFeature,
"IPH_TabGroupsQuicklyComparePages"); "IPH_TabGroupsQuicklyComparePages");
DEFINE_VARIATION_PARAM(kIPHTabGroupsTapToSeeAnotherTabFeature, DEFINE_VARIATION_PARAM(kIPHTabGroupsTapToSeeAnotherTabFeature,
...@@ -195,6 +199,8 @@ constexpr flags_ui::FeatureEntry::FeatureVariation ...@@ -195,6 +199,8 @@ constexpr flags_ui::FeatureEntry::FeatureVariation
VARIATION_ENTRY(kIPHPwaInstallAvailableFeature), VARIATION_ENTRY(kIPHPwaInstallAvailableFeature),
VARIATION_ENTRY(kIPHQuietNotificationPromptsFeature), VARIATION_ENTRY(kIPHQuietNotificationPromptsFeature),
VARIATION_ENTRY(kIPHReadLaterContextMenuFeature), VARIATION_ENTRY(kIPHReadLaterContextMenuFeature),
VARIATION_ENTRY(kIPHReadLaterAppMenuBookmarkThisPageFeature),
VARIATION_ENTRY(kIPHReadLaterAppMenuBookmarksFeature),
VARIATION_ENTRY(kIPHTabGroupsQuicklyComparePagesFeature), VARIATION_ENTRY(kIPHTabGroupsQuicklyComparePagesFeature),
VARIATION_ENTRY(kIPHTabGroupsTapToSeeAnotherTabFeature), VARIATION_ENTRY(kIPHTabGroupsTapToSeeAnotherTabFeature),
VARIATION_ENTRY(kIPHTabGroupsYourTabsAreTogetherFeature), VARIATION_ENTRY(kIPHTabGroupsYourTabsAreTogetherFeature),
......
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