Update ScreenshotRuleAsserter to support capturing a screenshot from a specified display ID

Test: Ran v2/wear-engprod/suites/test-mapping-presubmit-cloud-wear
Flag: EXEMPTED test only
Bug: 401504441
Change-Id: Ib82a42c0cba2b7d913c080cd470b78b4ee625dc7

diff --git a/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotAsserter.kt b/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotAsserter.kt
index de510ac..bc70c81 100644
--- a/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotAsserter.kt
+++ b/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotAsserter.kt
@@ -18,12 +18,14 @@
 
 import android.graphics.Bitmap
 import android.graphics.Rect
+import android.view.Display.DEFAULT_DISPLAY
 import androidx.test.runner.screenshot.Screenshot
 import platform.test.screenshot.matchers.BitmapMatcher
 import platform.test.screenshot.matchers.PixelPerfectMatcher
 
 interface ScreenshotAsserter {
     fun assertGoldenImage(goldenId: String, areas: List<Rect>)
+
     fun assertGoldenImage(goldenId: String)
 }
 
@@ -52,5 +54,31 @@
      * The [Bitmap] produced by [captureStrategy] will be recycled immediately after assertions are
      * completed. Therefore, do not retain references to created [Bitmap]s.
      */
-    val captureStrategy: BitmapSupplier = { Screenshot.capture().bitmap }
-)
+    val captureStrategy: BitmapSupplier = DEFAULT_SCREENSHOT_CAPTURE,
+) {
+    var displayId: Int = DEFAULT_DISPLAY
+        private set(value) {
+            require(captureStrategy == DEFAULT_SCREENSHOT_CAPTURE) {
+                "display ID can only be set when default capture strategy is in use"
+            }
+            field = value
+        }
+
+    constructor(
+        matcher: BitmapMatcher = PixelPerfectMatcher(),
+        beforeScreenshot: () -> Unit = {},
+        afterScreenshot: () -> Unit = {},
+        displayId: Int,
+    ) : this(
+        matcher = matcher,
+        beforeScreenshot = beforeScreenshot,
+        afterScreenshot = afterScreenshot,
+        captureStrategy = DEFAULT_SCREENSHOT_CAPTURE,
+    ) {
+        this.displayId = displayId
+    }
+
+    companion object {
+        val DEFAULT_SCREENSHOT_CAPTURE: BitmapSupplier = { Screenshot.capture().bitmap }
+    }
+}
diff --git a/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotTestRule.kt b/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotTestRule.kt
index 73539a3..fee6d6e 100644
--- a/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotTestRule.kt
+++ b/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotTestRule.kt
@@ -24,14 +24,16 @@
 import android.graphics.Rect
 import android.platform.uiautomatorhelpers.DeviceHelpers.shell
 import android.provider.Settings.System
+import android.view.Display.DEFAULT_DISPLAY
 import androidx.annotation.VisibleForTesting
+import androidx.core.graphics.createBitmap
 import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.runner.screenshot.Screenshot
 import com.android.internal.app.SimpleIconFactory
 import java.io.FileNotFoundException
 import org.junit.rules.TestRule
 import org.junit.runner.Description
 import org.junit.runners.model.Statement
+import platform.test.screenshot.ScreenshotAsserterConfig.Companion.DEFAULT_SCREENSHOT_CAPTURE
 import platform.test.screenshot.matchers.BitmapMatcher
 import platform.test.screenshot.matchers.MSSIMMatcher
 import platform.test.screenshot.matchers.MatchResult
@@ -306,6 +308,7 @@
             .setOnBeforeScreenshot(config.beforeScreenshot)
             .setOnAfterScreenshot(config.afterScreenshot)
             .setScreenshotProvider(config.captureStrategy)
+            .setDisplayId(config.displayId)
             .build()
     }
 
@@ -348,16 +351,26 @@
 
 typealias BitmapSupplier = () -> Bitmap
 
