Merge "Remove SystemUI ScreenshotTestRule (1/2)" into tm-qpr-dev
diff --git a/packages/SystemUI/screenshot/Android.bp b/packages/SystemUI/screenshot/Android.bp
index a79fd9040d..601e92f 100644
--- a/packages/SystemUI/screenshot/Android.bp
+++ b/packages/SystemUI/screenshot/Android.bp
@@ -26,11 +26,7 @@
     manifest: "AndroidManifest.xml",
 
     srcs: [
-        // All files in this library should be in Kotlin besides some exceptions.
         "src/**/*.kt",
-
-        // This file was forked from google3, so exceptionally it can be in Java.
-        "src/com/android/systemui/testing/screenshot/DynamicColorsTestUtils.java",
     ],
 
     resource_dirs: [
diff --git a/packages/SystemUI/screenshot/AndroidManifest.xml b/packages/SystemUI/screenshot/AndroidManifest.xml
index 3b703be..a405836 100644
--- a/packages/SystemUI/screenshot/AndroidManifest.xml
+++ b/packages/SystemUI/screenshot/AndroidManifest.xml
@@ -23,6 +23,4 @@
             android:exported="true"
             android:theme="@style/Theme.SystemUI.Screenshot" />
     </application>
-
-    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
 </manifest>
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/Bitmap.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/Bitmap.kt
new file mode 100644
index 0000000..3d26cda
--- /dev/null
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/Bitmap.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.testing.screenshot
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.os.Build
+import android.view.View
+import platform.test.screenshot.matchers.MSSIMMatcher
+import platform.test.screenshot.matchers.PixelPerfectMatcher
+
+/** Draw this [View] into a [Bitmap]. */
+fun View.drawIntoBitmap(): Bitmap {
+    val bitmap =
+        Bitmap.createBitmap(
+            measuredWidth,
+            measuredHeight,
+            Bitmap.Config.ARGB_8888,
+        )
+    val canvas = Canvas(bitmap)
+    draw(canvas)
+    return bitmap
+}
+
+/**
+ * The [BitmapMatcher][platform.test.screenshot.matchers.BitmapMatcher] that should be used for
+ * screenshot *unit* tests.
+ */
+val UnitTestBitmapMatcher =
+    if (Build.CPU_ABI == "x86_64") {
+        // Different CPU architectures can sometimes end up rendering differently, so we can't do
+        // pixel-perfect matching on different architectures using the same golden. Given that our
+        // presubmits are run on cf_x86_64_phone, our goldens should be perfectly matched on the
+        // x86_64 architecture and use the Structural Similarity Index on others.
+        // TODO(b/237511747): Run our screenshot presubmit tests on arm64 instead so that we can
+        // do pixel perfect matching both at presubmit time and at development time with actual
+        // devices.
+        PixelPerfectMatcher()
+    } else {
+        MSSIMMatcher()
+    }
+
+/**
+ * The [BitmapMatcher][platform.test.screenshot.matchers.BitmapMatcher] that should be used for
+ * screenshot *unit* tests.
+ *
+ * We use the Structural Similarity Index for integration tests because they usually contain
+ * additional information and noise that shouldn't break the test.
+ */
+val IntegrationTestBitmapMatcher = MSSIMMatcher()
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/DynamicColorsTestUtils.java b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/DynamicColorsTestUtils.java
deleted file mode 100644
index 96ec4c5..0000000
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/DynamicColorsTestUtils.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.testing.screenshot;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import android.app.UiAutomation;
-import android.content.Context;
-import android.provider.Settings;
-import android.util.Log;
-
-import androidx.annotation.ColorInt;
-import androidx.annotation.ColorRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.RequiresApi;
-import androidx.core.content.ContextCompat;
-import androidx.test.espresso.Espresso;
-import androidx.test.espresso.IdlingRegistry;
-import androidx.test.espresso.IdlingResource;
-
-import org.json.JSONObject;
-import org.junit.function.ThrowingRunnable;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/*
- * Note: This file was forked from
- * google3/third_party/java_src/android_libs/material_components/screenshot_tests/java/android/
- * support/design/scuba/color/DynamicColorsTestUtils.java.
- */
-
-/** Utility that helps change the dynamic system colors for testing. */
-@RequiresApi(32)
-public class DynamicColorsTestUtils {
-
-    private static final String TAG = DynamicColorsTestUtils.class.getSimpleName();
-
-    private static final String THEME_CUSTOMIZATION_KEY = "theme_customization_overlay_packages";
-    private static final String THEME_CUSTOMIZATION_SYSTEM_PALETTE_KEY =
-            "android.theme.customization.system_palette";
-
-    private static final int ORANGE_SYSTEM_SEED_COLOR = 0xA66800;
-    private static final int ORANGE_EXPECTED_SYSTEM_ACCENT1_600_COLOR = -8235756;
-
-    private DynamicColorsTestUtils() {
-    }
-
-    /**
-     * Update system dynamic colors (e.g., android.R.color.system_accent1_600) based on an orange
-     * seed color, and then wait for the change to propagate to the app by comparing
-     * android.R.color.system_accent1_600 to the expected orange value.
-     */
-    public static void updateSystemColorsToOrange() {
-        updateSystemColors(ORANGE_SYSTEM_SEED_COLOR, ORANGE_EXPECTED_SYSTEM_ACCENT1_600_COLOR);
-    }
-
-    /**
-     * Update system dynamic colors (e.g., android.R.color.system_accent1_600) based on the provided
-     * {@code seedColor}, and then wait for the change to propagate to the app by comparing
-     * android.R.color.system_accent1_600 to {@code expectedSystemAccent1600}.
-     */
-    public static void updateSystemColors(
-            @ColorInt int seedColor, @ColorInt int expectedSystemAccent1600) {
-        Context context = getInstrumentation().getTargetContext();
-
-        int actualSystemAccent1600 =
-                ContextCompat.getColor(context, android.R.color.system_accent1_600);
-
-        if (expectedSystemAccent1600 == actualSystemAccent1600) {
-            String expectedColorString = Integer.toHexString(expectedSystemAccent1600);
-            Log.d(
-                    TAG,
-                    "Skipped updating system colors since system_accent1_600 is already equal to "
-                            + "expected: "
-                            + expectedColorString);
-            return;
-        }
-
-        updateSystemColors(seedColor);
-    }
-
-    /**
-     * Update system dynamic colors (e.g., android.R.color.system_accent1_600) based on the provided
-     * {@code seedColor}, and then wait for the change to propagate to the app by checking
-     * android.R.color.system_accent1_600 for any change.
-     */
-    public static void updateSystemColors(@ColorInt int seedColor) {
-        Context context = getInstrumentation().getTargetContext();
-
-        // Initialize system color idling resource with original system_accent1_600 value.
-        ColorChangeIdlingResource systemColorIdlingResource =
-                new ColorChangeIdlingResource(context, android.R.color.system_accent1_600);
-
-        // Update system theme color setting to trigger fabricated resource overlay.
-        runWithShellPermissionIdentity(
-                () ->
-                        Settings.Secure.putString(
-                                context.getContentResolver(),
-                                THEME_CUSTOMIZATION_KEY,
-                                buildThemeCustomizationString(seedColor)));
-
-        // Wait for system color update to propagate to app.
-        IdlingRegistry idlingRegistry = IdlingRegistry.getInstance();
-        idlingRegistry.register(systemColorIdlingResource);
-        Espresso.onIdle();
-        idlingRegistry.unregister(systemColorIdlingResource);
-
-        Log.d(TAG,
-                Settings.Secure.getString(context.getContentResolver(), THEME_CUSTOMIZATION_KEY));
-    }
-
-    private static String buildThemeCustomizationString(@ColorInt int seedColor) {
-        String seedColorHex = Integer.toHexString(seedColor);
-        Map<String, String> themeCustomizationMap = new HashMap<>();
-        themeCustomizationMap.put(THEME_CUSTOMIZATION_SYSTEM_PALETTE_KEY, seedColorHex);
-        return new JSONObject(themeCustomizationMap).toString();
-    }
-
-    private static void runWithShellPermissionIdentity(@NonNull ThrowingRunnable runnable) {
-        UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
-        uiAutomation.adoptShellPermissionIdentity();
-        try {
-            runnable.run();
-        } catch (Throwable e) {
-            throw new RuntimeException(e);
-        } finally {
-            uiAutomation.dropShellPermissionIdentity();
-        }
-    }
-
-    private static class ColorChangeIdlingResource implements IdlingResource {
-
-        private final Context mContext;
-        private final int mColorResId;
-        private final int mInitialColorInt;
-
-        private ResourceCallback mResourceCallback;
-        private boolean mIdleNow;
-
-        ColorChangeIdlingResource(Context context, @ColorRes int colorResId) {
-            this.mContext = context;
-            this.mColorResId = colorResId;
-            this.mInitialColorInt = ContextCompat.getColor(context, colorResId);
-        }
-
-        @Override
-        public String getName() {
-            return ColorChangeIdlingResource.class.getName();
-        }
-
-        @Override
-        public boolean isIdleNow() {
-            if (mIdleNow) {
-                return true;
-            }
-
-            int currentColorInt = ContextCompat.getColor(mContext, mColorResId);
-
-            String initialColorString = Integer.toHexString(mInitialColorInt);
-            String currentColorString = Integer.toHexString(currentColorInt);
-            Log.d(TAG, String.format("Initial=%s, Current=%s", initialColorString,
-                    currentColorString));
-
-            mIdleNow = currentColorInt != mInitialColorInt;
-            Log.d(TAG, String.format("idleNow=%b", mIdleNow));
-
-            if (mIdleNow) {
-                mResourceCallback.onTransitionToIdle();
-            }
-            return mIdleNow;
-        }
-
-        @Override
-        public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
-            this.mResourceCallback = resourceCallback;
-        }
-    }
-}
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ScreenshotTestRule.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ScreenshotTestRule.kt
deleted file mode 100644
index 564901c..0000000
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ScreenshotTestRule.kt
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.testing.screenshot
-
-import android.app.UiModeManager
-import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import android.os.Build
-import android.os.UserHandle
-import android.view.Display
-import android.view.View
-import android.view.WindowManagerGlobal
-import androidx.test.platform.app.InstrumentationRegistry
-import org.junit.rules.TestRule
-import org.junit.runner.Description
-import org.junit.runners.model.Statement
-import platform.test.screenshot.GoldenImagePathManager
-import platform.test.screenshot.PathConfig
-import platform.test.screenshot.PathElementNoContext
-import platform.test.screenshot.ScreenshotTestRule
-import platform.test.screenshot.matchers.MSSIMMatcher
-import platform.test.screenshot.matchers.PixelPerfectMatcher
-
-/**
- * A base rule for screenshot diff tests.
- *
- * This rules takes care of setting up the activity according to [testSpec] by:
- * - emulating the display size and density.
- * - setting the dark/light mode.
- * - setting the system (Material You) colors to a fixed value.
- *
- * @see ComposeScreenshotTestRule
- * @see ViewScreenshotTestRule
- */
-class ScreenshotTestRule(private val testSpec: ScreenshotTestSpec) : TestRule {
-    private var currentDisplay: DisplaySpec? = null
-    private var currentGoldenIdentifier: String? = null
-
-    private val pathConfig =
-        PathConfig(
-            PathElementNoContext("model", isDir = true) {
-                currentDisplay?.name ?: error("currentDisplay is null")
-            },
-        )
-    private val matcher = if (shouldUsePerfectMatching()) {
-        PixelPerfectMatcher()
-    } else {
-        MSSIMMatcher()
-    }
-
-    private val screenshotRule =
-        ScreenshotTestRule(
-            SystemUIGoldenImagePathManager(
-                pathConfig,
-                currentGoldenIdentifier = {
-                    currentGoldenIdentifier ?: error("currentGoldenIdentifier is null")
-                },
-            )
-        )
-
-    private fun shouldUsePerfectMatching(): Boolean {
-        // Different CPU architectures can sometimes end up rendering differently, so we can't do
-        // pixel-perfect matching on different architectures using the same golden. Given that our
-        // presubmits are run on cf_x86_64_phone, our goldens should be perfectly matched on the
-        // x86_64 architecture and use the Structural Similarity Index on others.
-        // TODO(b/237511747): Run our screenshot presubmit tests on arm64 instead so that we can
-        // do pixel perfect matching both at presubmit time and at development time with actual
-        // devices.
-        return Build.CPU_ABI == "x86_64"
-    }
-
-    override fun apply(base: Statement, description: Description): Statement {
-        // The statement which call beforeTest() before running the test and afterTest() afterwards.
-        val statement =
-            object : Statement() {
-                override fun evaluate() {
-                    try {
-                        beforeTest()
-                        base.evaluate()
-                    } finally {
-                        afterTest()
-                    }
-                }
-            }
-
-        return screenshotRule.apply(statement, description)
-    }
-
-    private fun beforeTest() {
-        // Update the system colors to a fixed color, so that tests don't depend on the host device
-        // extracted colors. Note that we don't restore the default device colors at the end of the
-        // test because changing the colors (and waiting for them to be applied) is costly and makes
-        // the screenshot tests noticeably slower.
-        DynamicColorsTestUtils.updateSystemColorsToOrange()
-
-        // Emulate the display size and density.
-        val display = testSpec.display
-        val density = display.densityDpi
-        val wm = WindowManagerGlobal.getWindowManagerService()
-        val (width, height) = getEmulatedDisplaySize()
-        wm.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, density, UserHandle.myUserId())
-        wm.setForcedDisplaySize(Display.DEFAULT_DISPLAY, width, height)
-
-        // Force the dark/light theme.
-        val uiModeManager =
-            InstrumentationRegistry.getInstrumentation()
-                .targetContext
-                .getSystemService(Context.UI_MODE_SERVICE) as UiModeManager
-        uiModeManager.setApplicationNightMode(
-            if (testSpec.isDarkTheme) {
-                UiModeManager.MODE_NIGHT_YES
-            } else {
-                UiModeManager.MODE_NIGHT_NO
-            }
-        )
-    }
-
-    private fun afterTest() {
-        // Reset the density and display size.
-        val wm = WindowManagerGlobal.getWindowManagerService()
-        wm.clearForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, UserHandle.myUserId())
-        wm.clearForcedDisplaySize(Display.DEFAULT_DISPLAY)
-
-        // Reset the dark/light theme.
-        val uiModeManager =
-            InstrumentationRegistry.getInstrumentation()
-                .targetContext
-                .getSystemService(Context.UI_MODE_SERVICE) as UiModeManager
-        uiModeManager.setApplicationNightMode(UiModeManager.MODE_NIGHT_AUTO)
-    }
-
-    /**
-     * Compare the content of [view] with the golden image identified by [goldenIdentifier] in the
-     * context of [testSpec].
-     */
-    fun screenshotTest(goldenIdentifier: String, view: View) {
-        val bitmap = drawIntoBitmap(view)
-
-        // Compare bitmap against golden asset.
-        val isDarkTheme = testSpec.isDarkTheme
-        val isLandscape = testSpec.isLandscape
-        val identifierWithSpec = buildString {
-            append(goldenIdentifier)
-            if (isDarkTheme) append("_dark")
-            if (isLandscape) append("_landscape")
-        }
-
-        // TODO(b/230832101): Provide a way to pass a PathConfig and override the file name on
-        // device to assertBitmapAgainstGolden instead?
-        currentDisplay = testSpec.display
-        currentGoldenIdentifier = goldenIdentifier
-        screenshotRule.assertBitmapAgainstGolden(bitmap, identifierWithSpec, matcher)
-        currentDisplay = null
-        currentGoldenIdentifier = goldenIdentifier
-    }
-
-    /** Draw [view] into a [Bitmap]. */
-    private fun drawIntoBitmap(view: View): Bitmap {
-        val bitmap =
-            Bitmap.createBitmap(
-                view.measuredWidth,
-                view.measuredHeight,
-                Bitmap.Config.ARGB_8888,
-            )
-        val canvas = Canvas(bitmap)
-        view.draw(canvas)
-        return bitmap
-    }
-
-    /** Get the emulated display size for [testSpec]. */
-    private fun getEmulatedDisplaySize(): Pair<Int, Int> {
-        val display = testSpec.display
-        val isPortraitNaturalPosition = display.width < display.height
-        return if (testSpec.isLandscape) {
-            if (isPortraitNaturalPosition) {
-                display.height to display.width
-            } else {
-                display.width to display.height
-            }
-        } else {
-            if (isPortraitNaturalPosition) {
-                display.width to display.height
-            } else {
-                display.height to display.width
-            }
-        }
-    }
-}
-
-private class SystemUIGoldenImagePathManager(
-    pathConfig: PathConfig,
-    private val currentGoldenIdentifier: () -> String,
-) :
-    GoldenImagePathManager(
-        appContext = InstrumentationRegistry.getInstrumentation().context,
-        deviceLocalPath =
-            InstrumentationRegistry.getInstrumentation()
-                .targetContext
-                .filesDir
-                .absolutePath
-                .toString() + "/sysui_screenshots",
-        pathConfig = pathConfig,
-    ) {
-    // This string is appended to all actual/expected screenshots on the device. We append the
-    // golden identifier so that our pull_golden.py scripts can map a screenshot on device to its
-    // asset (and automatically update it, if necessary).
-    override fun toString() = currentGoldenIdentifier()
-}
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ScreenshotTestSpec.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ScreenshotTestSpec.kt
deleted file mode 100644
index 7fc6245..0000000
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ScreenshotTestSpec.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.testing.screenshot
-
-/** The specification of a device display to be used in a screenshot test. */
-data class DisplaySpec(
-    val name: String,
-    val width: Int,
-    val height: Int,
-    val densityDpi: Int,
-)
-
-/** The specification of a screenshot diff test. */
-class ScreenshotTestSpec(
-    val display: DisplaySpec,
-    val isDarkTheme: Boolean = false,
-    val isLandscape: Boolean = false,
-) {
-    companion object {
-        /**
-         * Return a list of [ScreenshotTestSpec] for each of the [displays].
-         *
-         * If [isDarkTheme] is null, this will create a spec for both light and dark themes, for
-         * each of the orientation.
-         *
-         * If [isLandscape] is null, this will create a spec for both portrait and landscape, for
-         * each of the light/dark themes.
-         */
-        fun forDisplays(
-            vararg displays: DisplaySpec,
-            isDarkTheme: Boolean? = null,
-            isLandscape: Boolean? = null,
-        ): List<ScreenshotTestSpec> {
-            return displays.flatMap { display ->
-                buildList {
-                    fun addDisplay(isLandscape: Boolean) {
-                        if (isDarkTheme != true) {
-                            add(ScreenshotTestSpec(display, isDarkTheme = false, isLandscape))
-                        }
-
-                        if (isDarkTheme != false) {
-                            add(ScreenshotTestSpec(display, isDarkTheme = true, isLandscape))
-                        }
-                    }
-
-                    if (isLandscape != true) {
-                        addDisplay(isLandscape = false)
-                    }
-
-                    if (isLandscape != false) {
-                        addDisplay(isLandscape = true)
-                    }
-                }
-            }
-        }
-    }
-
-    override fun toString(): String = buildString {
-        // This string is appended to PNGs stored in the device, so let's keep it simple.
-        append(display.name)
-        if (isDarkTheme) append("_dark")
-        if (isLandscape) append("_landscape")
-    }
-}
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt
new file mode 100644
index 0000000..cbab0a7
--- /dev/null
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.testing.screenshot
+
+import androidx.test.platform.app.InstrumentationRegistry
+import platform.test.screenshot.GoldenImagePathManager
+import platform.test.screenshot.PathConfig
+
+/** A [GoldenImagePathManager] that should be used for all SystemUI screenshot tests. */
+class SystemUIGoldenImagePathManager(
+    pathConfig: PathConfig,
+) :
+    GoldenImagePathManager(
+        appContext = InstrumentationRegistry.getInstrumentation().context,
+        deviceLocalPath =
+            InstrumentationRegistry.getInstrumentation()
+                .targetContext
+                .filesDir
+                .absolutePath
+                .toString() + "/sysui_screenshots",
+        pathConfig = pathConfig,
+    ) {
+    override fun toString(): String {
+        // This string is appended to all actual/expected screenshots on the device, so make sure
+        // it is a static value.
+        return "SystemUIGoldenImagePathManager"
+    }
+}
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt
index 6a80c48..3209c8b 100644
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.systemui.testing.screenshot
 
 import android.app.Activity
