Makes spinner timing configurable by host.

PiperOrigin-RevId: 250570958
Change-Id: I9ed5ffb8b1a6bbb76ca2778c8a89a2b384140b76
diff --git a/src/main/java/com/google/android/libraries/feed/api/host/config/Configuration.java b/src/main/java/com/google/android/libraries/feed/api/host/config/Configuration.java
index 595cc1b..ff510c7 100644
--- a/src/main/java/com/google/android/libraries/feed/api/host/config/Configuration.java
+++ b/src/main/java/com/google/android/libraries/feed/api/host/config/Configuration.java
@@ -77,6 +77,12 @@
     ConfigKey.SESSION_LIFETIME_MS,
     // Boolean which if true, will ask the server for a snippet from the article.
     ConfigKey.SNIPPETS_ENABLED,
+    // Delay before a spinner should be shown after content is requested. Only used for feed wide
+    // spinners, not for more button spinners.
+    ConfigKey.SPINNER_DELAY_MS,
+    // Minimum time before a spinner should show before disappearing. Only used for feed wide
+    // spinners, not for more button spinners.
+    ConfigKey.SPINNER_MINIMUM_SHOW_TIME_MS,
     // Time in ms for the length of the timeout
     ConfigKey.TIMEOUT_TIMEOUT_MS,
     ConfigKey.TRIGGER_IMMEDIATE_PAGINATION,
@@ -127,6 +133,8 @@
     String NON_CACHED_PAGE_SIZE = "non_cached_page_size";
     String SESSION_LIFETIME_MS = "session_lifetime_ms";
     String SNIPPETS_ENABLED = "snippets_enabled";
+    String SPINNER_DELAY_MS = "spinner_delay";
+    String SPINNER_MINIMUM_SHOW_TIME_MS = "spinner_minimum_show_time";
     String TIMEOUT_TIMEOUT_MS = "timeout_timeout_ms";
     String TRIGGER_IMMEDIATE_PAGINATION = "trigger_immediate_pagination_bool";
     String UNDOABLE_ACTIONS_ENABLED = "undoable_actions_enabled";
diff --git a/src/main/java/com/google/android/libraries/feed/basicstream/BasicStream.java b/src/main/java/com/google/android/libraries/feed/basicstream/BasicStream.java
index 997bde9..32bc464 100644
--- a/src/main/java/com/google/android/libraries/feed/basicstream/BasicStream.java
+++ b/src/main/java/com/google/android/libraries/feed/basicstream/BasicStream.java
@@ -101,9 +101,9 @@
   private static final String TAG = "BasicStream";
 
   @VisibleForTesting static final String KEY_STREAM_STATE = "stream-state";
-  @VisibleForTesting static final long DEFAULT_LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS = 1000L;
-  @VisibleForTesting static final long MINIMUM_SPINNER_SHOW_TIME = 500L;
-  @VisibleForTesting static final long MINIMUM_TIME_BEFORE_SHOWING_SPINNER = 500L;
+  private static final long DEFAULT_LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS = 1000L;
+  private static final long DEFAULT_MINIMUM_SPINNER_SHOW_TIME_MS = 500L;
+  private static final long DEFAULT_SPINNER_DELAY_TIME_MS = 500L;
 
   private final CardConfiguration cardConfiguration;
   private final Clock clock;
@@ -128,6 +128,8 @@
   private final UiSessionRequestLogger uiSessionRequestLogger;
   private final StreamConfiguration streamConfiguration;
   private final BasicStreamScrollMonitor scrollMonitor;
+  private final long minimumSpinnerShowTime;
+  private final long spinnerDelayTime;
 
   private RecyclerView recyclerView;
   private ContextMenuManager contextMenuManager;
@@ -218,6 +220,11 @@
     this.context =
         new ContextThemeWrapper(context, (isBackgroundDark ? R.style.Dark : R.style.Light));
     this.scrollMonitor = new BasicStreamScrollMonitor(clock);
+    this.minimumSpinnerShowTime =
+        configuration.getValueOrDefault(
+            ConfigKey.SPINNER_MINIMUM_SHOW_TIME_MS, DEFAULT_MINIMUM_SPINNER_SHOW_TIME_MS);
+    this.spinnerDelayTime =
+        configuration.getValueOrDefault(ConfigKey.SPINNER_DELAY_MS, DEFAULT_SPINNER_DELAY_TIME_MS);
   }
 
   @VisibleForTesting
