Add 0-arg factory to readaloud preparing to switch to ServiceLoader (Reland)

This reverts commit c8fcb7c0c2dec593c3baa94a4dc5bb58e4bdeb9e.

Reason for reland: Fixed startup crash

Cq-Include-Trybots: luci.chrome.try:android-internal-binary-size
Bug: 40901855
Change-Id: Ic8c95fdbce66b627db3ac44474c8e19dab558f14
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5891235
Owners-Override: Andrew Grieve <agrieve@chromium.org>
Auto-Submit: Mohamed Heikal <mheikal@chromium.org>
Reviewed-by: Andrew Grieve <agrieve@chromium.org>
Commit-Queue: Andrew Grieve <agrieve@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1360292}
diff --git a/chrome/browser/readaloud/android/BUILD.gn b/chrome/browser/readaloud/android/BUILD.gn
index 0e075b13..896c62d3 100644
--- a/chrome/browser/readaloud/android/BUILD.gn
+++ b/chrome/browser/readaloud/android/BUILD.gn
@@ -23,6 +23,7 @@
     ":hooks_java",
     ":metrics_java",
     "//base:base_java",
+    "//base:service_loader_java",
     "//chrome/android:chrome_app_java_resources",
     "//chrome/android/modules/readaloud/public:java",
     "//chrome/browser/android/lifecycle:java",
@@ -160,6 +161,7 @@
 android_library("hooks_java") {
   sources = [
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudReadabilityHooks.java",
+    "java/src/org/chromium/chrome/browser/readaloud/ReadAloudReadabilityHooksFactory.java",
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudReadabilityHooksImpl.java",
   ]
   deps = [
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java
index 85fb504..d45e6db7 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java
@@ -30,6 +30,7 @@
 import org.chromium.base.ObserverList;
 import org.chromium.base.Promise;
 import org.chromium.base.ResettersForTesting;
+import org.chromium.base.ServiceLoaderUtil;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.UserData;
 import org.chromium.base.supplier.ObservableSupplier;
@@ -133,7 +134,6 @@
     private final ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
     private ReadAloudReadabilityHooks mReadabilityHooks;
 
-    @Nullable private static ReadAloudReadabilityHooks sReadabilityHooksForTesting;
     @Nullable private ReadAloudPlaybackHooks mPlaybackHooks;
     @Nullable private static ReadAloudPlaybackHooks sPlaybackHooksForTesting;
     @Nullable private Highlighter mHighlighter;
@@ -549,10 +549,13 @@
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
     public void onProfileAvailable(Profile profile) {
         TraceEvent.begin("ReadAloudController#onProfileAvailable");
-        mReadabilityHooks =
-                sReadabilityHooksForTesting != null
-                        ? sReadabilityHooksForTesting
-                        : new ReadAloudReadabilityHooksImpl(mActivity, profile);
+        ReadAloudReadabilityHooksFactory factory =
+                ServiceLoaderUtil.maybeCreate(ReadAloudReadabilityHooksFactory.class);
+        if (factory != null) {
+            mReadabilityHooks = factory.create(mActivity, profile);
+        } else {
+            mReadabilityHooks = new ReadAloudReadabilityHooksImpl(mActivity, profile);
+        }
         if (mReadabilityHooks.isEnabled()) {
             boolean isAllowed = ReadAloudFeatures.isAllowed(profile);
             ReadAloudMetrics.recordIsUserEligible(isAllowed);
@@ -1748,8 +1751,8 @@
 
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
     public static void setReadabilityHooks(ReadAloudReadabilityHooks hooks) {
-        sReadabilityHooksForTesting = hooks;
-        ResettersForTesting.register(() -> sReadabilityHooksForTesting = null);
+        ServiceLoaderUtil.setInstanceForTesting(
+                ReadAloudReadabilityHooksFactory.class, (a, b) -> hooks);
     }
 
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java
index 554298f..a58c9fc 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java
@@ -149,7 +149,7 @@
     private ObservableSupplierImpl<LayoutManager> mLayoutManagerSupplier;
     @Mock private Profile mMockProfile;
     @Mock private Profile mMockIncognitoProfile;
-    @Mock private ReadAloudReadabilityHooksImpl mHooksImpl;
+    @Mock private ReadAloudReadabilityHooks mHooksImpl;
     @Mock private ReadAloudPlaybackHooks mPlaybackHooks;
     @Mock private Player mPlayerCoordinator;
     @Mock private BottomSheetController mBottomSheetController;
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudReadabilityHooksFactory.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudReadabilityHooksFactory.java
new file mode 100644
index 0000000..55ad8a96
--- /dev/null
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudReadabilityHooksFactory.java
@@ -0,0 +1,13 @@
+// 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.content.Context;
+
+import org.chromium.chrome.browser.profiles.Profile;
+
+public interface ReadAloudReadabilityHooksFactory {
+    ReadAloudReadabilityHooks create(Context context, Profile profile);
+}