Merge "Add test_app.test_android_app_dialog_has_dimmed_background." into emu-master-dev
diff --git a/pytest/test_embedded/AnimateBox/app/build.gradle b/pytest/test_embedded/AnimateBox/app/build.gradle
index 10b87d8..efad9f1 100644
--- a/pytest/test_embedded/AnimateBox/app/build.gradle
+++ b/pytest/test_embedded/AnimateBox/app/build.gradle
@@ -1,7 +1,6 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
- id 'kotlin-android-extensions'
id 'com.google.protobuf'
}
@@ -40,6 +39,12 @@
kotlinOptions {
jvmTarget = '1.8'
}
+ buildFeatures {
+ compose true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion '1.5.1'
+ }
namespace 'com.google.emu'
}
@@ -57,6 +62,11 @@
implementation 'javax.annotation:javax.annotation-api:1.2'
implementation 'com.google.protobuf:protobuf-java:' + rootProject.protocVersion
implementation 'com.google.protobuf:protobuf-java-util:' + rootProject.protocVersion
+
+ implementation 'androidx.activity:activity-compose:1.8.1'
+ implementation 'androidx.compose.foundation:foundation-android:1.5.4'
+ implementation 'androidx.compose.material3:material3:1.1.2'
+ implementation 'androidx.compose.ui:ui-tooling-android:1.5.4'
}
protobuf {
diff --git a/pytest/test_embedded/AnimateBox/app/src/main/AndroidManifest.xml b/pytest/test_embedded/AnimateBox/app/src/main/AndroidManifest.xml
index 9f10baf..2c24418 100644
--- a/pytest/test_embedded/AnimateBox/app/src/main/AndroidManifest.xml
+++ b/pytest/test_embedded/AnimateBox/app/src/main/AndroidManifest.xml
@@ -37,6 +37,12 @@
android:name=".ClipActivity"
android:exported="true"
android:label="@string/clip_activity_label"></activity>
+
+ <!-- Activity to test dialog background dimming -->
+ <activity
+ android:name=".DialogDimActivity"
+ android:exported="true"
+ android:label="@string/dialog_dim_activity_label"></activity>
</application>
<instrumentation
android:name="com.google.android.mobly.snippet.SnippetRunner"
diff --git a/pytest/test_embedded/AnimateBox/app/src/main/java/com/google/emu/DialogDimActivity.kt b/pytest/test_embedded/AnimateBox/app/src/main/java/com/google/emu/DialogDimActivity.kt
new file mode 100644
index 0000000..7102318
--- /dev/null
+++ b/pytest/test_embedded/AnimateBox/app/src/main/java/com/google/emu/DialogDimActivity.kt
@@ -0,0 +1,73 @@
+// Copyright 2023 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.google.emu
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.window.Dialog
+import androidx.compose.ui.window.DialogWindowProvider
+
+class DialogDimActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ // A surface container using the 'background' color from the theme
+ Surface(
+ modifier = Modifier.fillMaxSize(),
+ color = Color(0, 0, 255, 255)
+ ) {
+ Text(
+ text = "Background should not be #0000ff when background dimming is" +
+ " enabled while showing the dialog.",
+ color = Color.Red,
+ )
+
+ var hideDialog = false
+ if (intent != null && intent.extras != null) {
+ val value = intent.getStringExtra("hideDialog")
+
+ // hide dialog if hideDialog intent extra has content.
+ if (value != null) {
+ hideDialog = true
+ }
+ }
+ if (!hideDialog) {
+ DialogNoContent()
+ }
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+fun DialogNoContent() {
+ Dialog(onDismissRequest = {}) {
+ // This modifies the alpha value of the dimming background from the dialog.
+ // To validate that the dimming works, we need to set it to a value d that is
+ // 0 < d < 1.0. From testing, setting it to 1.0 does not trigger
+ // HWC2_COMPOSITION_SOLID_COLOR.
+ (LocalView.current.parent as DialogWindowProvider)?.window?.setDimAmount(0.8f)
+ }
+}
\ No newline at end of file
diff --git a/pytest/test_embedded/AnimateBox/app/src/main/res/values/strings.xml b/pytest/test_embedded/AnimateBox/app/src/main/res/values/strings.xml
index f3586c9..34d5f6d 100644
--- a/pytest/test_embedded/AnimateBox/app/src/main/res/values/strings.xml
+++ b/pytest/test_embedded/AnimateBox/app/src/main/res/values/strings.xml
@@ -1,4 +1,5 @@
<resources>
<string name="app_name">AnimateBox</string>
<string name="clip_activity_label">Clipboard Activity</string>
+ <string name="dialog_dim_activity_label">Dialog Dim Activity</string>
</resources>
\ No newline at end of file
diff --git a/pytest/test_embedded/AnimateBox/build.gradle b/pytest/test_embedded/AnimateBox/build.gradle
index 7518fa4..d44e821 100644
--- a/pytest/test_embedded/AnimateBox/build.gradle
+++ b/pytest/test_embedded/AnimateBox/build.gradle
@@ -1,6 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '1.6.21'
+ ext.kotlin_version = '1.9.0'
repositories {
google()
jcenter()
diff --git a/pytest/test_embedded/src/emu/apk/app-release.apk b/pytest/test_embedded/src/emu/apk/app-release.apk
index 7ce245a..633c6a6 100644
--- a/pytest/test_embedded/src/emu/apk/app-release.apk
+++ b/pytest/test_embedded/src/emu/apk/app-release.apk
Binary files differ
diff --git a/pytest/test_embedded/src/emu/emulator.py b/pytest/test_embedded/src/emu/emulator.py
index 92932f1..005732b 100644
--- a/pytest/test_embedded/src/emu/emulator.py
+++ b/pytest/test_embedded/src/emu/emulator.py
@@ -219,7 +219,7 @@
return self.adb.is_installed(package_name)
- def start_activity(self, activity: str, params) -> bool:
+ def start_activity(self, activity: str, params=None) -> bool:
"""Attempts to start the given activity.
An activity is considered to be running when the activity is in the list returned
diff --git a/pytest/test_embedded/tests/graphics/test_app.py b/pytest/test_embedded/tests/graphics/test_app.py
new file mode 100644
index 0000000..ed0842c
--- /dev/null
+++ b/pytest/test_embedded/tests/graphics/test_app.py
@@ -0,0 +1,103 @@
+# Copyright 2023 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.
+import pytest
+import logging
+from time import sleep
+from aemu.proto.emulator_controller_pb2 import ImageFormat
+
+from emu.timing import eventually, wait_until
+
+@pytest.mark.e2e
+@pytest.mark.timeout(timeout=60, func_only=True)
+@pytest.mark.graphics
+def test_android_app_dialog_has_dimmed_background(avd, get_screenshot):
+ """
+ Test for b/315308358.
+
+ This test launches com.google.emu.AnimateBox/.DialogDimActivity on an Android device, which has
+ a blank blue (#0000ff) surface, with a app Dialog with no content over it. If dialog dimming
+ works, then most of the pixels will have a darker shade of blue.
+
+ Note: We need to keep the percentage of blue we check to not too high (60% should be okay),
+ because of random system dialogs that can popup (e.g. Bluetooth keeps stopping).
+ """
+ def get_blue_pixel_percent(min_blue, max_blue):
+ """Helper function to get the percentage of blue pixels that meet the criteria
+ `min_blue <= x <= max_blue`.
+
+ Args:
+ min_blue (int): the minimum blue value. Must be between 0 and 255.
+ max_blue (int): the maximum blue value. Must be between 0 and 255.
+
+ Returns:
+ float: The percentage (0.0 to 1.0) of blue pixels that meet the above criteria.
+ """
+ _, rgb_image = get_screenshot(ImageFormat())
+ rgb_image = rgb_image.convert("RGB")
+
+ blue_count = 0
+ # Iterate over each pixel in the image
+ for x in range(rgb_image.width):
+ for y in range(rgb_image.height):
+ # Get the RGB values of the pixel at (x, y)
+ r, g, b = rgb_image.getpixel((x, y))
+
+ # Check if the pixel corresponds to the desired shade of blue
+ if r == 0 and g == 0 and b >= min_blue and b <= max_blue:
+ blue_count += 1
+ logging.info("blue pixel percent=%f", float(blue_count) / (rgb_image.width * rgb_image.height))
+ return float(blue_count) / (rgb_image.width * rgb_image.height)
+
+ # Start DialogDimActivity with no dialog showing.
+ avd.stop_activity("com.google.AnimateBox")
+ avd.start_activity("com.google.AnimateBox/com.google.emu.DialogDimActivity",
+ '--es "hideDialog" "true"')
+ # Give the system some time to start the activity
+ sleep(5)
+
+ # Without the dialog, most of the display should be blue (>= 60%).
+ def at_least_60percent_blue():
+ return get_blue_pixel_percent(240, 255) >= 0.6
+
+ max_retries = 3
+ passed = False
+ for _ in range(0, max_retries):
+ if wait_until(at_least_60percent_blue, timeout=5):
+ passed = True
+ break
+
+ assert (
+ passed
+ ), f"Did not see a screenshot with at least 60%% blue pixels with {max_retries} retries"
+
+ # Start DialogDimActivity with the dialog showing.
+ avd.stop_activity("com.google.AnimateBox")
+ avd.start_activity("com.google.AnimateBox/com.google.emu.DialogDimActivity")
+ # Give the system some time to start the activity
+ sleep(5)
+
+ # With the dialog, the blue will now become a darker shade.
+ def at_least_60percent_dark_blue():
+ return get_blue_pixel_percent(20, 100) >= 0.6
+
+ max_retries = 3
+ passed = False
+ for _ in range(0, max_retries):
+ if wait_until(at_least_60percent_dark_blue, timeout=5):
+ passed = True
+ break
+
+ assert (
+ passed
+ ), f"Did not see a screenshot with at most 60%% dark blue pixels with {max_retries} retries"