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"