summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Ram Peri <ramperi@google.com> 2023-07-07 03:37:22 +0000
committer Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> 2023-07-07 03:37:22 +0000
commitdc180e5e1a0f1036ffc2d3a04f07e5c6f5b36b4a (patch)
treed54cd58b804fc9bda6511ea476d8f7b32d841849
parentc41e070125d6c627cdc06fe07b4cdeec1b6e5d53 (diff)
parentaae1baa5c8f2bd9e723251c68b647a97d3e869b1 (diff)
Merge "Migrating code from systemui to common library." into udc-qpr-dev am: e13db76147 am: aae1baa5c8
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/23923591 Change-Id: Ic847bc0e6db53eadb2b5a16a82e48b18d53054eb Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--packages/SystemUI/compose/testing/Android.bp43
-rw-r--r--packages/SystemUI/compose/testing/AndroidManifest.xml25
-rw-r--r--packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt95
-rw-r--r--packages/SystemUI/screenshot/Android.bp45
-rw-r--r--packages/SystemUI/screenshot/AndroidManifest.xml27
-rw-r--r--packages/SystemUI/screenshot/res/values/themes.xml31
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/Bitmap.kt66
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ExternalViewScreenshotTestRule.kt107
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ScreenshotActivity.kt22
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt44
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/TestAppComponentFactory.kt60
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/View.kt42
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt233
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt188
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/WindowCapture.kt37
15 files changed, 0 insertions, 1065 deletions
diff --git a/packages/SystemUI/compose/testing/Android.bp b/packages/SystemUI/compose/testing/Android.bp
deleted file mode 100644
index 555f42ef9881..000000000000
--- a/packages/SystemUI/compose/testing/Android.bp
+++ /dev/null
@@ -1,43 +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 {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
-}
-
-android_library {
- name: "SystemUIComposeTesting",
- manifest: "AndroidManifest.xml",
-
- srcs: [
- "src/**/*.kt",
- ],
-
- static_libs: [
- "PlatformComposeCore",
- "SystemUIScreenshotLib",
-
- "androidx.compose.runtime_runtime",
- "androidx.compose.material3_material3",
- "androidx.compose.ui_ui-test-junit4",
- "androidx.compose.ui_ui-test-manifest",
- ],
-
- kotlincflags: ["-Xjvm-default=all"],
-}
diff --git a/packages/SystemUI/compose/testing/AndroidManifest.xml b/packages/SystemUI/compose/testing/AndroidManifest.xml
deleted file mode 100644
index b1f7c3be2796..000000000000
--- a/packages/SystemUI/compose/testing/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.android.systemui.testing.compose">
- <application
- android:appComponentFactory="androidx.core.app.AppComponentFactory"
- tools:replace="android:appComponentFactory">
- </application>
-</manifest>
diff --git a/packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt b/packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt
deleted file mode 100644
index cb9e53ce4d33..000000000000
--- a/packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt
+++ /dev/null
@@ -1,95 +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.compose
-
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.platform.ViewRootForTest
-import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import androidx.compose.ui.test.onRoot
-import com.android.compose.theme.PlatformTheme
-import com.android.systemui.testing.screenshot.ScreenshotActivity
-import com.android.systemui.testing.screenshot.SystemUIGoldenImagePathManager
-import com.android.systemui.testing.screenshot.UnitTestBitmapMatcher
-import com.android.systemui.testing.screenshot.drawIntoBitmap
-import org.junit.rules.RuleChain
-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 Compose screenshot diff tests. */
-class ComposeScreenshotTestRule(
- emulationSpec: DeviceEmulationSpec,
- assetPathRelativeToBuildRoot: String
-) : TestRule {
- private val colorsRule = MaterialYouColorsRule()
- private val deviceEmulationRule = DeviceEmulationRule(emulationSpec)
- private val screenshotRule =
- ScreenshotTestRule(
- SystemUIGoldenImagePathManager(
- getEmulatedDevicePathConfig(emulationSpec),
- assetPathRelativeToBuildRoot
- )
- )
- private val composeRule = createAndroidComposeRule<ScreenshotActivity>()
- private val delegateRule =
- RuleChain.outerRule(colorsRule)
- .around(deviceEmulationRule)
- .around(screenshotRule)
- .around(composeRule)
- private val matcher = UnitTestBitmapMatcher
-
- override fun apply(base: Statement, description: Description): Statement {
- return delegateRule.apply(base, description)
- }
-
- /**
- * Compare [content] with the golden image identified by [goldenIdentifier] in the context of
- * [testSpec].
- */
- fun screenshotTest(
- goldenIdentifier: String,
- content: @Composable () -> Unit,
- ) {
- // Make sure that the activity draws full screen and fits the whole display instead of the
- // system bars.
- val activity = composeRule.activity
- activity.mainExecutor.execute { activity.window.setDecorFitsSystemWindows(false) }
-
- // Set the content using the AndroidComposeRule to make sure that the Activity is set up
- // correctly.
- composeRule.setContent {
- PlatformTheme {
- Surface(
- color = MaterialTheme.colorScheme.background,
- ) {
- content()
- }
- }
- }
- composeRule.waitForIdle()
-
- val view = (composeRule.onRoot().fetchSemanticsNode().root as ViewRootForTest).view
- screenshotRule.assertBitmapAgainstGolden(view.drawIntoBitmap(), goldenIdentifier, matcher)
- }
-}
diff --git a/packages/SystemUI/screenshot/Android.bp b/packages/SystemUI/screenshot/Android.bp
deleted file mode 100644
index f449398fc9f8..000000000000
--- a/packages/SystemUI/screenshot/Android.bp
+++ /dev/null
@@ -1,45 +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 {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
-}
-
-android_library {
- name: "SystemUIScreenshotLib",
- manifest: "AndroidManifest.xml",
-
- srcs: [
- "src/**/*.kt",
- ],
-
- resource_dirs: [
- "res",
- ],
-
- static_libs: [
- "SystemUI-core",
- "androidx.test.espresso.core",
- "androidx.appcompat_appcompat",
- "platform-screenshot-diff-core",
- "guava",
- ],
-
- kotlincflags: ["-Xjvm-default=all"],
-}
diff --git a/packages/SystemUI/screenshot/AndroidManifest.xml b/packages/SystemUI/screenshot/AndroidManifest.xml
deleted file mode 100644
index ba3dc8c53fff..000000000000
--- a/packages/SystemUI/screenshot/AndroidManifest.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.systemui.testing.screenshot">
- <application>
- <activity
- android:name="com.android.systemui.testing.screenshot.ScreenshotActivity"
- android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize"
- android:exported="true"
- android:theme="@style/Theme.SystemUI.Screenshot" />
- </application>
-</manifest>
diff --git a/packages/SystemUI/screenshot/res/values/themes.xml b/packages/SystemUI/screenshot/res/values/themes.xml
deleted file mode 100644
index a7f8a264e892..000000000000
--- a/packages/SystemUI/screenshot/res/values/themes.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-<resources>
- <style name="Theme.SystemUI.Screenshot" parent="Theme.SystemUI">
- <item name="android:windowActionBar">false</item>
- <item name="android:windowNoTitle">true</item>
-
- <!-- We make the status and navigation bars transparent so that the screenshotted content is
- not clipped by the status bar height when drawn into the Bitmap (which is what happens
- given that we draw the view into the Bitmap using hardware acceleration). -->
- <item name="android:statusBarColor">@android:color/transparent</item>
- <item name="android:navigationBarColor">@android:color/transparent</item>
-
- <!-- Make sure that device specific cutouts don't impact the outcome of screenshot tests -->
- <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
- </style>
-</resources> \ No newline at end of file
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
deleted file mode 100644
index a4a70a49fce3..000000000000
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/Bitmap.kt
+++ /dev/null
@@ -1,66 +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.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]. */
-// TODO(b/195673633): Remove this once Compose screenshot tests use hardware rendering for their
-// tests.
-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/ExternalViewScreenshotTestRule.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ExternalViewScreenshotTestRule.kt
deleted file mode 100644
index e032bb9b7e30..000000000000
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ExternalViewScreenshotTestRule.kt
+++ /dev/null
@@ -1,107 +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.Activity
-import android.graphics.Color
-import android.view.View
-import android.view.Window
-import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
-import androidx.core.view.WindowInsetsCompat
-import androidx.core.view.WindowInsetsControllerCompat
-import androidx.core.view.WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
-import androidx.test.platform.app.InstrumentationRegistry
-import org.junit.rules.RuleChain
-import org.junit.rules.TestRule
-import org.junit.runner.Description
-import org.junit.runners.model.Statement
-import platform.test.screenshot.*
-
-/**
- * A rule that allows to run a screenshot diff test on a view that is hosted in another activity.
- */
-class ExternalViewScreenshotTestRule(
- emulationSpec: DeviceEmulationSpec,
- assetPathRelativeToBuildRoot: String
-) : TestRule {
-
- private val colorsRule = MaterialYouColorsRule()
- private val deviceEmulationRule = DeviceEmulationRule(emulationSpec)
- private val screenshotRule =
- ScreenshotTestRule(
- SystemUIGoldenImagePathManager(
- getEmulatedDevicePathConfig(emulationSpec),
- assetPathRelativeToBuildRoot
- )
- )
- private val delegateRule =
- RuleChain.outerRule(colorsRule).around(deviceEmulationRule).around(screenshotRule)
- private val matcher = UnitTestBitmapMatcher
-
- override fun apply(base: Statement, description: Description): Statement {
- return delegateRule.apply(base, description)
- }
-
- /**
- * Compare the content of the [view] with the golden image identified by [goldenIdentifier] in
- * the context of [emulationSpec]. Window must be specified to capture views that render
- * hardware buffers.
- */
- fun screenshotTest(goldenIdentifier: String, view: View, window: Window? = null) {
- view.removeElevationRecursively()
-
- ScreenshotRuleAsserter.Builder(screenshotRule)
- .setScreenshotProvider { view.toBitmap(window) }
- .withMatcher(matcher)
- .build()
- .assertGoldenImage(goldenIdentifier)
- }
-
- /**
- * Compare the content of the [activity] with the golden image identified by [goldenIdentifier]
- * in the context of [emulationSpec].
- */
- fun activityScreenshotTest(
- goldenIdentifier: String,
- activity: Activity,
- ) {
- val rootView = activity.window.decorView
-
- // Hide system bars, remove insets, focus and make sure device-specific cutouts
- // don't affect screenshots
- InstrumentationRegistry.getInstrumentation().runOnMainSync {
- val window = activity.window
- window.setDecorFitsSystemWindows(false)
- WindowInsetsControllerCompat(window, rootView).apply {
- hide(WindowInsetsCompat.Type.systemBars())
- systemBarsBehavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
- }
-
- window.statusBarColor = Color.TRANSPARENT
- window.navigationBarColor = Color.TRANSPARENT
- window.attributes =
- window.attributes.apply {
- layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
- }
-
- rootView.removeInsetsRecursively()
- activity.currentFocus?.clearFocus()
- }
-
- screenshotTest(goldenIdentifier, rootView, activity.window)
- }
-}
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ScreenshotActivity.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ScreenshotActivity.kt
deleted file mode 100644
index 2a55a80eb7f4..000000000000
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ScreenshotActivity.kt
+++ /dev/null
@@ -1,22 +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 androidx.activity.ComponentActivity
-
-/** The Activity that is launched and whose content is set for screenshot tests. */
-class ScreenshotActivity : ComponentActivity()
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
deleted file mode 100644
index 72d8c5a09852..000000000000
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt
+++ /dev/null
@@ -1,44 +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 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,
- assetsPathRelativeToBuildRoot: String
-) :
- GoldenImagePathManager(
- appContext = InstrumentationRegistry.getInstrumentation().context,
- assetsPathRelativeToBuildRoot = assetsPathRelativeToBuildRoot,
- 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/TestAppComponentFactory.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/TestAppComponentFactory.kt
deleted file mode 100644
index 98e9aaf44ceb..000000000000
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/TestAppComponentFactory.kt
+++ /dev/null
@@ -1,60 +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.Activity
-import android.content.Intent
-import androidx.core.app.AppComponentFactory
-
-class TestAppComponentFactory : AppComponentFactory() {
-
- init {
- instance = this
- }
-
- private val overrides: MutableMap<String, () -> Activity> = hashMapOf()
-
- fun clearOverrides() {
- overrides.clear()
- }
-
- fun <T : Activity> registerActivityOverride(activity: Class<T>, provider: () -> T) {
- overrides[activity.name] = provider
- }
-
- override fun instantiateActivityCompat(
- cl: ClassLoader,
- className: String,
- intent: Intent?
- ): Activity {
- return overrides
- .getOrDefault(className) { super.instantiateActivityCompat(cl, className, intent) }
- .invoke()
- }
-
- companion object {
-
- private var instance: TestAppComponentFactory? = null
-
- fun getInstance(): TestAppComponentFactory =
- instance
- ?: error(
- "TestAppComponentFactory is not initialized, " +
- "did you specify it in the manifest?"
- )
- }
-}
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/View.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/View.kt
deleted file mode 100644
index b84d26a7034a..000000000000
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/View.kt
+++ /dev/null
@@ -1,42 +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.view.View
-import android.view.ViewGroup
-import com.android.systemui.util.children
-import android.view.WindowInsets
-
-/**
- * Elevation/shadows is not deterministic when doing hardware rendering, this exentsion allows to
- * disable it for any view in the hierarchy.
- */
-fun View.removeElevationRecursively() {
- this.elevation = 0f
- (this as? ViewGroup)?.children?.forEach(View::removeElevationRecursively)
-}
-
-/**
- * Different devices could have different insets (e.g. different height of the navigation bar or
- * taskbar). This method dispatches empty insets to the whole view hierarchy and removes
- * the original listener, so the views won't receive real insets.
- */
-fun View.removeInsetsRecursively() {
- this.dispatchApplyWindowInsets(WindowInsets.CONSUMED)
- this.setOnApplyWindowInsetsListener(null)
- (this as? ViewGroup)?.children?.forEach(View::removeInsetsRecursively)
-}
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt
deleted file mode 100644
index dedf0a7a742d..000000000000
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt
+++ /dev/null
@@ -1,233 +0,0 @@
-package com.android.systemui.testing.screenshot
-
-import android.annotation.WorkerThread
-import android.app.Activity
-import android.content.Context
-import android.content.ContextWrapper
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import android.graphics.HardwareRenderer
-import android.graphics.Rect
-import android.os.Build
-import android.os.Handler
-import android.os.Looper
-import android.util.Log
-import android.view.PixelCopy
-import android.view.SurfaceView
-import android.view.View
-import android.view.ViewTreeObserver
-import android.view.Window
-import androidx.annotation.RequiresApi
-import androidx.concurrent.futures.ResolvableFuture
-import androidx.test.annotation.ExperimentalTestApi
-import androidx.test.core.internal.os.HandlerExecutor
-import androidx.test.espresso.Espresso
-import androidx.test.platform.graphics.HardwareRendererCompat
-import com.google.common.util.concurrent.FutureCallback
-import com.google.common.util.concurrent.Futures
-import com.google.common.util.concurrent.ListenableFuture
-import kotlin.coroutines.suspendCoroutine
-import kotlinx.coroutines.runBlocking
-
-/*
- * This file was forked from androidx/test/core/view/ViewCapture.kt to add [Window] parameter to
- * [View.captureToBitmap].
- * TODO(b/195673633): Remove this fork and use the AndroidX version instead.
- */
-
-/**
- * Asynchronously captures an image of the underlying view into a [Bitmap].
- *
- * For devices below [Build.VERSION_CODES#O] (or if the view's window cannot be determined), the
- * image is obtained using [View#draw]. Otherwise, [PixelCopy] is used.
- *
- * This method will also enable [HardwareRendererCompat#setDrawingEnabled(boolean)] if required.
- *
- * This API is primarily intended for use in lower layer libraries or frameworks. For test authors,
- * its recommended to use espresso or compose's captureToImage.
- *
- * This API is currently experimental and subject to change or removal.
- */
-@ExperimentalTestApi
-@RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
-fun View.captureToBitmap(window: Window? = null): ListenableFuture<Bitmap> {
- val bitmapFuture: ResolvableFuture<Bitmap> = ResolvableFuture.create()
- val mainExecutor = HandlerExecutor(Handler(Looper.getMainLooper()))
- val isRobolectric = if (Build.FINGERPRINT.contains("robolectric")) true else false
-
- // disable drawing again if necessary once work is complete
- if (!HardwareRendererCompat.isDrawingEnabled()) {
- HardwareRendererCompat.setDrawingEnabled(true)
- bitmapFuture.addListener({ HardwareRendererCompat.setDrawingEnabled(false) }, mainExecutor)
- }
-
- mainExecutor.execute {
- if (isRobolectric) {
- generateBitmap(bitmapFuture)
- } else {
- val forceRedrawFuture = forceRedraw()
- forceRedrawFuture.addListener({ generateBitmap(bitmapFuture, window) }, mainExecutor)
- }
- }
-
- return bitmapFuture
-}
-
-/**
- * Synchronously captures an image of the view into a [Bitmap]. Synchronous equivalent of
- * [captureToBitmap].
- */
-@WorkerThread
-@ExperimentalTestApi
-@RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
-fun View.toBitmap(window: Window? = null): Bitmap {
- if (Looper.getMainLooper() == Looper.myLooper()) {
- error("toBitmap() can't be called from the main thread")
- }
-
- if (!HardwareRenderer.isDrawingEnabled()) {
- error("Hardware rendering is not enabled")
- }
-
- // Make sure we are idle.
- Espresso.onIdle()
-
- val mainExecutor = context.mainExecutor
- return runBlocking {
- suspendCoroutine { continuation ->
- Futures.addCallback(
- captureToBitmap(window),
- object : FutureCallback<Bitmap> {
- override fun onSuccess(result: Bitmap?) {
- continuation.resumeWith(Result.success(result!!))
- }
-
- override fun onFailure(t: Throwable) {
- continuation.resumeWith(Result.failure(t))
- }
- },
- // We know that we are not on the main thread, so we can block the current
- // thread and wait for the result in the main thread.
- mainExecutor,
- )
- }
- }
-}
-
-/**
- * Trigger a redraw of the given view.
- *
- * Should only be called on UI thread.
- *
- * @return a [ListenableFuture] that will be complete once ui drawing is complete
- */
-// NoClassDefFoundError occurs on API 15
-@RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
-// @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-@ExperimentalTestApi
-fun View.forceRedraw(): ListenableFuture<Void> {
- val future: ResolvableFuture<Void> = ResolvableFuture.create()
-
- if (Build.VERSION.SDK_INT >= 29 && isHardwareAccelerated) {
- viewTreeObserver.registerFrameCommitCallback() { future.set(null) }
- } else {
- viewTreeObserver.addOnDrawListener(
- object : ViewTreeObserver.OnDrawListener {
- var handled = false
- override fun onDraw() {
- if (!handled) {
- handled = true
- future.set(null)
- // cannot remove on draw listener inside of onDraw
- Handler(Looper.getMainLooper()).post {
- viewTreeObserver.removeOnDrawListener(this)
- }
- }
- }
- }
- )
- }
- invalidate()
- return future
-}
-
-private fun View.generateBitmap(
- bitmapFuture: ResolvableFuture<Bitmap>,
- window: Window? = null,
-) {
- if (bitmapFuture.isCancelled) {
- return
- }
- val destBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
- when {
- Build.VERSION.SDK_INT < 26 -> generateBitmapFromDraw(destBitmap, bitmapFuture)
- this is SurfaceView -> generateBitmapFromSurfaceViewPixelCopy(destBitmap, bitmapFuture)
- else -> {
- val window = window ?: getActivity()?.window
- if (window != null) {
- generateBitmapFromPixelCopy(window, destBitmap, bitmapFuture)
- } else {
- Log.i(
- "View.captureToImage",
- "Could not find window for view. Falling back to View#draw instead of PixelCopy"
- )
- generateBitmapFromDraw(destBitmap, bitmapFuture)
- }
- }
- }
-}
-
-@SuppressWarnings("NewApi")
-private fun SurfaceView.generateBitmapFromSurfaceViewPixelCopy(
- destBitmap: Bitmap,
- bitmapFuture: ResolvableFuture<Bitmap>
-) {
- val onCopyFinished =
- PixelCopy.OnPixelCopyFinishedListener { result ->
- if (result == PixelCopy.SUCCESS) {
- bitmapFuture.set(destBitmap)
- } else {
- bitmapFuture.setException(
- RuntimeException(String.format("PixelCopy failed: %d", result))
- )
- }
- }
- PixelCopy.request(this, null, destBitmap, onCopyFinished, handler)
-}
-
-internal fun View.generateBitmapFromDraw(
- destBitmap: Bitmap,
- bitmapFuture: ResolvableFuture<Bitmap>
-) {
- destBitmap.density = resources.displayMetrics.densityDpi
- computeScroll()
- val canvas = Canvas(destBitmap)
- canvas.translate((-scrollX).toFloat(), (-scrollY).toFloat())
- draw(canvas)
- bitmapFuture.set(destBitmap)
-}
-
-private fun View.getActivity(): Activity? {
- fun Context.getActivity(): Activity? {
- return when (this) {
- is Activity -> this
- is ContextWrapper -> this.baseContext.getActivity()
- else -> null
- }
- }
- return context.getActivity()
-}
-
-private fun View.generateBitmapFromPixelCopy(
- window: Window,
- destBitmap: Bitmap,
- bitmapFuture: ResolvableFuture<Bitmap>
-) {
- val locationInWindow = intArrayOf(0, 0)
- getLocationInWindow(locationInWindow)
- val x = locationInWindow[0]
- val y = locationInWindow[1]
- val boundsInWindow = Rect(x, y, x + width, y + height)
-
- return window.generateBitmapFromPixelCopy(boundsInWindow, destBitmap, bitmapFuture)
-}
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
deleted file mode 100644
index 070a45170d04..000000000000
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt
+++ /dev/null
@@ -1,188 +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.Activity
-import android.app.Dialog
-import android.graphics.Bitmap
-import android.os.Build
-import android.view.View
-import android.view.ViewGroup
-import android.view.ViewGroup.LayoutParams
-import android.view.ViewGroup.LayoutParams.MATCH_PARENT
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import androidx.activity.ComponentActivity
-import androidx.test.ext.junit.rules.ActivityScenarioRule
-import java.util.concurrent.TimeUnit
-import org.junit.Assert.assertEquals
-import org.junit.rules.RuleChain
-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
-import platform.test.screenshot.matchers.BitmapMatcher
-
-/** A rule for View screenshot diff unit tests. */
-open class ViewScreenshotTestRule(
- emulationSpec: DeviceEmulationSpec,
- private val matcher: BitmapMatcher = UnitTestBitmapMatcher,
- assetsPathRelativeToBuildRoot: String
-) : TestRule {
- private val colorsRule = MaterialYouColorsRule()
- private val deviceEmulationRule = DeviceEmulationRule(emulationSpec)
- protected val screenshotRule =
- ScreenshotTestRule(
- SystemUIGoldenImagePathManager(
- getEmulatedDevicePathConfig(emulationSpec),
- assetsPathRelativeToBuildRoot
- )
- )
- private val activityRule = ActivityScenarioRule(ScreenshotActivity::class.java)
- private val roboRule =
- RuleChain.outerRule(deviceEmulationRule).around(screenshotRule).around(activityRule)
- private val delegateRule = RuleChain.outerRule(colorsRule).around(roboRule)
- private val isRobolectric = if (Build.FINGERPRINT.contains("robolectric")) true else false
-
- override fun apply(base: Statement, description: Description): Statement {
- if (isRobolectric) {
- // In robolectric mode, we enable NATIVE graphics and unpack font and icu files.
- // We need to use reflection, as this library is only needed and therefore
- // only available in deviceless mode.
- val nativeLoaderClassName = "org.robolectric.nativeruntime.DefaultNativeRuntimeLoader"
- val defaultNativeRuntimeLoader = Class.forName(nativeLoaderClassName)
- System.setProperty("robolectric.graphicsMode", "NATIVE")
- defaultNativeRuntimeLoader.getMethod("injectAndLoad").invoke(null)
- }
- val ruleToApply = if (isRobolectric) roboRule else delegateRule
- return ruleToApply.apply(base, description)
- }
-
- protected fun takeScreenshot(
- mode: Mode = Mode.WrapContent,
- viewProvider: (ComponentActivity) -> View,
- beforeScreenshot: (ComponentActivity) -> Unit = {}
- ): Bitmap {
- activityRule.scenario.onActivity { activity ->
- // Make sure that the activity draws full screen and fits the whole display instead of
- // the system bars.
- val window = activity.window
- window.setDecorFitsSystemWindows(false)
-
- // Set the content.
- activity.setContentView(viewProvider(activity), mode.layoutParams)
-
- // Elevation/shadows is not deterministic when doing hardware rendering, so we disable
- // it for any view in the hierarchy.
- window.decorView.removeElevationRecursively()
-
- activity.currentFocus?.clearFocus()
- }
-
- // We call onActivity again because it will make sure that our Activity is done measuring,
- // laying out and drawing its content (that we set in the previous onActivity lambda).
- var contentView: View? = null
- activityRule.scenario.onActivity { activity ->
- // Check that the content is what we expected.
- val content = activity.requireViewById<ViewGroup>(android.R.id.content)
- assertEquals(1, content.childCount)
- contentView = content.getChildAt(0)
- beforeScreenshot(activity)
- }
-
- return if (isRobolectric) {
- contentView?.captureToBitmap()?.get(10, TimeUnit.SECONDS)
- ?: error("timeout while trying to capture view to bitmap")
- } else {
- contentView?.toBitmap() ?: error("contentView is null")
- }
- }
-
- /**
- * Compare the content of the view provided by [viewProvider] with the golden image identified
- * by [goldenIdentifier] in the context of [emulationSpec].
- */
- fun screenshotTest(
- goldenIdentifier: String,
- mode: Mode = Mode.WrapContent,
- beforeScreenshot: (ComponentActivity) -> Unit = {},
- viewProvider: (ComponentActivity) -> View
- ) {
- val bitmap = takeScreenshot(mode, viewProvider, beforeScreenshot)
- screenshotRule.assertBitmapAgainstGolden(
- bitmap,
- goldenIdentifier,
- matcher,
- )
- }
-
- /**
- * Compare the content of the dialog provided by [dialogProvider] with the golden image
- * identified by [goldenIdentifier] in the context of [emulationSpec].
- */
- fun dialogScreenshotTest(
- goldenIdentifier: String,
- dialogProvider: (Activity) -> Dialog,
- ) {
- var dialog: Dialog? = null
- activityRule.scenario.onActivity { activity ->
- dialog =
- dialogProvider(activity).apply {
- // Make sure that the dialog draws full screen and fits the whole display
- // instead of the system bars.
- window.setDecorFitsSystemWindows(false)
-
- // Disable enter/exit animations.
- create()
- window.setWindowAnimations(0)
-
- // Elevation/shadows is not deterministic when doing hardware rendering, so we
- // disable it for any view in the hierarchy.
- window.decorView.removeElevationRecursively()
-
- // Show the dialog.
- show()
- }
- }
-
- try {
- val bitmap = dialog?.toBitmap() ?: error("dialog is null")
- screenshotRule.assertBitmapAgainstGolden(
- bitmap,
- goldenIdentifier,
- matcher,
- )
- } finally {
- dialog?.dismiss()
- }
- }
-
- private fun Dialog.toBitmap(): Bitmap {
- val window = window
- return window.decorView.toBitmap(window)
- }
-
- enum class Mode(val layoutParams: LayoutParams) {
- WrapContent(LayoutParams(WRAP_CONTENT, WRAP_CONTENT)),
- MatchSize(LayoutParams(MATCH_PARENT, MATCH_PARENT)),
- MatchWidth(LayoutParams(MATCH_PARENT, WRAP_CONTENT)),
- MatchHeight(LayoutParams(WRAP_CONTENT, MATCH_PARENT)),
- }
-}
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/WindowCapture.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/WindowCapture.kt
deleted file mode 100644
index d34f46bf48a6..000000000000
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/WindowCapture.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.android.systemui.testing.screenshot
-
-import android.graphics.Bitmap
-import android.graphics.Rect
-import android.os.Handler
-import android.os.Looper
-import android.view.PixelCopy
-import android.view.Window
-import androidx.concurrent.futures.ResolvableFuture
-
-/*
- * This file was forked from androidx/test/core/view/WindowCapture.kt.
- * TODO(b/195673633): Remove this fork and use the AndroidX version instead.
- */
-fun Window.generateBitmapFromPixelCopy(
- boundsInWindow: Rect? = null,
- destBitmap: Bitmap,
- bitmapFuture: ResolvableFuture<Bitmap>
-) {
- val onCopyFinished =
- PixelCopy.OnPixelCopyFinishedListener { result ->
- if (result == PixelCopy.SUCCESS) {
- bitmapFuture.set(destBitmap)
- } else {
- bitmapFuture.setException(
- RuntimeException(String.format("PixelCopy failed: %d", result))
- )
- }
- }
- PixelCopy.request(
- this,
- boundsInWindow,
- destBitmap,
- onCopyFinished,
- Handler(Looper.getMainLooper())
- )
-}