@@ -638,7 +645,7 @@
     if (isInitialLoad && initialLoadingSpinnerStartTime != 0L) {
       long spinnerDisplayTime = clock.currentTimeMillis() - initialLoadingSpinnerStartTime;
       // If MINIMUM_SPINNER_SHOW_TIME has elapsed, the new content can be shown immediately.
-      if (spinnerDisplayTime >= MINIMUM_SPINNER_SHOW_TIME) {
+      if (spinnerDisplayTime >= minimumSpinnerShowTime) {
         updateAdapterAfterSessionStart(localModelProvider);
       } else {
         // If MINIMUM_SPINNER_SHOW_TIME has not elapsed, the new content should only be shown once
@@ -652,7 +659,7 @@
                 updateAdapterAfterSessionStart(localModelProvider);
               }
             },
-            MINIMUM_SPINNER_SHOW_TIME - spinnerDisplayTime);
+            minimumSpinnerShowTime - spinnerDisplayTime);
       }
     } else {
       updateAdapterAfterSessionStart(localModelProvider);
@@ -800,7 +807,7 @@
   private void showSpinnerWithDelay() {
     cancellableShowSpinnerRunnable =
         mainThreadRunner.executeWithDelay(
-            TAG + " onShow", new ShowSpinnerRunnable(), MINIMUM_TIME_BEFORE_SHOWING_SPINNER);
+            TAG + " onShow", new ShowSpinnerRunnable(), spinnerDelayTime);
   }
 
   private String convertStreamSavedInstanceStateToString(
diff --git a/src/test/java/com/google/android/libraries/feed/basicstream/BasicStreamTest.java b/src/test/java/com/google/android/libraries/feed/basicstream/BasicStreamTest.java
index 9a896c4..d75057b 100644
--- a/src/test/java/com/google/android/libraries/feed/basicstream/BasicStreamTest.java
+++ b/src/test/java/com/google/android/libraries/feed/basicstream/BasicStreamTest.java
@@ -16,8 +16,6 @@
 
 import static com.google.android.libraries.feed.api.client.stream.Stream.POSITION_NOT_KNOWN;
 import static com.google.android.libraries.feed.basicstream.BasicStream.KEY_STREAM_STATE;
-import static com.google.android.libraries.feed.basicstream.BasicStream.MINIMUM_SPINNER_SHOW_TIME;
-import static com.google.android.libraries.feed.basicstream.BasicStream.MINIMUM_TIME_BEFORE_SHOWING_SPINNER;
 import static com.google.android.libraries.feed.common.testing.RunnableSubject.assertThatRunnable;
 import static com.google.common.truth.Truth.assertThat;
 import static java.nio.charset.StandardCharsets.UTF_8;
@@ -134,11 +132,15 @@
           .setSessionId(SESSION_ID)
           .setScrollState(SCROLL_STATE)
           .build();
+  private static final long SPINNER_DELAY_MS = 123L;
+  private static final long SPINNER_MINIMUM_SHOW_TIME_MS = 655L;
   private static final Configuration CONFIGURATION =
       new Configuration.Builder()
           .put(
               ConfigKey.LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS,
               LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS)
+          .put(ConfigKey.SPINNER_DELAY_MS, SPINNER_DELAY_MS)
+          .put(ConfigKey.SPINNER_MINIMUM_SHOW_TIME_MS, SPINNER_MINIMUM_SHOW_TIME_MS)
           .build();
 
   @Mock private StreamConfiguration streamConfiguration;
@@ -340,7 +342,7 @@
     basicStream.onShow();
 
     // Advance so that the spinner starts showing
-    clock.advance(MINIMUM_TIME_BEFORE_SHOWING_SPINNER);
+    clock.advance(SPINNER_DELAY_MS);
 
     // Advance so that is has taken long enough that onOpenedWithNoImmediateContent is logged.
     clock.advance(LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS);
@@ -632,7 +634,7 @@
 
     verify(streamDriver, never()).showSpinner();
 
-    clock.advance(MINIMUM_TIME_BEFORE_SHOWING_SPINNER);
+    clock.advance(SPINNER_DELAY_MS);
 
     verify(streamDriver).showSpinner();
   }
