Aperture: Extract overlay configuration into a new class

Change-Id: Ie6f158eb8ef2657b18bc26ce87ed89f94238225c
diff --git a/app/src/main/java/org/lineageos/aperture/CameraActivity.kt b/app/src/main/java/org/lineageos/aperture/CameraActivity.kt
index 7f1cfe4..bc22892 100644
--- a/app/src/main/java/org/lineageos/aperture/CameraActivity.kt
+++ b/app/src/main/java/org/lineageos/aperture/CameraActivity.kt
@@ -1524,7 +1524,7 @@
                         )
                     )
                     .setAllowedResolutionMode(
-                        if (cameraManager.enableHighResolution) {
+                        if (cameraManager.overlayConfiguration.enableHighResolution) {
                             ResolutionSelector.PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE
                         } else {
                             ResolutionSelector.PREFER_CAPTURE_RATE_OVER_HIGHER_RESOLUTION
diff --git a/app/src/main/java/org/lineageos/aperture/camera/CameraManager.kt b/app/src/main/java/org/lineageos/aperture/camera/CameraManager.kt
index f07db70..e71f284 100644
--- a/app/src/main/java/org/lineageos/aperture/camera/CameraManager.kt
+++ b/app/src/main/java/org/lineageos/aperture/camera/CameraManager.kt
@@ -10,15 +10,12 @@
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.video.Quality
 import androidx.camera.view.LifecycleCameraController
-import org.lineageos.aperture.R
-import org.lineageos.aperture.ext.*
 import org.lineageos.aperture.models.CameraFacing
 import org.lineageos.aperture.models.CameraMode
 import org.lineageos.aperture.models.CameraType
-import org.lineageos.aperture.models.FrameRate
+import org.lineageos.aperture.utils.OverlayConfiguration
 import java.util.concurrent.ExecutorService
 import java.util.concurrent.Executors
-import kotlin.math.absoluteValue
 
 /**
  * Class managing an app camera session
@@ -31,80 +28,7 @@
     val cameraController = LifecycleCameraController(context)
     val cameraExecutor: ExecutorService = Executors.newSingleThreadExecutor()
 
-    private val additionalVideoConfigurations by lazy {
-        mutableMapOf<String, MutableMap<Quality, MutableMap<FrameRate, Boolean>>>().apply {
-            context.resources.getStringArray(context, R.array.config_additionalVideoConfigurations)
-                .let {
-                    if (it.size % 3 != 0) {
-                        // Invalid configuration
-                        return@apply
-                    }
-
-                    for (i in it.indices step 3) {
-                        val cameraId = it[i]
-                        val frameRates = it[i + 2].split("|").mapNotNull { frameRate ->
-                            FrameRate.fromValue(frameRate.toInt().absoluteValue)?.let { value ->
-                                value to frameRate.startsWith('-')
-                            }
-                        }.toMap()
-
-                        it[i + 1].split("|").mapNotNull { quality ->
-                            when (quality) {
-                                "sd" -> Quality.SD
-                                "hd" -> Quality.HD
-                                "fhd" -> Quality.FHD
-                                "uhd" -> Quality.UHD
-                                else -> null
-                            }
-                        }.distinct().forEach { quality ->
-                            getOrCreate(cameraId).apply {
-                                getOrCreate(quality).apply {
-                                    putAll(frameRates)
-                                }
-                            }
-                        }
-                    }
-                }
-        }.map { a ->
-            a.key to a.value.map { b ->
-                b.key to b.value.toList()
-            }.toMap()
-        }.toMap()
-    }
-    private val enableAuxCameras by lazy {
-        context.resources.getBoolean(context, R.bool.config_enableAuxCameras)
-    }
-    private val ignoredAuxCameraIds by lazy {
-        context.resources.getStringArray(context, R.array.config_ignoredAuxCameraIds)
-    }
-    private val ignoreLogicalAuxCameras by lazy {
-        context.resources.getBoolean(context, R.bool.config_ignoreLogicalAuxCameras)
-    }
-    private val logicalZoomRatios by lazy {
-        mutableMapOf<String, MutableMap<Float, Float>>().apply {
-            context.resources.getStringArray(context, R.array.config_logicalZoomRatios).let {
-                if (it.size % 3 != 0) {
-                    // Invalid configuration
-                    return@apply
-                }
-
-                for (i in it.indices step 3) {
-                    val cameraId = it[i]
-                    val approximateZoomRatio = it[i + 1].toFloat()
-                    val exactZoomRatio = it[i + 2].toFloat()
-
-                    getOrCreate(cameraId).apply {
-                        this[approximateZoomRatio] = exactZoomRatio
-                    }
-                }
-            }
-        }.map { a ->
-            a.key to a.value.toMap()
-        }.toMap()
-    }
-    val enableHighResolution by lazy {
-        context.resources.getBoolean(context, R.bool.config_enableHighResolution)
-    }
+    val overlayConfiguration = OverlayConfiguration(context)
 
     private val cameras: List<Camera>
         get() = cameraProvider.availableCameraInfos.map {
@@ -113,7 +37,8 @@
 
     // We expect device cameras to never change
     private val internalCameras = cameras.filter {
-        it.cameraType == CameraType.INTERNAL && !ignoredAuxCameraIds.contains(it.cameraId)
+        it.cameraType == CameraType.INTERNAL
+                && !overlayConfiguration.ignoredAuxCameraIds.contains(it.cameraId)
     }
 
     private val backCameras = prepareDeviceCamerasList(CameraFacing.BACK)
@@ -151,10 +76,10 @@
         get() = availableCameras.filter { it.supportsVideoRecording }
 
     fun getAdditionalVideoFrameRates(cameraId: String, quality: Quality) =
-        additionalVideoConfigurations[cameraId]?.get(quality) ?: setOf()
+        overlayConfiguration.additionalVideoConfigurations[cameraId]?.get(quality) ?: setOf()
 
     fun getLogicalZoomRatios(cameraId: String) = mutableMapOf(1.0f to 1.0f).apply {
-        logicalZoomRatios[cameraId]?.let {
+        overlayConfiguration.logicalZoomRatios[cameraId]?.let {
             putAll(it)
         }
     }.toSortedMap()
@@ -253,7 +178,7 @@
 
         val mainCamera = facingCameras.first()
 
-        if (!enableAuxCameras) {
+        if (!overlayConfiguration.enableAuxCameras) {
             // Return only the main camera
             return listOf(mainCamera)
         }
@@ -261,7 +186,7 @@
         // Get the list of aux cameras
         val auxCameras = facingCameras
             .drop(1)
-            .filter { !ignoreLogicalAuxCameras || !it.isLogical }
+            .filter { !overlayConfiguration.ignoreLogicalAuxCameras || !it.isLogical }
 
         return listOf(mainCamera) + auxCameras
     }
diff --git a/app/src/main/java/org/lineageos/aperture/utils/OverlayConfiguration.kt b/app/src/main/java/org/lineageos/aperture/utils/OverlayConfiguration.kt
new file mode 100644
index 0000000..3ff4cf9
--- /dev/null
+++ b/app/src/main/java/org/lineageos/aperture/utils/OverlayConfiguration.kt
@@ -0,0 +1,111 @@
+/*
+ * SPDX-FileCopyrightText: 2024 The LineageOS Project
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.lineageos.aperture.utils
+
+import android.content.Context
+import androidx.camera.video.Quality
+import org.lineageos.aperture.R
+import org.lineageos.aperture.ext.getBoolean
+import org.lineageos.aperture.ext.getOrCreate
+import org.lineageos.aperture.ext.getStringArray
+import org.lineageos.aperture.models.FrameRate
+import kotlin.math.absoluteValue
+
+/**
+ * Overlay configuration manager.
+ */
+class OverlayConfiguration(context: Context) {
+    private val resources = context.resources
+
+    /**
+     * @see R.bool.config_enableAuxCameras
+     */
+    val enableAuxCameras = resources.getBoolean(context, R.bool.config_enableAuxCameras)
+
+    /**
+     * @see R.array.config_ignoredAuxCameraIds
+     */
+    val ignoredAuxCameraIds = resources.getStringArray(context, R.array.config_ignoredAuxCameraIds)
+
+    /**
+     * @see R.bool.config_ignoreLogicalAuxCameras
+     */
+    val ignoreLogicalAuxCameras = resources.getBoolean(
+        context, R.bool.config_ignoreLogicalAuxCameras
+    )
+
+    /**
+     * @see R.array.config_additionalVideoConfigurations
+     */
+    val additionalVideoConfigurations =
+        mutableMapOf<String, MutableMap<Quality, MutableMap<FrameRate, Boolean>>>().apply {
+            resources.getStringArray(context, R.array.config_additionalVideoConfigurations)
+                .let {
+                    if (it.size % 3 != 0) {
+                        // Invalid configuration
+                        return@apply
+                    }
+
+                    for (i in it.indices step 3) {
+                        val cameraId = it[i]
+                        val frameRates = it[i + 2].split("|").mapNotNull { frameRate ->
+                            FrameRate.fromValue(frameRate.toInt().absoluteValue)?.let { value ->
+                                value to frameRate.startsWith('-')
+                            }
+                        }.toMap()
+
+                        it[i + 1].split("|").mapNotNull { quality ->
+                            when (quality) {
+                                "sd" -> Quality.SD
+                                "hd" -> Quality.HD
+                                "fhd" -> Quality.FHD
+                                "uhd" -> Quality.UHD
+                                else -> null
+                            }
+                        }.distinct().forEach { quality ->
+                            getOrCreate(cameraId).apply {
+                                getOrCreate(quality).apply {
+                                    putAll(frameRates)
+                                }
+                            }
+                        }
+                    }
+                }
+        }.map { a ->
+            a.key to a.value.map { b ->
+                b.key to b.value.toList()
+            }.toMap()
+        }.toMap()
+
+    /**
+     * @see R.array.config_logicalZoomRatios
+     */
+    val logicalZoomRatios = mutableMapOf<String, MutableMap<Float, Float>>().apply {
+        resources.getStringArray(context, R.array.config_logicalZoomRatios).let {
+            if (it.size % 3 != 0) {
+                // Invalid configuration
+                return@apply
+            }
+
+            for (i in it.indices step 3) {
+                val cameraId = it[i]
+                val approximateZoomRatio = it[i + 1].toFloat()
+                val exactZoomRatio = it[i + 2].toFloat()
+
+                getOrCreate(cameraId).apply {
+                    this[approximateZoomRatio] = exactZoomRatio
+                }
+            }
+        }
+    }.map { a ->
+        a.key to a.value.toMap()
+    }.toMap()
+
+    /**
+     * @see R.bool.config_enableHighResolution
+     */
+    val enableHighResolution = resources.getBoolean(context, R.bool.config_enableHighResolution)
+}