commit 6fa79937124b4b2040631aec3b923ca0bd29d7ce
parent b120b6a27d198a8fbc03991db9c8d888016190ea
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Fri, 10 Nov 2023 21:24:05 +0100

feat(core/camera_tweaks): custom resolution

Diffstat:
Mapp/src/main/kotlin/me/rhunk/snapenhance/ui/util/AlertDialogs.kt | 19+++++++++++--------
Mcommon/src/main/assets/lang/en_US.json | 8++++++++
Mcommon/src/main/kotlin/me/rhunk/snapenhance/common/config/ConfigObjects.kt | 3++-
Mcommon/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Camera.kt | 6++++--
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/tweaks/CameraTweaks.kt | 17++++++++++-------
5 files changed, 35 insertions(+), 18 deletions(-)

diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/util/AlertDialogs.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/util/AlertDialogs.kt @@ -176,7 +176,7 @@ class AlertDialogs( val focusRequester = remember { FocusRequester() } DefaultDialogCard { - val fieldValue = remember { + var fieldValue by remember { mutableStateOf(property.value.get().toString().let { TextFieldValue( text = it, @@ -193,10 +193,8 @@ class AlertDialogs( focusRequester.requestFocus() } .focusRequester(focusRequester), - value = fieldValue.value, - onValueChange = { - fieldValue.value = it - }, + value = fieldValue, + onValueChange = { fieldValue = it }, keyboardOptions = when (property.key.dataType.type) { DataProcessors.Type.INTEGER -> KeyboardOptions(keyboardType = KeyboardType.Number) DataProcessors.Type.FLOAT -> KeyboardOptions(keyboardType = KeyboardType.Decimal) @@ -215,22 +213,27 @@ class AlertDialogs( Text(text = translation["button.cancel"]) } Button(onClick = { + if (fieldValue.text.isNotEmpty() && property.key.params.inputCheck?.invoke(fieldValue.text) == false) { + dismiss() + return@Button + } + when (property.key.dataType.type) { DataProcessors.Type.INTEGER -> { runCatching { - property.value.setAny(fieldValue.value.text.toInt()) + property.value.setAny(fieldValue.text.toInt()) }.onFailure { property.value.setAny(0) } } DataProcessors.Type.FLOAT -> { runCatching { - property.value.setAny(fieldValue.value.text.toFloat()) + property.value.setAny(fieldValue.text.toFloat()) }.onFailure { property.value.setAny(0f) } } - else -> property.value.setAny(fieldValue.value.text) + else -> property.value.setAny(fieldValue.text) } dismiss() }) { diff --git a/common/src/main/assets/lang/en_US.json b/common/src/main/assets/lang/en_US.json @@ -454,6 +454,14 @@ "name": "Override Picture Resolution", "description": "Overrides the picture resolution" }, + "custom_preview_resolution": { + "name": "Custom Preview Resolution", + "description": "Sets a custom camera preview resolution, width x height (e.g. 1920x1080).\nThe custom resolution must be supported by your device" + }, + "custom_picture_resolution": { + "name": "Custom Picture Resolution", + "description": "Sets a custom picture resolution, width x height (e.g. 1920x1080).\nThe custom resolution must be supported by your device" + }, "custom_frame_rate": { "name": "Custom Frame Rate", "description": "Overrides the camera frame rate" diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/config/ConfigObjects.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/config/ConfigObjects.kt @@ -38,7 +38,8 @@ class ConfigParams( var icon: String? = null, var disabledKey: String? = null, var customTranslationPath: String? = null, - var customOptionTranslationPath: String? = null + var customOptionTranslationPath: String? = null, + var inputCheck: ((String) -> Boolean)? = { true }, ) { val notices get() = _notices?.let { FeatureNotice.entries.filter { flag -> it and flag.id != 0 } } ?: emptyList() val flags get() = _flags?.let { ConfigFlag.entries.filter { flag -> it and flag.id != 0 } } ?: emptyList() diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Camera.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Camera.kt @@ -41,10 +41,12 @@ class Camera : ConfigContainer() { val disable = boolean("disable_camera") val immersiveCameraPreview = boolean("immersive_camera_preview") { addNotices(FeatureNotice.UNSTABLE) } val blackPhotos = boolean("black_photos") - val overridePreviewResolution get() = _overridePreviewResolution - val overridePictureResolution get() = _overridePictureResolution val customFrameRate = unique("custom_frame_rate", "5", "10", "20", "25", "30", "48", "60", "90", "120" ) { addNotices(FeatureNotice.UNSTABLE); addFlags(ConfigFlag.NO_TRANSLATE) } val forceCameraSourceEncoding = boolean("force_camera_source_encoding") + val overridePreviewResolution get() = _overridePreviewResolution + val overridePictureResolution get() = _overridePictureResolution + val customPreviewResolution = string("custom_preview_resolution") { addNotices(FeatureNotice.UNSTABLE); inputCheck = { it.matches(Regex("\\d+x\\d+")) } } + val customPictureResolution = string("custom_picture_resolution") { addNotices(FeatureNotice.UNSTABLE); inputCheck = { it.matches(Regex("\\d+x\\d+")) } } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/tweaks/CameraTweaks.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/tweaks/CameraTweaks.kt @@ -22,13 +22,14 @@ import java.nio.ByteBuffer class CameraTweaks : Feature("Camera Tweaks", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) { - private fun parseResolution(resolution: String): IntArray { - return resolution.split("x").map { it.toInt() }.toIntArray() + private fun parseResolution(resolution: String): IntArray? { + return runCatching { resolution.split("x").map { it.toInt() }.toIntArray() }.getOrNull() } @SuppressLint("MissingPermission", "DiscouragedApi") override fun onActivityCreate() { - if (context.config.camera.disable.get()) { + val config = context.config.camera + if (config.disable.get()) { ContextWrapper::class.java.hook("checkPermission", HookStage.BEFORE) { param -> val permission = param.arg<String>(0) if (permission == Manifest.permission.CAMERA) { @@ -41,10 +42,12 @@ class CameraTweaks : Feature("Camera Tweaks", loadParams = FeatureLoadParams.ACT } } - val previewResolutionConfig = context.config.camera.overridePreviewResolution.getNullable()?.let { parseResolution(it) } - val captureResolutionConfig = context.config.camera.overridePictureResolution.getNullable()?.let { parseResolution(it) } + val previewResolutionConfig = config.customPreviewResolution.getNullable()?.takeIf { it.isNotEmpty() }?.let { parseResolution(it) } + ?: config.overridePreviewResolution.getNullable()?.let { parseResolution(it) } + val captureResolutionConfig = config.customPictureResolution.getNullable()?.takeIf { it.isNotEmpty() }?.let { parseResolution(it) } + ?: config.overridePictureResolution.getNullable()?.let { parseResolution(it) } - context.config.camera.customFrameRate.getNullable()?.also { value -> + config.customFrameRate.getNullable()?.also { value -> val customFrameRate = value.toInt() CameraCharacteristics::class.java.hook("get", HookStage.AFTER) { param -> val key = param.arg<Key<*>>(0) @@ -76,7 +79,7 @@ class CameraTweaks : Feature("Camera Tweaks", loadParams = FeatureLoadParams.ACT } } - if (context.config.camera.blackPhotos.get()) { + if (config.blackPhotos.get()) { findClass("android.media.ImageReader\$SurfaceImage").hook("getPlanes", HookStage.AFTER) { param -> val image = param.thisObject() as? Image ?: return@hook val planes = param.getResult() as? Array<*> ?: return@hook