@@ -1004,10 +1006,10 @@
     reset(adapter);
 
     // Advance so the spinner is shown.
-    clock.advance(MINIMUM_TIME_BEFORE_SHOWING_SPINNER);
+    clock.advance(SPINNER_DELAY_MS);
 
     // Advance so that it has shown long enough
-    clock.advance(MINIMUM_SPINNER_SHOW_TIME);
+    clock.advance(SPINNER_MINIMUM_SHOW_TIME_MS);
 
     basicStream.onSessionStart(UiContext.getDefaultInstance());
 
@@ -1020,7 +1022,7 @@
     basicStream.onShow();
 
     // Advance so the spinner is shown.
-    clock.advance(MINIMUM_TIME_BEFORE_SHOWING_SPINNER);
+    clock.advance(SPINNER_DELAY_MS);
 
     reset(adapter);
 
@@ -1028,7 +1030,7 @@
     verify(adapter, never()).setDriver(any(StreamDriver.class));
 
     // Advance so that it has shown long enough.
-    clock.advance(MINIMUM_SPINNER_SHOW_TIME);
+    clock.advance(SPINNER_MINIMUM_SHOW_TIME_MS);
 
     verify(adapter).setDriver(streamDriver);
   }
@@ -1042,13 +1044,13 @@
     reset(adapter);
 
     // Advance so the spinner is shown.
-    clock.advance(MINIMUM_TIME_BEFORE_SHOWING_SPINNER);
+    clock.advance(SPINNER_DELAY_MS);
 
     basicStream.onSessionStart(UiContext.getDefaultInstance());
     basicStream.onSessionFinished(UiContext.getDefaultInstance());
 
     // Advance so that it has shown long enough.
-    clock.advance(MINIMUM_SPINNER_SHOW_TIME);
+    clock.advance(SPINNER_MINIMUM_SHOW_TIME_MS);
 
     verify(adapter, never()).setDriver(streamDriver);
   }
@@ -1070,7 +1072,20 @@
     verify(streamDriver, never()).showSpinner();
 
     // Advance so the spinner is shown.
-    clock.advance(MINIMUM_TIME_BEFORE_SHOWING_SPINNER);
+    clock.advance(SPINNER_DELAY_MS);
+
+    verify(streamDriver).showSpinner();
+  }
+
+  @Test
+  public void testOnShow_delaysShowingZeroState_configured_delay_time() {
+    basicStream.onShow();
+
+    verify(streamDriver, never()).showZeroState(/* zeroStateShowReason= */ anyInt());
+    verify(streamDriver, never()).showSpinner();
+
+    // Advance so the spinner is shown.
+    clock.advance(SPINNER_DELAY_MS);
 
     verify(streamDriver).showSpinner();
   }
@@ -1164,7 +1179,7 @@
 
     basicStream.onSessionFinished(UiContext.getDefaultInstance());
 
-    clock.advance(BasicStream.MINIMUM_TIME_BEFORE_SHOWING_SPINNER);
+    clock.advance(SPINNER_DELAY_MS);
 
     verify(streamDriver).showSpinner();
   }
@@ -1174,7 +1189,7 @@
     basicStream.onShow();
 
     // Advance, but not enough so that the spinner shows.
-    clock.advance(MINIMUM_TIME_BEFORE_SHOWING_SPINNER / 2);
+    clock.advance(SPINNER_DELAY_MS / 2);
 
     // Start the session. At this point, the spinner enqueued by onShow() should never be shown.
     basicStream.onSessionStart(UiContext.getDefaultInstance());
@@ -1185,7 +1200,7 @@
 
     // It has now been long enough since onShow that the spinner would show if onSessionStart()
     // hadn't been called first.
-    clock.advance(BasicStream.MINIMUM_TIME_BEFORE_SHOWING_SPINNER / 2);
+    clock.advance(SPINNER_DELAY_MS / 2 + 1);
 
     verify(streamDriver, never()).showSpinner();
   }
@@ -1199,7 +1214,7 @@
 
     verify(streamDriver, never()).showSpinner();
 
-    clock.advance(MINIMUM_TIME_BEFORE_SHOWING_SPINNER);
+    clock.advance(SPINNER_DELAY_MS);
 
     verify(streamDriver).showSpinner();
   }