Merge "[API review] Rename ScreenFlashUiCompleter to ScreenFlashListener and clarify docs" into androidx-main
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CapturePipeline.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CapturePipeline.java
index 6d8869e..93dbd14 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CapturePipeline.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CapturePipeline.java
@@ -681,23 +681,14 @@
         public ListenableFuture<Boolean> preCapture(@Nullable TotalCaptureResult captureResult) {
             Logger.d(TAG, "ScreenFlashTask#preCapture");
 
-            AtomicReference<ImageCapture.ScreenFlashUiCompleter> screenFlashUiCompleter =
+            AtomicReference<ImageCapture.ScreenFlashListener> screenFlashListener =
                     new AtomicReference<>();
 
             ListenableFuture<Void> uiAppliedFuture = CallbackToFutureAdapter.getFuture(
                     completer -> {
-                        screenFlashUiCompleter.set(new ImageCapture.ScreenFlashUiCompleter() {
-                            @Override
-                            public void complete() {
-                                Logger.d(TAG, "ScreenFlashTask#preCapture: UI change applied");
-                                completer.set(null);
-                            }
-
-                            @Override
-                            public long getExpirationTimeMillis() {
-                                return System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(
-                                        ImageCapture.SCREEN_FLASH_UI_APPLY_TIMEOUT_SECONDS);
-                            }
+                        screenFlashListener.set(() -> {
+                            Logger.d(TAG, "ScreenFlashTask#preCapture: UI change applied");
+                            completer.set(null);
                         });
                         return "OnScreenFlashUiApplied";
                     });
@@ -705,7 +696,9 @@
             ListenableFuture<Void> future = CallbackToFutureAdapter.getFuture(completer -> {
                 CameraXExecutors.mainThreadExecutor().execute(() -> {
                     Logger.d(TAG, "ScreenFlashTask#preCapture: invoking applyScreenFlashUi");
-                    mScreenFlash.apply(screenFlashUiCompleter.get());
+                    mScreenFlash.apply(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(
+                                    ImageCapture.SCREEN_FLASH_UI_APPLY_TIMEOUT_SECONDS),
+                            screenFlashListener.get());
                     completer.set(null);
                 });
                 return "OnScreenFlashStart";
@@ -730,8 +723,8 @@
                     mExecutor
             ).transformAsync(
                     input -> Futures.makeTimeoutFuture(
-                            // Not using ScreenFlashUiCompleter#getExpirationTimeMillis here gives
-                            // users a bit more grace time before CameraX stops waiting.
+                            // Not using the previous timestamp here gives users a bit more grace
+                            // time before CameraX stops waiting.
                             TimeUnit.SECONDS.toMillis(
                                     ImageCapture.SCREEN_FLASH_UI_APPLY_TIMEOUT_SECONDS),
                             mScheduler, null, true, uiAppliedFuture),
diff --git a/camera/camera-core/api/current.txt b/camera/camera-core/api/current.txt
index 85c40da..90f8e1c 100644
--- a/camera/camera-core/api/current.txt
+++ b/camera/camera-core/api/current.txt
@@ -378,13 +378,12 @@
   }
 
   public static interface ImageCapture.ScreenFlash {
-    method @UiThread public void apply(androidx.camera.core.ImageCapture.ScreenFlashUiCompleter);
+    method @UiThread public void apply(long, androidx.camera.core.ImageCapture.ScreenFlashListener);
     method @UiThread public void clear();
   }
 
-  public static interface ImageCapture.ScreenFlashUiCompleter {
-    method public void complete();
-    method public long getExpirationTimeMillis();
+  public static interface ImageCapture.ScreenFlashListener {
+    method public void onCompleted();
   }
 
   public interface ImageCaptureCapabilities {
diff --git a/camera/camera-core/api/restricted_current.txt b/camera/camera-core/api/restricted_current.txt
index 85c40da..90f8e1c 100644
--- a/camera/camera-core/api/restricted_current.txt
+++ b/camera/camera-core/api/restricted_current.txt
@@ -378,13 +378,12 @@
   }
 
   public static interface ImageCapture.ScreenFlash {
-    method @UiThread public void apply(androidx.camera.core.ImageCapture.ScreenFlashUiCompleter);
+    method @UiThread public void apply(long, androidx.camera.core.ImageCapture.ScreenFlashListener);
     method @UiThread public void clear();
   }
 
-  public static interface ImageCapture.ScreenFlashUiCompleter {
-    method public void complete();
-    method public long getExpirationTimeMillis();
+  public static interface ImageCapture.ScreenFlashListener {
+    method public void onCompleted();
   }
 
   public interface ImageCaptureCapabilities {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
index e06e4e1..2176913 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
@@ -251,10 +251,11 @@
      * <pre>{@code
      * imageCapture.setScreenFlash(new ImageCapture.ScreenFlash() {
      *     @Override
-     *     public void apply(@NonNull ScreenFlashUiCompleter screenFlashUiCompleter) {
+     *     public void apply(long expirationTimeMillis,
+     *             @NonNull ScreenFlashListener screenFlashListener) {
      *         whiteColorOverlayView.setVisibility(View.VISIBLE);
      *         maximizeScreenBrightness();
-     *         screenFlashUiCompleter.complete();
+     *         screenFlashListener.onCompleted();
      *     }
      *
      *     @Override
@@ -1672,28 +1673,22 @@
     }
 
     /**
-     * Interface to inform if application UI change has been completed for a screen flash image
-     * capture.
+     * Callback listener for discovering when the application has completed its changes for a
+     * screen flash image capture.
+     *
+     * <p> For example, an application may change its UI to a full white screen with maximum
+     * brightness for a proper screen flash capture.
+     *
+     * @see ScreenFlash#apply(long, ScreenFlashListener)
      */
-    public interface ScreenFlashUiCompleter {
+    public interface ScreenFlashListener {
         /**
-         * Completes this {@link ScreenFlashUiCompleter} instance so that CameraX is no
-         * longer waiting.
+         * Invoked by the application when it has completed its changes due to a screen flash
+         * image capture.
          *
          * @see ScreenFlash#apply
          */
-        void complete();
-
-        /**
-         * Gets the timestamp after which CameraX will no longer be waiting.
-         *
-         * <p>The timestamp is based on {@link System#currentTimeMillis()}. It is at least
-         * 3 seconds later from the start of a screen flash image capture operation. Since
-         * CameraX will no longer wait for the UI change to be completed after this timestamp,
-         * users shouldn't be doing any screen flash related UI change that may go past this
-         * timestamp.
-         */
-        long getExpirationTimeMillis();
+        void onCompleted();
     }
 
 
@@ -1708,37 +1703,48 @@
         /**
          * Applies the necessary application changes for a screen flash photo capture.
          *
-         * <p>CameraX will invoke this method when the application UI needs to be changed for a
-         * successful photo capture with screen flash feature. When this API is invoked, the
-         * application UI should utilize the screen to provide extra light as an alternative to
-         * physical flash. For example, the screen brightness can be maximized and screen color
-         * can be covered with some bright color like white.
+         * <p>When the application UI needs to be changed for a successful photo capture with
+         * screen flash feature, CameraX will invoke this method and wait for the application to
+         * complete its changes. When this API is invoked, the application UI should utilize the
+         * screen to provide extra light as an alternative to physical flash. For example, the
+         * screen brightness can be maximized and screen color can be covered with some bright
+         * color like white.
          *
-         * <p>Until the timestamp of {@link ScreenFlashUiCompleter#getExpirationTimeMillis()},
-         * CameraX will wait for the provided {@link ScreenFlashUiCompleter} argument to be
-         * completed before starting any operation that is dependent on the UI change.
-         * Applications must call {@link ScreenFlashUiCompleter#complete()} after their UI
-         * changes are done so that CameraX is not unnecessarily waiting. If the application does
-         * not call {@code ScreenFlashUiCompleter#complete} before the provided timestamp,
-         * CameraX will stop waiting and move forward with the subsequent operations regardless.
-         * In such case, it is the application's responsibility to clear any UI change done after
-         * {@link #clear} has been invoked.
+         * <p>The parameter {@code expirationTimeMillis} is based on
+         * {@link System#currentTimeMillis()}. It is at least 3 seconds later from the start of a
+         * screen flash image capture operation. Until the timestamp of {@code expirationTimeMillis}
+         * parameter, CameraX will wait for the application to notify the completion of the
+         * application-side changes using the {@link ScreenFlashListener} parameter of this
+         * method. Applications must call {@link ScreenFlashListener#onCompleted()} after their
+         * UI changes are done so that CameraX is not unnecessarily waiting. If the application
+         * does not call {@code ScreenFlashListener#onCompleted} before {@code
+         * expirationTimeMillis}, CameraX will stop waiting and move forward with the subsequent
+         * operations regardless. In such case, the application no longer needs to call {@code
+         * ScreenFlashListener#onCompleted()}. If {@link #clear} has also been invoked while the
+         * application is still doing the changes, it is the application's responsibility to
+         * clear any UI change done after {@link #clear} has been invoked.
          *
          * <p>The following code snippet shows an example implementation of this API.
          * <pre>{@code
          * @Override
-         * public void apply(@NonNull ScreenFlashUiCompleter screenFlashUiCompleter) {
+         * public void apply(long expirationTimeMillis,
+         *         @NonNull ScreenFlashListener screenFlashListener) {
          *     // Enable top overlay to make screen color white
          *     whiteColorOverlay.setVisible(true);
          *     // Maximize screen brightness
          *     maximizeScreenBrightness();
-         *     screenFlashUiCompleter.complete();
+         *     screenFlashListener.onCompleted();
          * }}</pre>
          *
-         * @param screenFlashUiCompleter Used to notify when UI changes have been applied.
+         * @param expirationTimeMillis The timestamp after which CameraX will no longer listen
+         *                             to {@code screenFlashListener}.
+         * @param screenFlashListener  Used to notify when UI changes have been applied.
          */
+        // ExecutorRegistration lint suppressed since this is called by app and CameraX supports
+        // receiving the call on any thread. Adding executor will make it harder for apps.
+        @SuppressWarnings("ExecutorRegistration")
         @UiThread
-        void apply(@NonNull ScreenFlashUiCompleter screenFlashUiCompleter);
+        void apply(long expirationTimeMillis, @NonNull ScreenFlashListener screenFlashListener);
 
         /**
          * Clears any application change done for screen flash operation, if required.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/ScreenFlashWrapper.kt b/camera/camera-core/src/main/java/androidx/camera/core/internal/ScreenFlashWrapper.kt
index 118f945..3241d1a 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/ScreenFlashWrapper.kt
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/ScreenFlashWrapper.kt
@@ -19,11 +19,11 @@
 import androidx.annotation.GuardedBy
 import androidx.annotation.RequiresApi
 import androidx.camera.core.ImageCapture.ScreenFlash
-import androidx.camera.core.ImageCapture.ScreenFlashUiCompleter
+import androidx.camera.core.ImageCapture.ScreenFlashListener
 import androidx.camera.core.Logger
 
 /**
- * Wrapper class around [ScreenFlash] to save the [ScreenFlashUiCompleter] passed to app.
+ * Wrapper class around [ScreenFlash] to save the [ScreenFlashListener] passed to app.
  *
  * This allows us to clean up properly in case a capture is cancelled earlier (e.g. ImageCapture is
  * unbound after [apply] is invoked but [clear] is not).
@@ -37,37 +37,32 @@
     @GuardedBy("lock")
     private var isClearScreenFlashPending: Boolean = false
     @GuardedBy("lock")
-    private var pendingCompleter: ScreenFlashUiCompleter? = null
+    private var pendingListener: ScreenFlashListener? = null
 
     companion object {
         private const val TAG = "ScreenFlashWrapper"
 
         @JvmStatic
-        fun from(screenFlash: ScreenFlash?) =
-            ScreenFlashWrapper(screenFlash)
+        fun from(screenFlash: ScreenFlash?) = ScreenFlashWrapper(screenFlash)
     }
 
-    override fun apply(screenFlashUiCompleter: ScreenFlashUiCompleter) {
+    override fun apply(expirationTimeMillis: Long, screenFlashListener: ScreenFlashListener) {
         synchronized(lock) {
             isClearScreenFlashPending = true
-            pendingCompleter = screenFlashUiCompleter
+            pendingListener = screenFlashListener
         }
 
-        screenFlash?.apply(object : ScreenFlashUiCompleter {
-            override fun complete() {
-                synchronized(lock) {
-                    if (pendingCompleter == null) {
-                        Logger.w(TAG, "apply: pendingCompleter is null!")
-                    }
-                    completePendingScreenFlashUiCompleter()
+        screenFlash?.apply(expirationTimeMillis) {
+            synchronized(lock) {
+                if (pendingListener == null) {
+                    Logger.w(TAG, "apply: pendingListener is null!")
                 }
+                completePendingScreenFlashListener()
             }
-
-            override fun getExpirationTimeMillis() = screenFlashUiCompleter.expirationTimeMillis
-        }) ?: run {
+        } ?: run {
             Logger.e(TAG, "apply: screenFlash is null!")
             // Complete immediately in case this error case is invoked by some bug
-            completePendingScreenFlashUiCompleter()
+            completePendingScreenFlashListener()
         }
     }
 
@@ -81,12 +76,12 @@
     fun getBaseScreenFlash(): ScreenFlash? = screenFlash
 
     /**
-     * Completes the pending [ScreenFlashUiCompleter], if any.
+     * Completes the pending [ScreenFlashListener], if any.
      */
-    private fun completePendingScreenFlashUiCompleter() {
+    private fun completePendingScreenFlashListener() {
         synchronized(lock) {
-            pendingCompleter?.complete()
-            pendingCompleter = null
+            pendingListener?.onCompleted()
+            pendingListener = null
         }
     }
 
@@ -110,7 +105,7 @@
      * Completes all pending operations.
      */
     fun completePendingTasks() {
-        completePendingScreenFlashUiCompleter()
+        completePendingScreenFlashListener()
         completePendingScreenFlashClear()
     }
 }
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
index 758471f..30155a3 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
@@ -60,7 +60,7 @@
 import androidx.camera.testing.impl.fakes.FakeImageReaderProxy
 import androidx.camera.testing.impl.fakes.FakeSessionProcessor
 import androidx.camera.testing.impl.mocks.MockScreenFlash
-import androidx.camera.testing.impl.mocks.MockScreenFlashUiCompleter
+import androidx.camera.testing.impl.mocks.MockScreenFlashListener
 import androidx.test.core.app.ApplicationProvider
 import com.google.common.truth.Truth.assertThat
 import java.io.File
@@ -629,7 +629,8 @@
         imageCapture.screenFlash = MockScreenFlash()
 
         (cameraFront.cameraControl as FakeCameraControl).screenFlash?.apply(
-            MockScreenFlashUiCompleter()
+            0L,
+            MockScreenFlashListener()
         )
         imageCapture.unbindFromCamera(cameraFront)
 
@@ -646,7 +647,8 @@
         imageCapture.screenFlash = MockScreenFlash()
 
         (cameraFront.cameraControl as FakeCameraControl).screenFlash?.apply(
-            MockScreenFlashUiCompleter()
+            0L,
+            MockScreenFlashListener()
         )
         (cameraFront.cameraControl as FakeCameraControl).screenFlash?.clear()
         imageCapture.unbindFromCamera(cameraFront)
@@ -656,8 +658,8 @@
     }
 
     @Test
-    fun cameraControlScreenFlashUiCompleterCompleted_whenImageCaptureCompleterIsCompleted() {
-        val completer = MockScreenFlashUiCompleter()
+    fun cameraControlScreenFlashListenerCompleted_whenImageCaptureListenerIsCompleted() {
+        val listener = MockScreenFlashListener()
         val imageCapture = bindImageCapture(
             cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA,
             imageReaderProxyProvider = getFakeImageReaderProxyProvider(),
@@ -666,16 +668,16 @@
             setApplyCompletedInstantly(false)
         }
 
-        (cameraFront.cameraControl as FakeCameraControl).screenFlash?.apply(completer)
-        (imageCapture.screenFlash as MockScreenFlash).lastApplyCompleter?.complete()
+        (cameraFront.cameraControl as FakeCameraControl).screenFlash?.apply(0L, listener)
+        (imageCapture.screenFlash as MockScreenFlash).lastApplyListener?.onCompleted()
 
-        completer.awaitComplete(3000)
-        assertThat(completer.getCompleteCount()).isEqualTo(1)
+        listener.awaitComplete(3000)
+        assertThat(listener.getCompleteCount()).isEqualTo(1)
     }
 
     @Test
-    fun imageCaptureUnboundWithoutCompletion_cameraControlScreenFlashUiCompleterCompleted() {
-        val completer = MockScreenFlashUiCompleter()
+    fun imageCaptureUnboundWithoutCompletion_cameraControlScreenFlashListenerCompleted() {
+        val listener = MockScreenFlashListener()
         val imageCapture = bindImageCapture(
             cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA,
             imageReaderProxyProvider = getFakeImageReaderProxyProvider(),
@@ -684,16 +686,16 @@
             setApplyCompletedInstantly(false)
         }
 
-        (cameraFront.cameraControl as FakeCameraControl).screenFlash?.apply(completer)
+        (cameraFront.cameraControl as FakeCameraControl).screenFlash?.apply(0L, listener)
         imageCapture.unbindFromCamera(cameraFront)
 
-        completer.awaitComplete(3000)
-        assertThat(completer.getCompleteCount()).isEqualTo(1)
+        listener.awaitComplete(3000)
+        assertThat(listener.getCompleteCount()).isEqualTo(1)
     }
 
     @Test
-    fun imageCaptureUnboundAndCompleterCompleted_cameraControlCompleterCompletedOnlyOnce() {
-        val completer = MockScreenFlashUiCompleter()
+    fun imageCaptureUnboundAndListenerCompleted_cameraControlListenerCompletedOnlyOnce() {
+        val listener = MockScreenFlashListener()
         val imageCapture = bindImageCapture(
             cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA,
             imageReaderProxyProvider = getFakeImageReaderProxyProvider(),
@@ -702,12 +704,12 @@
             setApplyCompletedInstantly(false)
         }
 
-        (cameraFront.cameraControl as FakeCameraControl).screenFlash?.apply(completer)
+        (cameraFront.cameraControl as FakeCameraControl).screenFlash?.apply(0L, listener)
         imageCapture.unbindFromCamera(cameraFront)
-        (imageCapture.screenFlash as MockScreenFlash).lastApplyCompleter?.complete()
+        (imageCapture.screenFlash as MockScreenFlash).lastApplyListener?.onCompleted()
 
-        completer.awaitComplete(3000)
-        assertThat(completer.getCompleteCount()).isEqualTo(1)
+        listener.awaitComplete(3000)
+        assertThat(listener.getCompleteCount()).isEqualTo(1)
     }
 
     @Test
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockScreenFlash.java b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockScreenFlash.java
index 1ec0d11..59bfb0c 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockScreenFlash.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockScreenFlash.java
@@ -21,8 +21,9 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
+import androidx.camera.core.ImageCapture;
 import androidx.camera.core.ImageCapture.ScreenFlash;
-import androidx.camera.core.ImageCapture.ScreenFlashUiCompleter;
+import androidx.camera.core.ImageCapture.ScreenFlashListener;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
@@ -39,7 +40,7 @@
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 public class MockScreenFlash implements ScreenFlash {
     /**
-     * Represents {@link ScreenFlash#apply(ScreenFlashUiCompleter)} event.
+     * Represents {@link ImageCapture.ScreenFlash#apply} event.
      */
     public static final int APPLY = 0;
     /**
@@ -64,7 +65,7 @@
 
     @GuardedBy("mLock")
     @Nullable
-    private ScreenFlashUiCompleter mLastApplyCompleter;
+    private ScreenFlashListener mLastApplyListener;
 
     /**
      * Returns a list of {@link ScreenFlashEvent} in the same order as invoked.
@@ -91,33 +92,34 @@
     }
 
     /**
-     * Enables or disables the {@link ScreenFlashUiCompleter} being completed instantly when
-     * {@link ScreenFlash#apply(ScreenFlashUiCompleter)} is invoked.
+     * Enables or disables the {@link ScreenFlashListener} being completed instantly when
+     * {@link ScreenFlash#apply(long, ScreenFlashListener)} is invoked.
      */
     public void setApplyCompletedInstantly(boolean completedInstantly) {
         mIsApplyCompletedInstantly = completedInstantly;
     }
 
     /**
-     * Gets the {@link ScreenFlashUiCompleter} instance of the last
-     * {@link ScreenFlash#apply(ScreenFlashUiCompleter)} invocation, or null in case of no
+     * Gets the {@link ScreenFlashListener} instance of the last
+     * {@link ScreenFlash#apply(long, ScreenFlashListener)} invocation, or null in case of no
      * invocation.
      */
     @Nullable
-    public ScreenFlashUiCompleter getLastApplyCompleter() {
+    public ScreenFlashListener getLastApplyListener() {
         synchronized (mLock) {
-            return mLastApplyCompleter;
+            return mLastApplyListener;
         }
     }
 
     /** {@inheritDoc} */
     @Override
-    public void apply(@NonNull ScreenFlashUiCompleter screenFlashUiCompleter) {
+    public void apply(long expirationTimeMillis,
+            @NonNull ScreenFlashListener screenFlashListener) {
         synchronized (mLock) {
             mEventList.add(APPLY);
-            mLastApplyCompleter = screenFlashUiCompleter;
+            mLastApplyListener = screenFlashListener;
             if (mIsApplyCompletedInstantly) {
-                screenFlashUiCompleter.complete();
+                screenFlashListener.onCompleted();
             }
         }
     }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockScreenFlashUiCompleter.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockScreenFlashListener.kt
similarity index 74%
rename from camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockScreenFlashUiCompleter.kt
rename to camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockScreenFlashListener.kt
index ef11bca..8bf696b 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockScreenFlashUiCompleter.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockScreenFlashListener.kt
@@ -17,39 +17,37 @@
 package androidx.camera.testing.impl.mocks
 
 import androidx.annotation.GuardedBy
-import androidx.camera.core.ImageCapture.ScreenFlashUiCompleter
+import androidx.camera.core.ImageCapture.ScreenFlashListener
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
 
 /**
- * A mock implementations of [ScreenFlashUiCompleter] for testing purpose.
+ * A mock implementations of [ScreenFlashListener] for testing purpose.
  */
-class MockScreenFlashUiCompleter : ScreenFlashUiCompleter {
+class MockScreenFlashListener : ScreenFlashListener {
     private val lock = Object()
 
     @GuardedBy("lock")
     private var completeCount: Int = 0
     private val completeLatch = CountDownLatch(1)
 
-    override fun complete() {
+    override fun onCompleted() {
         synchronized(lock) {
             completeCount++
         }
         completeLatch.countDown()
     }
 
-    override fun getExpirationTimeMillis() = 0L
-
     /**
-     * Gets the number of times [complete] was invoked.
+     * Gets the number of times [onCompleted] was invoked.
      */
     fun getCompleteCount() = synchronized(lock) { completeCount }
 
     /**
-     * Waits for [complete] to be invoked once.
+     * Waits for [onCompleted] to be invoked once.
      *
      * @param timeoutInMillis The timeout of waiting in milliseconds.
-     * @return True if [complete] was invoked, false if timed out.
+     * @return True if [onCompleted] was invoked, false if timed out.
      */
     fun awaitComplete(timeoutInMillis: Long): Boolean {
         return completeLatch.await(timeoutInMillis, TimeUnit.MILLISECONDS)
diff --git a/camera/camera-testing/src/test/java/androidx/camera/testing/mocks/MockScreenFlashTest.kt b/camera/camera-testing/src/test/java/androidx/camera/testing/mocks/MockScreenFlashTest.kt
index 1110a44..c951560 100644
--- a/camera/camera-testing/src/test/java/androidx/camera/testing/mocks/MockScreenFlashTest.kt
+++ b/camera/camera-testing/src/test/java/androidx/camera/testing/mocks/MockScreenFlashTest.kt
@@ -18,11 +18,13 @@
 
 import android.annotation.SuppressLint
 import android.os.Build
-import androidx.camera.core.ImageCapture.ScreenFlashUiCompleter
+import androidx.camera.core.ImageCapture
+import androidx.camera.core.ImageCapture.ScreenFlashListener
 import androidx.camera.testing.impl.mocks.MockScreenFlash
 import androidx.camera.testing.impl.mocks.MockScreenFlash.APPLY
 import androidx.camera.testing.impl.mocks.MockScreenFlash.CLEAR
 import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.TimeUnit
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -34,14 +36,8 @@
 @DoNotInstrument
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 class MockScreenFlashTest {
-    private val dummyCompleter = object : ScreenFlashUiCompleter {
-        override fun complete() {
-            // no-op
-        }
-
-        override fun getExpirationTimeMillis(): Long {
-            return 0
-        }
+    private val dummyListener = ScreenFlashListener {
+        // no-op
     }
 
     private lateinit var mMockScreenFlash: MockScreenFlash
@@ -54,7 +50,12 @@
     @Test
     fun getScreenFlashEvents_invocationsRecordedExactlyInSameOrder() {
         mMockScreenFlash.clear()
-        mMockScreenFlash.apply(dummyCompleter)
+        mMockScreenFlash.apply(
+            System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(
+                ImageCapture.SCREEN_FLASH_UI_APPLY_TIMEOUT_SECONDS
+            ),
+            dummyListener,
+        )
         mMockScreenFlash.clear()
 
         assertThat(mMockScreenFlash.screenFlashEvents).isEqualTo(listOf(
@@ -67,16 +68,13 @@
     @Test
     fun awaitApply_listenerCompletedAutomaticallyByDefault() {
         var isCompleted = false
-        val completer = object : ScreenFlashUiCompleter {
-            override fun complete() {
-                isCompleted = true
-            }
-
-            override fun getExpirationTimeMillis(): Long {
-                return 0
-            }
-        }
-        mMockScreenFlash.apply(completer)
+        val listener = ScreenFlashListener { isCompleted = true }
+        mMockScreenFlash.apply(
+            System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(
+                ImageCapture.SCREEN_FLASH_UI_APPLY_TIMEOUT_SECONDS
+            ),
+            listener,
+        )
 
         assertThat(isCompleted).isTrue()
     }
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java b/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
index 45b27db..5a54f82 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
@@ -223,9 +223,9 @@
     private static final ScreenFlash NO_OP_SCREEN_FLASH =
             new ScreenFlash() {
                 @Override
-                public void apply(
-                        @NonNull ImageCapture.ScreenFlashUiCompleter screenFlashUiCompleter) {
-                    screenFlashUiCompleter.complete();
+                public void apply(long expirationTimeMillis,
+                        @NonNull ImageCapture.ScreenFlashListener screenFlashListener) {
+                    screenFlashListener.onCompleted();
                 }
 
                 @Override
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/ScreenFlashView.java b/camera/camera-view/src/main/java/androidx/camera/view/ScreenFlashView.java
index 5798558..6128f0f 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/ScreenFlashView.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/ScreenFlashView.java
@@ -174,8 +174,8 @@
                 private float mPreviousBrightness;
 
                 @Override
-                public void apply(
-                        @NonNull ImageCapture.ScreenFlashUiCompleter screenFlashUiCompleter) {
+                public void apply(long expirationTimeMillis,
+                        @NonNull ImageCapture.ScreenFlashListener screenFlashListener) {
                     Logger.d(TAG, "ScreenFlash#apply");
 
                     setAlpha(1f);
@@ -186,7 +186,7 @@
                     layoutParam.screenBrightness = 1F;
                     mScreenFlashWindow.setAttributes(layoutParam);
 
-                    screenFlashUiCompleter.complete();
+                    screenFlashListener.onCompleted();
                 }
 
                 @Override
@@ -208,7 +208,7 @@
      * Returns an {@link ScreenFlash} implementation based on the {@link Window} instance
      * set via {@link #setScreenFlashWindow(Window)}.
      *
-     * <p> When {@link ScreenFlash#apply(ImageCapture.ScreenFlashUiCompleter)} is invoked,
+     * <p> When {@link ScreenFlash#apply(long, ImageCapture.ScreenFlashListener)} is invoked,
      * this view becomes fully visible and screen brightness is maximized using the provided
      * {@code Window}. The default color of the overlay view is {@link Color#WHITE}. To change
      * the color, use {@link #setBackgroundColor(int)}.
diff --git a/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt b/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt
index 2a6c395..19a2e7d 100644
--- a/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt
+++ b/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt
@@ -546,9 +546,10 @@
                 ScreenFlashUiInfo.ProviderType.SCREEN_FLASH_VIEW,
                 object : ScreenFlash {
                     override fun apply(
-                        screenFlashUiCompleter: ImageCapture.ScreenFlashUiCompleter
+                        expirationTimeMillis: Long,
+                        screenFlashListener: ImageCapture.ScreenFlashListener,
                     ) {
-                        screenFlashUiCompleter.complete()
+                        screenFlashListener.onCompleted()
                     }
 
                     override fun clear() {
diff --git a/camera/camera-view/src/test/java/androidx/camera/view/ScreenFlashViewTest.kt b/camera/camera-view/src/test/java/androidx/camera/view/ScreenFlashViewTest.kt
index 744ef07..569b64f 100644
--- a/camera/camera-view/src/test/java/androidx/camera/view/ScreenFlashViewTest.kt
+++ b/camera/camera-view/src/test/java/androidx/camera/view/ScreenFlashViewTest.kt
@@ -22,9 +22,10 @@
 import androidx.camera.core.CameraSelector
 import androidx.camera.core.ImageCapture
 import androidx.camera.core.ImageCapture.ScreenFlash
-import androidx.camera.core.ImageCapture.ScreenFlashUiCompleter
+import androidx.camera.core.ImageCapture.ScreenFlashListener
 import androidx.test.core.app.ApplicationProvider
 import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.TimeUnit
 import org.junit.Assert
 import org.junit.Assume
 import org.junit.Before
@@ -39,14 +40,8 @@
 @DoNotInstrument
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 class ScreenFlashViewTest {
-    private val noOpUiCompleter = object : ScreenFlashUiCompleter {
-        override fun complete() {
-            // no-op
-        }
-
-        override fun getExpirationTimeMillis(): Long {
-            return 0
-        }
+    private val noOpListener = ScreenFlashListener {
+        // no-op
     }
 
     private val appContext = ApplicationProvider.getApplicationContext<Context>()
@@ -107,21 +102,36 @@
     @Test
     fun isFullyVisible_whenScreenFlashApplyInvoked() {
         val screenFlash = getScreenFlashAfterSettingWindow(true)
-        screenFlash!!.apply(noOpUiCompleter)
+        screenFlash!!.apply(
+            System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(
+                ImageCapture.SCREEN_FLASH_UI_APPLY_TIMEOUT_SECONDS
+            ),
+            noOpListener,
+        )
         assertThat(screenFlashView.alpha).isEqualTo(1f)
     }
 
     @Test
     fun windowBrightnessMaximized_whenScreenFlashApplyInvoked() {
         val screenFlash = getScreenFlashAfterSettingWindow(true)
-        screenFlash!!.apply(noOpUiCompleter)
+        screenFlash!!.apply(
+            System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(
+                ImageCapture.SCREEN_FLASH_UI_APPLY_TIMEOUT_SECONDS
+            ),
+            noOpListener,
+        )
         assertThat(window.attributes.screenBrightness).isEqualTo(1f)
     }
 
     @Test
     fun isTransparent_whenScreenFlashUiClearedAfterApply() {
         val screenFlash = getScreenFlashAfterSettingWindow(true)
-        screenFlash!!.apply(noOpUiCompleter)
+        screenFlash!!.apply(
+            System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(
+                ImageCapture.SCREEN_FLASH_UI_APPLY_TIMEOUT_SECONDS
+            ),
+            noOpListener,
+        )
         screenFlash.clear()
         assertThat(screenFlashView.alpha).isEqualTo(0f)
     }
@@ -133,7 +143,12 @@
         layoutParam.screenBrightness = initialBrightness
         window.setAttributes(layoutParam)
         val screenFlash = getScreenFlashAfterSettingWindow(true)
-        screenFlash!!.apply(noOpUiCompleter)
+        screenFlash!!.apply(
+            System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(
+                ImageCapture.SCREEN_FLASH_UI_APPLY_TIMEOUT_SECONDS
+            ),
+            noOpListener,
+        )
         screenFlash.clear()
         assertThat(window.attributes.screenBrightness).isEqualTo(initialBrightness)
     }