-/** Implements a screenshot asserter based on the ScreenshotRule */
-class ScreenshotRuleAsserter private constructor(private val rule: ScreenshotTestRule) :
-    ScreenshotAsserter {
-    // use the most constraining matcher as default
-    private var matcher: BitmapMatcher = PixelPerfectMatcher()
-    private var beforeScreenshot: Runnable? = null
-    private var afterScreenshot: Runnable? = null
+/** Implements a screenshot asserter based on the ScreenshotRule. */
+class ScreenshotRuleAsserter
+private constructor(
+    private val rule: ScreenshotTestRule,
+    private val beforeScreenshot: Runnable?,
+    private val afterScreenshot: Runnable?,
+    private val screenShotter: BitmapSupplier,
+    private val matcher: BitmapMatcher,
+    private val targetDisplayId: Int,
+) : ScreenshotAsserter {
+    init {
+        require(screenShotter == DEFAULT_SCREENSHOT_CAPTURE || targetDisplayId == DEFAULT_DISPLAY) {
+            "DEFAULT_SCREENSHOT_CAPTURE must be used when non default display ID is set."
+        }
+    }
 
-    // use the instrumentation screenshot as default
-    private var screenShotter: BitmapSupplier = { Screenshot.capture().bitmap }
+    private val displayScreenShotter: (Int) -> Bitmap = { _displayId ->
+        InstrumentationRegistry.getInstrumentation().uiAutomation.takeScreenshot(_displayId)
+            ?: createBitmap(0, 0)
+    }
 
     private var pointerLocationSetting: Int
         get() = shell("settings get system ${System.POINTER_LOCATION}").trim().toIntOrNull() ?: 0
@@ -379,7 +392,7 @@
         runBeforeScreenshot()
         var actual: Bitmap? = null
         try {
-            actual = screenShotter()
+            actual = captureScreenshot(targetDisplayId)
             rule.assertBitmapAgainstGolden(actual, goldenId, matcher)
         } finally {
             actual?.recycle()
@@ -392,7 +405,7 @@
         runBeforeScreenshot()
         var actual: Bitmap? = null
         try {
-            actual = screenShotter()
+            actual = captureScreenshot(targetDisplayId)
             rule.assertBitmapAgainstGolden(actual, goldenId, matcher, areas)
         } finally {
             actual?.recycle()
@@ -400,6 +413,13 @@
         }
     }
 
+    private fun captureScreenshot(displayId: Int): Bitmap =
+        if (screenShotter != DEFAULT_SCREENSHOT_CAPTURE) {
+            screenShotter()
+        } else {
+            displayScreenShotter(displayId)
+        }
+
     private fun runBeforeScreenshot() {
         // Disable Sensitive Content Redaction
         shell("settings put secure disable_secure_windows 1")
@@ -424,25 +444,49 @@
 
     @Deprecated("Use ScreenshotAsserterFactory instead")
     class Builder(private val rule: ScreenshotTestRule) {
-        private var asserter = ScreenshotRuleAsserter(rule)
+        private var matcher: BitmapMatcher? = null
+        private var screenshotProvider: BitmapSupplier? = null
+        private var beforeScreenshot: Runnable? = null
+        private var afterScreenshot: Runnable? = null
+        private var displayId: Int? = null
 
-        fun withMatcher(matcher: BitmapMatcher): Builder = apply { asserter.matcher = matcher }
+        fun withMatcher(matcher: BitmapMatcher): Builder = apply { this.matcher = matcher }
 
         /**
          * The [Bitmap] produced by [screenshotProvider] will be recycled immediately after
          * assertions are completed. Therefore, do not retain references to created [Bitmap]s.
          */
         fun setScreenshotProvider(screenshotProvider: BitmapSupplier): Builder = apply {
-            asserter.screenShotter = screenshotProvider
+            require(displayId == null || displayId == DEFAULT_DISPLAY) {
+                "Custom screenshot provider is unsupported when a display ID is specified."
+            }
+            this.screenshotProvider = screenshotProvider
         }
 
-        fun setOnBeforeScreenshot(run: Runnable): Builder = apply {
-            asserter.beforeScreenshot = run
+        fun setOnBeforeScreenshot(run: Runnable): Builder = apply { this.beforeScreenshot = run }
+
+        fun setOnAfterScreenshot(run: Runnable): Builder = apply { this.afterScreenshot = run }
+
+        fun setDisplayId(displayId: Int): Builder = apply {
+            require(
+                displayId == DEFAULT_DISPLAY ||
+                    screenshotProvider == null ||
+                    screenshotProvider == DEFAULT_SCREENSHOT_CAPTURE
+            ) {
+                "Custom display ID is unsupported when a custom screenshot provider is used"
+            }
+            this.displayId = displayId
         }
 
-        fun setOnAfterScreenshot(run: Runnable): Builder = apply { asserter.afterScreenshot = run }
-
-        fun build(): ScreenshotAsserter = asserter.also { asserter = ScreenshotRuleAsserter(rule) }
+        fun build(): ScreenshotAsserter =
+            ScreenshotRuleAsserter(
+                rule = rule,
+                beforeScreenshot = beforeScreenshot,
+                afterScreenshot = afterScreenshot,
+                screenShotter = screenshotProvider ?: DEFAULT_SCREENSHOT_CAPTURE,
+                matcher = matcher ?: PixelPerfectMatcher(),
+                targetDisplayId = displayId ?: DEFAULT_DISPLAY,
+            )
     }
 }