@@ -11,21 +27,35 @@
 import org.junit.rules.TestRule
 import org.junit.runner.Description
 import org.junit.runners.model.Statement
+import platform.test.screenshot.DeviceEmulationRule
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.MaterialYouColorsRule
+import platform.test.screenshot.ScreenshotTestRule
+import platform.test.screenshot.getEmulatedDevicePathConfig
 
-/** A rule for View screenshot diff tests. */
-class ViewScreenshotTestRule(testSpec: ScreenshotTestSpec) : TestRule {
+/** A rule for View screenshot diff unit tests. */
+class ViewScreenshotTestRule(emulationSpec: DeviceEmulationSpec) : TestRule {
+    private val colorsRule = MaterialYouColorsRule()
+    private val deviceEmulationRule = DeviceEmulationRule(emulationSpec)
+    private val screenshotRule =
+        ScreenshotTestRule(
+            SystemUIGoldenImagePathManager(getEmulatedDevicePathConfig(emulationSpec))
+        )
     private val activityRule = ActivityScenarioRule(ScreenshotActivity::class.java)
-    private val screenshotRule = ScreenshotTestRule(testSpec)
-
-    private val delegate = RuleChain.outerRule(screenshotRule).around(activityRule)
+    private val delegateRule =
+        RuleChain.outerRule(colorsRule)
+            .around(deviceEmulationRule)
+            .around(screenshotRule)
+            .around(activityRule)
+    private val matcher = UnitTestBitmapMatcher
 
     override fun apply(base: Statement, description: Description): Statement {
-        return delegate.apply(base, description)
+        return delegateRule.apply(base, description)
     }
 
     /**
      * Compare the content of the view provided by [viewProvider] with the golden image identified
-     * by [goldenIdentifier] in the context of [testSpec].
+     * by [goldenIdentifier] in the context of [emulationSpec].
      */
     fun screenshotTest(
         goldenIdentifier: String,
@@ -46,13 +76,17 @@
             // Check that the content is what we expected.
             val content = activity.requireViewById<ViewGroup>(android.R.id.content)
             assertEquals(1, content.childCount)
-            screenshotRule.screenshotTest(goldenIdentifier, content.getChildAt(0))
+            screenshotRule.assertBitmapAgainstGolden(
+                content.getChildAt(0).drawIntoBitmap(),
+                goldenIdentifier,
+                matcher
+            )
         }
     }
 
     /**
      * Compare the content of the dialog provided by [dialogProvider] with the golden image
-     * identified by [goldenIdentifier] in the context of [testSpec].
+     * identified by [goldenIdentifier] in the context of [emulationSpec].
      */
     fun dialogScreenshotTest(
         goldenIdentifier: String,
@@ -81,7 +115,11 @@
             // Check that the content is what we expected.
             val dialog = dialog ?: error("dialog is null")
             try {
-                screenshotRule.screenshotTest(goldenIdentifier, dialog.window.decorView)
+                screenshotRule.assertBitmapAgainstGolden(
+                    dialog.window.decorView.drawIntoBitmap(),
+                    goldenIdentifier,
+                    matcher,
+                )
             } finally {
                 dialog.dismiss()
             }