[go: nahoru, domu]

[M123] IPH and highlight for the app menu button

Add a ReadAloud IPH controller class that will decide whether to
request an IPH for the 'Listen to this page' button in the app menu
depending on the tab's readability. Triggered onPageLoadFinished.

(cherry picked from commit be8ecacd63897656c549593bd405e21114115df0)

Bug: 323224119, b/326618992
Change-Id: I0beac883f426dfad5be99f200acdfb83d310fa3f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5318435
Reviewed-by: Dana Fried <dfried@chromium.org>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Reviewed-by: Basia Zimirska <basiaz@google.com>
Reviewed-by: Tommy Nyquist <nyquist@chromium.org>
Commit-Queue: Andrea Gomez <andreaxg@google.com>
Cr-Original-Commit-Position: refs/heads/main@{#1264301}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5328777
Cr-Commit-Position: refs/branch-heads/6312@{#231}
Cr-Branched-From: 6711dcdae48edaf98cbc6964f90fac85b7d9986e-refs/heads/main@{#1262506}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
index ccc799b..8087c0e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
@@ -91,6 +91,7 @@
 import org.chromium.chrome.browser.privacy_sandbox.TrackingProtectionNoticeController;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.read_later.ReadLaterIPHController;
+import org.chromium.chrome.browser.readaloud.ReadAloudIPHController;
 import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
 import org.chromium.chrome.browser.share.ShareDelegate;
 import org.chromium.chrome.browser.share.link_to_text.LinkToTextIPHController;
@@ -154,6 +155,7 @@
     private StatusIndicatorCoordinator.StatusIndicatorObserver mStatusIndicatorObserver;
     private OfflineIndicatorControllerV2 mOfflineIndicatorController;
     private OfflineIndicatorInProductHelpController mOfflineIndicatorInProductHelpController;
+    private ReadAloudIPHController mReadAloudIPHController;
     private ReadLaterIPHController mReadLaterIPHController;
     private DesktopSiteSettingsIPHController mDesktopSiteSettingsIPHController;
     private WebFeedFollowIntroController mWebFeedFollowIntroController;
@@ -433,6 +435,10 @@
             mToolbarButtonInProductHelpController.destroy();
         }
 
+        if (mReadAloudIPHController != null) {
+            mReadAloudIPHController.destroy();
+        }
+
         if (mWebFeedFollowIntroController != null) {
             mWebFeedFollowIntroController.destroy();
         }
@@ -825,6 +831,13 @@
                         mIsInOverviewModeSupplier,
                         mToolbarManager.getMenuButtonView(),
                         mToolbarManager.getSecurityIconView());
+        mReadAloudIPHController =
+                new ReadAloudIPHController(
+                        mActivity,
+                        getToolbarManager().getMenuButtonView(),
+                        mAppMenuCoordinator.getAppMenuHandler(),
+                        mActivityTabProvider,
+                        mReadAloudControllerSupplier);
         mReadLaterIPHController =
                 new ReadLaterIPHController(
                         mActivity,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
index 69e5fc0..c73b2b8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -319,7 +319,7 @@
     @Nullable protected final BackPressManager mBackPressManager;
     private final boolean mIsIncognitoReauthPendingOnRestore;
     protected final ExpandedSheetHelper mExpandedBottomSheetHelper;
-    private final ObservableSupplierImpl<ReadAloudController> mReadAloudControllerSupplier =
+    protected final ObservableSupplierImpl<ReadAloudController> mReadAloudControllerSupplier =
             new ObservableSupplierImpl<>();
     @Nullable private ContextualSearchObserver mReadAloudContextualSearchObserver;
     @Nullable private PageZoomCoordinator mPageZoomCoordinator;
diff --git a/chrome/browser/readaloud/android/BUILD.gn b/chrome/browser/readaloud/android/BUILD.gn
index 13abde9..1cf039a 100644
--- a/chrome/browser/readaloud/android/BUILD.gn
+++ b/chrome/browser/readaloud/android/BUILD.gn
@@ -8,6 +8,7 @@
 android_library("java") {
   sources = [
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java",
+    "java/src/org/chromium/chrome/browser/readaloud/ReadAloudIPHController.java",
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudMiniPlayerSceneLayer.java",
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudPrefs.java",
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudToolbarButtonController.java",
@@ -26,6 +27,7 @@
     "//chrome/browser/profiles/android:java",
     "//chrome/browser/tab:java",
     "//chrome/browser/tabmodel:java",
+    "//chrome/browser/ui/android/appmenu:java",
     "//chrome/browser/ui/android/layouts:java",
     "//chrome/browser/ui/android/strings:ui_strings_grd",
     "//chrome/browser/ui/android/toolbar:java",
@@ -174,6 +176,7 @@
   testonly = true
   sources = [
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java",
+    "java/src/org/chromium/chrome/browser/readaloud/ReadAloudIPHControllerUnitTest.java",
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudMiniPlayerSceneLayerUnitTest.java",
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudPrefsUnitTest.java",
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudToolbarButtonControllerUnitTest.java",
@@ -205,8 +208,10 @@
     "//chrome/browser/signin/services/android:java",
     "//chrome/browser/tab:java",
     "//chrome/browser/tabmodel:java",
+    "//chrome/browser/ui/android/appmenu:java",
     "//chrome/browser/ui/android/layouts:java",
     "//chrome/browser/ui/android/toolbar:java",
+    "//chrome/browser/user_education:java",
     "//chrome/test/android:chrome_java_unit_test_support",
     "//components/browser_ui/bottomsheet/android:java",
     "//components/feature_engagement/public:public_java",
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudIPHController.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudIPHController.java
new file mode 100644
index 0000000..2b2f115
--- /dev/null
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudIPHController.java
@@ -0,0 +1,127 @@
+// Copyright 2024 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.readaloud;
+
+import android.app.Activity;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.base.supplier.Supplier;
+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.feature_engagement.FeatureConstants;
+
+/**
+ * Controller to manage when and how we show ReadAloud in-product-help messages to users in the app
+ * menu.
+ */
+public class ReadAloudIPHController {
+    private final UserEducationHelper mUserEducationHelper;
+    private final AppMenuHandler mAppMenuHandler;
+    private final View mToolbarMenuButton;
+    private final ObservableSupplier<ReadAloudController> mReadAloudControllerSupplier;
+    @Nullable ObservableSupplier<String> mReadAloudReadabilitySupplier;
+    private final Supplier<Tab> mCurrentTabSupplier;
+
+    /**
+     * Constructor.
+     *
+     * @param activity The current activity.
+     * @param toolbarMenuButton The toolbar menu button to which IPH will be anchored.
+     * @param appMenuHandler The app menu handler
+     * @param tabSupplier The tab supplier
+     * @param readAloudControllerSupplier Supplies the readaloud controller
+     */
+    public ReadAloudIPHController(
+            Activity activity,
+            View toolbarMenuButton,
+            AppMenuHandler appMenuHandler,
+            ObservableSupplier<Tab> tabSupplier,
+            ObservableSupplier<ReadAloudController> readAloudControllerSupplier) {
+        this(
+                activity,
+                toolbarMenuButton,
+                appMenuHandler,
+                new UserEducationHelper(activity, new Handler(Looper.getMainLooper())),
+                tabSupplier,
+                readAloudControllerSupplier);
+    }
+
+    ReadAloudIPHController(
+            Activity activity,
+            View toolbarMenuButton,
+            AppMenuHandler appMenuHandler,
+            UserEducationHelper userEducationHelper,
+            ObservableSupplier<Tab> tabSupplier,
+            ObservableSupplier<ReadAloudController> readAloudControllerSupplier) {
+        mToolbarMenuButton = toolbarMenuButton;
+        mAppMenuHandler = appMenuHandler;
+        mUserEducationHelper = userEducationHelper;
+        mCurrentTabSupplier = tabSupplier;
+        mReadAloudControllerSupplier = readAloudControllerSupplier;
+        mReadAloudControllerSupplier.addObserver(this::readAloudControllerReady);
+    }
+
+    /**
+     * If the current tab is readable, requests to show a "Listen to this page" IPH for the app menu
+     * and turns on the highlight for the ReadAloud item in the menu.
+     *
+     * @param url URL the readability check returns
+     */
+    public void maybeShowReadAloudAppMenuIPH(String url) {
+        if (shouldShowIPH(url)) {
+            mUserEducationHelper.requestShowIPH(
+                    new IPHCommandBuilder(
+                                    mToolbarMenuButton.getContext().getResources(),
+                                    FeatureConstants.READ_ALOUD_APP_MENU_FEATURE,
+                                    R.string.menu_listen_to_this_page,
+                                    R.string.menu_listen_to_this_page)
+                            .setAnchorView(mToolbarMenuButton)
+                            .setOnShowCallback(
+                                    () -> turnOnHighlightForMenuItem(R.id.readaloud_menu_id))
+                            .setOnDismissCallback(this::turnOffHighlightForMenuItem)
+                            .build());
+        }
+    }
+
+    private void turnOnHighlightForMenuItem(int highlightMenuItemId) {
+        mAppMenuHandler.setMenuHighlight(highlightMenuItemId);
+    }
+
+    private void turnOffHighlightForMenuItem() {
+        mAppMenuHandler.clearMenuHighlight();
+    }
+
+    protected boolean shouldShowIPH(String url) {
+        if (mCurrentTabSupplier.get() == null
+                || !mCurrentTabSupplier.get().getUrl().isValid()
+                || mReadAloudControllerSupplier.get() == null) {
+            return false;
+        }
+        if (mCurrentTabSupplier.get().getUrl().getSpec().equals(url)) {
+            return mReadAloudControllerSupplier.get().isReadable(mCurrentTabSupplier.get());
+        }
+        return false;
+    }
+
+    void readAloudControllerReady(@Nullable ReadAloudController readAloudController) {
+        if (readAloudController != null) {
+            mReadAloudReadabilitySupplier = readAloudController.getReadabilitySupplier();
+            mReadAloudReadabilitySupplier.addObserver(this::maybeShowReadAloudAppMenuIPH);
+        }
+    }
+
+    public void destroy() {
+        if (mReadAloudReadabilitySupplier != null) {
+            mReadAloudReadabilitySupplier.removeObserver(this::maybeShowReadAloudAppMenuIPH);
+        }
+    }
+}
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudIPHControllerUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudIPHControllerUnitTest.java
new file mode 100644
index 0000000..db854e46
--- /dev/null
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudIPHControllerUnitTest.java
@@ -0,0 +1,127 @@
+// Copyright 2024 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.readaloud;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.base.supplier.ObservableSupplierImpl;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Features.JUnitProcessor;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.tab.MockTab;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.ui.appmenu.AppMenuHandler;
+import org.chromium.chrome.browser.user_education.IPHCommand;
+import org.chromium.chrome.browser.user_education.UserEducationHelper;
+import org.chromium.url.GURL;
+import org.chromium.url.JUnitTestGURLs;
+
+/** Unit test for {@link ReadAloudIPHController}. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class ReadAloudIPHControllerUnitTest {
+    @Rule public TestRule mFeaturesProcessor = new JUnitProcessor();
+
+    @Mock Activity mActivity;
+    @Mock View mToolbarMenuButton;
+    @Mock AppMenuHandler mAppMenuHandler;
+    @Mock UserEducationHelper mUserEducationHelper;
+    @Mock Context mContext;
+    @Mock Resources mResources;
+    @Captor ArgumentCaptor<IPHCommand> mIPHCommandCaptor;
+    @Mock private ObservableSupplier<Tab> mMockTabProvider;
+    @Mock ReadAloudController mReadAloudController;
+    ObservableSupplierImpl<ReadAloudController> mReadAloudControllerSupplier;
+    private MockTab mTab;
+    @Mock private Profile mProfile;
+    private static final GURL sTestGURL = JUnitTestGURLs.EXAMPLE_URL;
+
+    ReadAloudIPHController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        doReturn(mResources).when(mContext).getResources();
+        doReturn(mContext).when(mToolbarMenuButton).getContext();
+
+        doReturn(false).when(mProfile).isOffTheRecord();
+        mTab = new MockTab(1, mProfile);
+        mTab.setGurlOverrideForTesting(sTestGURL);
+        doReturn(mTab).when(mMockTabProvider).get();
+
+        mReadAloudControllerSupplier = new ObservableSupplierImpl<>();
+        mReadAloudControllerSupplier.set(mReadAloudController);
+        doReturn(true).when(mReadAloudController).isReadable(mTab);
+
+        mController =
+                new ReadAloudIPHController(
+                        mActivity,
+                        mToolbarMenuButton,
+                        mAppMenuHandler,
+                        mUserEducationHelper,
+                        mMockTabProvider,
+                        mReadAloudControllerSupplier);
+    }
+
+    @Test
+    @SmallTest
+    public void maybeShowReadAloudAppMenuIPH() {
+        mController.maybeShowReadAloudAppMenuIPH(sTestGURL.getSpec());
+        verify(mUserEducationHelper).requestShowIPH(mIPHCommandCaptor.capture());
+
+        IPHCommand command = mIPHCommandCaptor.getValue();
+        command.onShowCallback.run();
+        verify(mAppMenuHandler).setMenuHighlight(R.id.readaloud_menu_id);
+
+        command.onDismissCallback.run();
+        verify(mAppMenuHandler).clearMenuHighlight();
+    }
+
+    @Test
+    @SmallTest
+    public void maybeShowReadAloudAppMenuIPH_false() {
+        doReturn(false).when(mReadAloudController).isReadable(mTab);
+
+        mController.maybeShowReadAloudAppMenuIPH(sTestGURL.getSpec());
+        verify(mUserEducationHelper, never()).requestShowIPH(mIPHCommandCaptor.capture());
+    }
+
+    @Test
+    @SmallTest
+    public void maybeShowReadAloudAppMenuIPH_invalid() {
+        // mismatched urls
+        mController.maybeShowReadAloudAppMenuIPH("https://en.wikipedia.org/wiki/Google");
+        verify(mUserEducationHelper, never()).requestShowIPH(mIPHCommandCaptor.capture());
+        // invalid url
+        mController.maybeShowReadAloudAppMenuIPH("http://0x100.0/");
+        verify(mUserEducationHelper, never()).requestShowIPH(mIPHCommandCaptor.capture());
+        // null tab
+        doReturn(null).when(mMockTabProvider).get();
+        mController.maybeShowReadAloudAppMenuIPH(sTestGURL.getSpec());
+        verify(mUserEducationHelper, never()).requestShowIPH(mIPHCommandCaptor.capture());
+    }
+}
diff --git a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java
index 421bf67..34457b0 100644
--- a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java
+++ b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java
@@ -78,6 +78,7 @@
     FeatureConstants.PAGE_INFO_FEATURE,
     FeatureConstants.PAGE_INFO_STORE_INFO_FEATURE,
     FeatureConstants.PAGE_ZOOM_FEATURE,
+    FeatureConstants.READ_ALOUD_APP_MENU_FEATURE,
     FeatureConstants.READ_LATER_APP_MENU_BOOKMARK_THIS_PAGE_FEATURE,
     FeatureConstants.READ_LATER_APP_MENU_BOOKMARKS_FEATURE,
     FeatureConstants.READ_LATER_BOTTOM_SHEET_FEATURE,
@@ -152,6 +153,7 @@
     String PAGE_ZOOM_FEATURE = "IPH_PageZoom";
     String PREVIEWS_OMNIBOX_UI_FEATURE = "IPH_PreviewsOmniboxUI";
     String TRANSLATE_MENU_BUTTON_FEATURE = "IPH_TranslateMenuButton";
+    String READ_ALOUD_APP_MENU_FEATURE = "IPH_ReadAloudAppMenuFeature";
     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";
diff --git a/components/feature_engagement/public/feature_constants.cc b/components/feature_engagement/public/feature_constants.cc
index 6b70faf..53c4800 100644
--- a/components/feature_engagement/public/feature_constants.cc
+++ b/components/feature_engagement/public/feature_constants.cc
@@ -256,6 +256,9 @@
 BASE_FEATURE(kIPHQuietNotificationPromptsFeature,
              "IPH_QuietNotificationPrompts",
              base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kIPHReadAloudAppMenuFeature,
+             "IPH_ReadAloudAppMenuFeature",
+             base::FEATURE_DISABLED_BY_DEFAULT);
 BASE_FEATURE(kIPHReadLaterContextMenuFeature,
              "IPH_ReadLaterContextMenu",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/components/feature_engagement/public/feature_constants.h b/components/feature_engagement/public/feature_constants.h
index db5d614..26b01de 100644
--- a/components/feature_engagement/public/feature_constants.h
+++ b/components/feature_engagement/public/feature_constants.h
@@ -148,6 +148,7 @@
 BASE_DECLARE_FEATURE(kIPHPreviewsOmniboxUIFeature);
 BASE_DECLARE_FEATURE(kIPHPriceDropNTPFeature);
 BASE_DECLARE_FEATURE(kIPHQuietNotificationPromptsFeature);
+BASE_DECLARE_FEATURE(kIPHReadAloudAppMenuFeature);
 BASE_DECLARE_FEATURE(kIPHReadLaterContextMenuFeature);
 BASE_DECLARE_FEATURE(kIPHReadLaterAppMenuBookmarkThisPageFeature);
 BASE_DECLARE_FEATURE(kIPHReadLaterAppMenuBookmarksFeature);
diff --git a/components/feature_engagement/public/feature_list.cc b/components/feature_engagement/public/feature_list.cc
index a4f928d4..9c07844 100644
--- a/components/feature_engagement/public/feature_list.cc
+++ b/components/feature_engagement/public/feature_list.cc
@@ -75,6 +75,7 @@
     &kIPHPriceDropNTPFeature,
     &kIPHPwaInstallAvailableFeature,
     &kIPHQuietNotificationPromptsFeature,
+    &kIPHReadAloudAppMenuFeature,
     &kIPHReadLaterContextMenuFeature,
     &kIPHReadLaterAppMenuBookmarkThisPageFeature,
     &kIPHReadLaterAppMenuBookmarksFeature,
diff --git a/components/feature_engagement/public/feature_list.h b/components/feature_engagement/public/feature_list.h
index 0907a534..3d3f2cc 100644
--- a/components/feature_engagement/public/feature_list.h
+++ b/components/feature_engagement/public/feature_list.h
@@ -150,6 +150,8 @@
                        "IPH_PwaInstallAvailableFeature");
 DEFINE_VARIATION_PARAM(kIPHQuietNotificationPromptsFeature,
                        "IPH_QuietNotificationPrompts");
+DEFINE_VARIATION_PARAM(kIPHReadAloudAppMenuFeature,
+                       "IPH_ReadAloudAppMenuFeature");
 DEFINE_VARIATION_PARAM(kIPHReadLaterContextMenuFeature,
                        "IPH_ReadLaterContextMenu");
 DEFINE_VARIATION_PARAM(kIPHReadLaterAppMenuBookmarkThisPageFeature,
@@ -537,6 +539,7 @@
         VARIATION_ENTRY(kIPHPriceDropNTPFeature),
         VARIATION_ENTRY(kIPHPwaInstallAvailableFeature),
         VARIATION_ENTRY(kIPHQuietNotificationPromptsFeature),
+        VARIATION_ENTRY(kIPHReadAloudAppMenuFeature),
         VARIATION_ENTRY(kIPHReadLaterContextMenuFeature),
         VARIATION_ENTRY(kIPHReadLaterAppMenuBookmarkThisPageFeature),
         VARIATION_ENTRY(kIPHReadLaterAppMenuBookmarksFeature),
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index e8d8036..76b3818 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -15569,6 +15569,27 @@
             ]
         }
     ],
+    "ReadAloudAppMenuIPH": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "ReadAloudAppMenuIPHEnabled",
+                    "params": {
+                        "availability": "any",
+                        "event_trigger": "name:read_aloud_app_menu_iph_trigger;comparator:==0;window:30;storage:90",
+                        "event_used": "name:read_aloud_app_menu_pressed;comparator:==0;window:30;storage:90",
+                        "session_rate": "any"
+                    },
+                    "enable_features": [
+                        "IPH_ReadAloudAppMenuFeature"
+                    ]
+                }
+            ]
+        }
+    ],
     "ReadAloudClankStudy": [
         {
             "platforms": [
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 5982f89..f7dc472a 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -41548,6 +41548,7 @@
       label="For Android PWA install menu."/>
   <suffix name="QuietNotificationPrompts"
       label="For the quiet notification prompts feature."/>
+  <suffix name="ReadAloudAppMenuFeature" label="For ReadAloudAppMenu feature."/>
   <suffix name="ReadingListDiscovery"
       label="For ReadingListDiscoveryFeature feature."/>
   <suffix name="ReadingListEntryPoint"
diff --git a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
index a538c82..5bff6d1a 100644
--- a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
+++ b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
@@ -335,6 +335,8 @@
       summary="surfacing the Android PWA install option"/>
   <variant name="IPH_QuietNotificationPrompts"
       summary="the quiet notification prompts feature"/>
+  <variant name="IPH_ReadAloudAppMenuFeature"
+      summary="the Read Aloud button in the overflow menu"/>
   <variant name="IPH_ReadingListDiscovery"
       summary="first time a tab is added to reading list"/>
   <variant name="IPH_ReadingListEntryPoint"