Merge "[5/n] Create LetterboxConfiguration component" into main
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxConfiguration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxConfiguration.kt
new file mode 100644
index 0000000..3bcbf13
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxConfiguration.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2024 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.wm.shell.compatui.letterbox
+
+import android.annotation.ColorRes
+import android.content.Context
+import android.graphics.Color
+import com.android.internal.R
+import com.android.wm.shell.dagger.WMSingleton
+import javax.inject.Inject
+
+/**
+ * Contains configuration properties for the letterbox implementation in Shell.
+ */
+@WMSingleton
+class LetterboxConfiguration @Inject constructor(
+    private val context: Context
+) {
+
+    // TODO(b/370940685): Integrate CommandHandler in LetterboxConfiguration.
+    private var letterboxBackgroundColorOverride: Color? = Color.valueOf(Color.YELLOW)
+
+    // Color resource id for the solid color letterbox background type.
+    private var letterboxBackgroundColorResourceIdOverride: Int? = null
+
+    /**
+     * Sets color of letterbox background which is used when using the solid background mode.
+     */
+    fun setLetterboxBackgroundColor(color: Color) {
+        letterboxBackgroundColorOverride = color
+    }
+
+    /**
+     * Sets color ID of letterbox background which is used when using the solid background mode.
+     */
+    fun setLetterboxBackgroundColorResourceId(@ColorRes colorId: Int) {
+        letterboxBackgroundColorResourceIdOverride = colorId
+    }
+
+    /**
+     * Gets color of letterbox background which is used when the solid color mode is active.
+     */
+    fun getLetterboxBackgroundColor(): Color {
+        if (letterboxBackgroundColorOverride != null) {
+            return letterboxBackgroundColorOverride!!
+        }
+        val colorId = if (letterboxBackgroundColorResourceIdOverride != null) {
+            letterboxBackgroundColorResourceIdOverride
+        } else {
+            R.color.config_letterboxBackgroundColor
+        }
+        // Query color dynamically because material colors extracted from wallpaper are updated
+        // when wallpaper is changed.
+        return Color.valueOf(context.getResources().getColor(colorId!!, /* theme */null))
+    }
+
+    /**
+     * Resets color of letterbox background to the default.
+     */
+    fun resetLetterboxBackgroundColor() {
+        letterboxBackgroundColorOverride = null
+        letterboxBackgroundColorResourceIdOverride = null
+    }
+
+    /**
+     * The background color for the Letterbox.
+     */
+    fun getBackgroundColorRgbArray(): FloatArray = getLetterboxBackgroundColor().components
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt
index 406823d..0ac7aff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.compatui.letterbox
 
-import android.graphics.Color
 import android.graphics.Rect
 import android.view.SurfaceControl
 import com.android.internal.protolog.ProtoLog
@@ -28,7 +27,9 @@
  * Component responsible for handling the lifecycle of the letterbox surfaces.
  */
 @WMSingleton
-class LetterboxController @Inject constructor() {
+class LetterboxController @Inject constructor(
+    private val letterboxConfiguration: LetterboxConfiguration
+) {
 
     companion object {
         /*
@@ -64,8 +65,7 @@
                             this,
                             TASK_CHILD_LAYER_LETTERBOX_BACKGROUND
                         ).setColorSpaceAgnostic(this, true)
-                            // TODO(b/370940063): Implement LetterboxConfiguration
-                            .setColor(this, Color.valueOf(Color.YELLOW).components)
+                            .setColor(this, letterboxConfiguration.getBackgroundColorRgbArray())
                     }
             )
         })
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxConfigurationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxConfigurationTest.kt
new file mode 100644
index 0000000..75025d90
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxConfigurationTest.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2024 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.wm.shell.compatui.letterbox
+
+import android.annotation.ColorRes
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Color
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn
+import com.android.internal.R
+import com.android.wm.shell.ShellTestCase
+import java.util.function.Consumer
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+
+/**
+ * Tests for [LetterboxConfiguration].
+ *
+ * Build/Install/Run:
+ *  atest WMShellUnitTests:LetterboxConfigurationTest
+ */
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class LetterboxConfigurationTest : ShellTestCase() {
+
+    companion object {
+        @JvmStatic
+        val COLOR_WHITE = Color.valueOf(Color.WHITE)
+        @JvmStatic
+        val COLOR_RED = Color.valueOf(Color.RED)
+        @JvmStatic
+        val COLOR_BLACK = Color.valueOf(Color.BLACK)
+        @JvmStatic
+        val COLOR_WHITE_RESOURCE_ID = android.R.color.white
+        @JvmStatic
+        val COLOR_BLACK_RESOURCE_ID = android.R.color.black
+    }
+
+    @Test
+    fun `default background color is used if override is not set`() {
+        runTestScenario { r ->
+            r.setDefaultBackgroundColorId(COLOR_WHITE_RESOURCE_ID)
+            r.loadConfiguration()
+            r.checkBackgroundColor(COLOR_WHITE)
+        }
+    }
+
+    @Test
+    fun `overridden background color is used if set`() {
+        runTestScenario { r ->
+            r.setDefaultBackgroundColorId(COLOR_WHITE_RESOURCE_ID)
+            r.loadConfiguration()
+            r.overrideBackgroundColor(COLOR_RED)
+            r.checkBackgroundColor(COLOR_RED)
+        }
+    }
+
+    @Test
+    fun `overridden background color resource is used if set without override`() {
+        runTestScenario { r ->
+            r.setDefaultBackgroundColorId(COLOR_WHITE_RESOURCE_ID)
+            r.loadConfiguration()
+            r.overrideBackgroundColorId(COLOR_BLACK_RESOURCE_ID)
+            r.checkBackgroundColor(COLOR_BLACK)
+        }
+    }
+
+    @Test
+    fun `overridden background color has precedence over color id`() {
+        runTestScenario { r ->
+            r.setDefaultBackgroundColorId(COLOR_WHITE_RESOURCE_ID)
+            r.loadConfiguration()
+            r.overrideBackgroundColor(COLOR_RED)
+            r.overrideBackgroundColorId(COLOR_BLACK_RESOURCE_ID)
+            r.checkBackgroundColor(COLOR_RED)
+        }
+    }
+
+    @Test
+    fun `reset background color`() {
+        runTestScenario { r ->
+            r.setDefaultBackgroundColorId(COLOR_WHITE_RESOURCE_ID)
+            r.loadConfiguration()
+            r.overrideBackgroundColor(COLOR_RED)
+            r.checkBackgroundColor(COLOR_RED)
+
+            r.resetBackgroundColor()
+            r.checkBackgroundColor(COLOR_WHITE)
+
+            r.overrideBackgroundColorId(COLOR_BLACK_RESOURCE_ID)
+            r.checkBackgroundColor(COLOR_BLACK)
+
+            r.resetBackgroundColor()
+            r.checkBackgroundColor(COLOR_WHITE)
+        }
+    }
+
+    /**
+     * Runs a test scenario providing a Robot.
+     */
+    fun runTestScenario(consumer: Consumer<LetterboxConfigurationRobotTest>) {
+        val robot = LetterboxConfigurationRobotTest(mContext)
+        consumer.accept(robot)
+    }
+
+    class LetterboxConfigurationRobotTest(private val ctx: Context) {
+
+        private val resources: Resources
+        private lateinit var letterboxConfig: LetterboxConfiguration
+
+        init {
+            resources = ctx.resources
+            spyOn(resources)
+        }
+
+        fun setDefaultBackgroundColorId(@ColorRes colorId: Int) {
+            doReturn(colorId).`when`(resources)
+                .getColor(R.color.config_letterboxBackgroundColor, null)
+        }
+
+        fun loadConfiguration() {
+            letterboxConfig = LetterboxConfiguration(ctx)
+        }
+
+        fun overrideBackgroundColor(color: Color) {
+            letterboxConfig.setLetterboxBackgroundColor(color)
+        }
+
+        fun resetBackgroundColor() {
+            letterboxConfig.resetLetterboxBackgroundColor()
+        }
+
+        fun overrideBackgroundColorId(@ColorRes colorId: Int) {
+            letterboxConfig.setLetterboxBackgroundColorResourceId(colorId)
+        }
+
+        fun checkBackgroundColor(expected: Color) {
+            val colorComponents = letterboxConfig.getBackgroundColorRgbArray()
+            val expectedComponents = expected.components
+            assert(expectedComponents.contentEquals(colorComponents))
+        }
+    }
+}