diff --git a/apps/android/app/src/main/java/ai/openclaw/android/node/CameraCaptureManager.kt b/apps/android/app/src/main/java/ai/openclaw/android/node/CameraCaptureManager.kt index 87572b37ad8..67241ef2ef7 100644 --- a/apps/android/app/src/main/java/ai/openclaw/android/node/CameraCaptureManager.kt +++ b/apps/android/app/src/main/java/ai/openclaw/android/node/CameraCaptureManager.kt @@ -33,10 +33,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withTimeout import kotlinx.coroutines.withContext -import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.JsonPrimitive -import kotlinx.serialization.json.contentOrNull import java.io.ByteArrayOutputStream import java.io.File import java.util.concurrent.Executor @@ -101,7 +98,7 @@ class CameraCaptureManager(private val context: Context) { withContext(Dispatchers.Main) { ensureCameraPermission() val owner = lifecycleOwner ?: throw IllegalStateException("UNAVAILABLE: camera not ready") - val params = parseParamsObject(paramsJson) + val params = parseJsonParamsObject(paramsJson) val facing = parseFacing(params) ?: "front" val quality = (parseQuality(params) ?: 0.95).coerceIn(0.1, 1.0) val maxWidth = parseMaxWidth(params) ?: 1600 @@ -167,7 +164,7 @@ class CameraCaptureManager(private val context: Context) { withContext(Dispatchers.Main) { ensureCameraPermission() val owner = lifecycleOwner ?: throw IllegalStateException("UNAVAILABLE: camera not ready") - val params = parseParamsObject(paramsJson) + val params = parseJsonParamsObject(paramsJson) val facing = parseFacing(params) ?: "front" val durationMs = (parseDurationMs(params) ?: 3_000).coerceIn(200, 60_000) val includeAudio = parseIncludeAudio(params) ?: true @@ -293,20 +290,8 @@ class CameraCaptureManager(private val context: Context) { return rotated } - private fun parseParamsObject(paramsJson: String?): JsonObject? { - if (paramsJson.isNullOrBlank()) return null - return try { - Json.parseToJsonElement(paramsJson).asObjectOrNull() - } catch (_: Throwable) { - null - } - } - - private fun readPrimitive(params: JsonObject?, key: String): JsonPrimitive? = - params?.get(key) as? JsonPrimitive - private fun parseFacing(params: JsonObject?): String? { - val value = readPrimitive(params, "facing")?.contentOrNull?.trim()?.lowercase() ?: return null + val value = parseJsonString(params, "facing")?.trim()?.lowercase() ?: return null return when (value) { "front", "back" -> value else -> null @@ -314,31 +299,21 @@ class CameraCaptureManager(private val context: Context) { } private fun parseQuality(params: JsonObject?): Double? = - readPrimitive(params, "quality")?.contentOrNull?.toDoubleOrNull() + parseJsonDouble(params, "quality") private fun parseMaxWidth(params: JsonObject?): Int? = - readPrimitive(params, "maxWidth") - ?.contentOrNull - ?.toIntOrNull() + parseJsonInt(params, "maxWidth") ?.takeIf { it > 0 } private fun parseDurationMs(params: JsonObject?): Int? = - readPrimitive(params, "durationMs")?.contentOrNull?.toIntOrNull() + parseJsonInt(params, "durationMs") private fun parseDeviceId(params: JsonObject?): String? = - readPrimitive(params, "deviceId") - ?.contentOrNull + parseJsonString(params, "deviceId") ?.trim() ?.takeIf { it.isNotEmpty() } - private fun parseIncludeAudio(params: JsonObject?): Boolean? { - val value = readPrimitive(params, "includeAudio")?.contentOrNull?.trim()?.lowercase() - return when (value) { - "true" -> true - "false" -> false - else -> null - } - } + private fun parseIncludeAudio(params: JsonObject?): Boolean? = parseJsonBooleanFlag(params, "includeAudio") private fun Context.mainExecutor(): Executor = ContextCompat.getMainExecutor(this) diff --git a/apps/android/app/src/main/java/ai/openclaw/android/node/NodeUtils.kt b/apps/android/app/src/main/java/ai/openclaw/android/node/NodeUtils.kt index c3f463174a4..5ba58c23860 100644 --- a/apps/android/app/src/main/java/ai/openclaw/android/node/NodeUtils.kt +++ b/apps/android/app/src/main/java/ai/openclaw/android/node/NodeUtils.kt @@ -1,10 +1,12 @@ package ai.openclaw.android.node import ai.openclaw.android.gateway.parseInvokeErrorFromThrowable +import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonNull import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.contentOrNull const val DEFAULT_SEAM_COLOR_ARGB: Long = 0xFF4F7A9A @@ -21,6 +23,35 @@ fun String.toJsonString(): String { fun JsonElement?.asObjectOrNull(): JsonObject? = this as? JsonObject +fun parseJsonParamsObject(paramsJson: String?): JsonObject? { + if (paramsJson.isNullOrBlank()) return null + return try { + Json.parseToJsonElement(paramsJson).asObjectOrNull() + } catch (_: Throwable) { + null + } +} + +fun readJsonPrimitive(params: JsonObject?, key: String): JsonPrimitive? = params?.get(key) as? JsonPrimitive + +fun parseJsonInt(params: JsonObject?, key: String): Int? = + readJsonPrimitive(params, key)?.contentOrNull?.toIntOrNull() + +fun parseJsonDouble(params: JsonObject?, key: String): Double? = + readJsonPrimitive(params, key)?.contentOrNull?.toDoubleOrNull() + +fun parseJsonString(params: JsonObject?, key: String): String? = + readJsonPrimitive(params, key)?.contentOrNull + +fun parseJsonBooleanFlag(params: JsonObject?, key: String): Boolean? { + val value = readJsonPrimitive(params, key)?.contentOrNull?.trim()?.lowercase() ?: return null + return when (value) { + "true" -> true + "false" -> false + else -> null + } +} + fun JsonElement?.asStringOrNull(): String? = when (this) { is JsonNull -> null diff --git a/apps/android/app/src/main/java/ai/openclaw/android/node/ScreenRecordManager.kt b/apps/android/app/src/main/java/ai/openclaw/android/node/ScreenRecordManager.kt index 98a3e4d9593..bb06d1200e4 100644 --- a/apps/android/app/src/main/java/ai/openclaw/android/node/ScreenRecordManager.kt +++ b/apps/android/app/src/main/java/ai/openclaw/android/node/ScreenRecordManager.kt @@ -10,10 +10,7 @@ import ai.openclaw.android.ScreenCaptureRequester import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.withContext -import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.JsonPrimitive -import kotlinx.serialization.json.contentOrNull import java.io.File import kotlin.math.roundToInt @@ -39,7 +36,7 @@ class ScreenRecordManager(private val context: Context) { "SCREEN_PERMISSION_REQUIRED: grant Screen Recording permission", ) - val params = parseParamsObject(paramsJson) + val params = parseJsonParamsObject(paramsJson) val durationMs = (parseDurationMs(params) ?: 10_000).coerceIn(250, 60_000) val fps = (parseFps(params) ?: 10.0).coerceIn(1.0, 60.0) val fpsInt = fps.roundToInt().coerceIn(1, 60) @@ -146,38 +143,19 @@ class ScreenRecordManager(private val context: Context) { } } - private fun parseParamsObject(paramsJson: String?): JsonObject? { - if (paramsJson.isNullOrBlank()) return null - return try { - Json.parseToJsonElement(paramsJson).asObjectOrNull() - } catch (_: Throwable) { - null - } - } - - private fun readPrimitive(params: JsonObject?, key: String): JsonPrimitive? = - params?.get(key) as? JsonPrimitive - private fun parseDurationMs(params: JsonObject?): Int? = - readPrimitive(params, "durationMs")?.contentOrNull?.toIntOrNull() + parseJsonInt(params, "durationMs") private fun parseFps(params: JsonObject?): Double? = - readPrimitive(params, "fps")?.contentOrNull?.toDoubleOrNull() + parseJsonDouble(params, "fps") private fun parseScreenIndex(params: JsonObject?): Int? = - readPrimitive(params, "screenIndex")?.contentOrNull?.toIntOrNull() + parseJsonInt(params, "screenIndex") - private fun parseIncludeAudio(params: JsonObject?): Boolean? { - val value = readPrimitive(params, "includeAudio")?.contentOrNull?.trim()?.lowercase() - return when (value) { - "true" -> true - "false" -> false - else -> null - } - } + private fun parseIncludeAudio(params: JsonObject?): Boolean? = parseJsonBooleanFlag(params, "includeAudio") private fun parseString(params: JsonObject?, key: String): String? = - readPrimitive(params, key)?.contentOrNull + parseJsonString(params, key) private fun estimateBitrate(width: Int, height: Int, fps: Int): Int { val pixels = width.toLong() * height.toLong()