diff --git a/.changelog/lang_zh-Hans.json b/.changelog/lang_zh-Hans.json index 2a7d80da..780b24d2 100644 --- a/.changelog/lang_zh-Hans.json +++ b/.changelog/lang_zh-Hans.json @@ -17,6 +17,8 @@ "images.loadAsync 方法, 用于异步获取网络图像资源 _[`issue #327`](http://issues.autojs6.com/327)_", "ui.getNavigationBarHeight 方法/navigationBarHeight 属性 (getter), 用于获取导航栏高度 _[`issue #456`](http://issues.autojs6.com/456)_", "ui.getVisible(Status/Navigation)BarHeight 方法/visible(Status/Navigation)BarHeight 属性 (getter), 用于快捷获取状态栏或导航栏可见高度 _[`issue #456`](http://issues.autojs6.com/456)_", + "http.put/del(ete)/head 方法, 用于使用不同的 HTTP 请求方法发送网络请求", + "http.getAsync/postAsync/requestAsync 等方法, 用于异步发送网络请求 _[`issue #423`](http://issues.autojs6.com/423)_", "http 模块请求相关方法获取的 body 对象增加 stream/saveToFile/close 等方法 _[`issue #452`](http://issues.autojs6.com/452)_", "http 模块请求相关方法支持缓存控制选项参数 (cacheBody/bodyCacheThresholdBytes)", "http 模块请求相关方法支持不安全选项参数 (isInsecure/insecure), 用于忽略证书相关异常 _[`issue #417`](http://issues.autojs6.com/417)_", diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/augment/http/Http.kt b/app/src/main/java/org/autojs/autojs/runtime/api/augment/http/Http.kt index fa19d456..2ace1c60 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/augment/http/Http.kt +++ b/app/src/main/java/org/autojs/autojs/runtime/api/augment/http/Http.kt @@ -32,9 +32,11 @@ import org.autojs.autojs.util.RhinoUtils.coerceIntNumber import org.autojs.autojs.util.RhinoUtils.coerceLongNumber import org.autojs.autojs.util.RhinoUtils.coerceObject import org.autojs.autojs.util.RhinoUtils.coerceString +import org.autojs.autojs.util.RhinoUtils.handleAsyncOperation import org.autojs.autojs.util.RhinoUtils.isUiThread import org.autojs.autojs.util.RhinoUtils.js_json_stringify import org.autojs.autojs.util.RhinoUtils.newNativeObject +import org.autojs.autojs.util.RhinoUtils.withRhinoContext import org.mozilla.javascript.BaseFunction import org.mozilla.javascript.Context import org.mozilla.javascript.NativeObject @@ -53,10 +55,23 @@ class Http(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) { override val selfAssignmentFunctions = listOf( ::buildRequest.name, ::request.name, + ::requestAsync.name, ::get.name, + ::getAsync.name, + ::head.name, + ::headAsync.name, ::post.name, + ::postAsync.name, ::postJson.name, + ::postJsonAsync.name, ::postMultipart.name, + ::postMultipartAsync.name, + "put", + ::putAsync.name, + ::delete.name, + ::del.name, + ::deleteAsync.name, + ::delAsync.name, ) companion object : Augmentable() { @@ -74,7 +89,10 @@ class Http(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) { private const val KEY_BODY_CACHE_THRESHOLD_BYTES = "bodyCacheThresholdBytes" private const val METHOD_GET = "GET" + private const val METHOD_HEAD = "HEAD" private const val METHOD_POST = "POST" + private const val METHOD_PUT = "PUT" + private const val METHOD_DELETE = "DELETE" private val DEFAULT_CONTENT_TYPE = Mime.APPLICATION_X_WWW_FORM_URLENCODED @@ -117,31 +135,22 @@ class Http(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) { @JvmOverloads @RhinoFunctionBody fun requestRhinoWithRuntime(scriptRuntime: ScriptRuntime, url: Any?, options: Any? = null, callback: Any? = null): Any { + val cont: Creator? = when { callback.isJsFunction() && isUiThread() && Continuation.isEnabled(scriptRuntime) -> Continuation.createRhinoWithRuntime(scriptRuntime) else -> null } - val opt = coerceObject(options, newNativeObject()) - - scriptRuntime.http.okhttp.apply { - maxRetries = coerceIntNumber(opt.prop(KEY_MAX_RETRIES), DEFAULT_MAX_RETRIES) - applyOkHttpClientBuilder(opt) - } - - val cacheBody = opt.inquire(KEY_CACHE_BODY, ::coerceBoolean, DEFAULT_CACHE_BODY) - val cacheThreshold = coerceLongNumber(opt.prop(KEY_BODY_CACHE_THRESHOLD_BYTES), DEFAULT_BODY_CACHE_THRESHOLD_BYTES) - - val newCall = scriptRuntime.http.client().newCall(buildRequestRhinoWithRuntime(scriptRuntime, url, options)) + val prepared = prepareRequest(scriptRuntime, url, options) if (!callback.isJsFunction() && cont == null) { - return ResponseWrapper(scriptRuntime, newCall.execute(), cacheBody, cacheThreshold).wrap() + return wrapResponse(scriptRuntime, prepared.call.execute(), prepared.cacheBody, prepared.cacheThreshold) } scriptRuntime.loopers.waitWhenIdle(true) - newCall.enqueue(object : Callback { + prepared.call.enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { - val wrappedResponse = ResponseWrapper(scriptRuntime, response, cacheBody, cacheThreshold).wrap() + val wrappedResponse = wrapResponse(scriptRuntime, response, prepared.cacheBody, prepared.cacheThreshold) cont?.resume(wrappedResponse) if (callback is BaseFunction) { scriptRuntime.bridges.call(callback, callback, arrayOf(wrappedResponse, null)) @@ -157,10 +166,49 @@ class Http(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) { scriptRuntime.loopers.waitWhenIdle(false) } }) + cont?.await() return UNDEFINED } + @JvmStatic + @RhinoRuntimeFunctionInterface + fun requestAsync(scriptRuntime: ScriptRuntime, args: Array): NativeObject = ensureArgumentsLengthInRange(args, 1..3) { + val (url, options, callback) = it + requestAsyncRhinoWithRuntime(scriptRuntime, url, options, callback) + } + + @JvmStatic + @JvmOverloads + @RhinoFunctionBody + fun requestAsyncRhinoWithRuntime(scriptRuntime: ScriptRuntime, url: Any?, options: Any? = null, callback: Any? = null): NativeObject { + + val prepared = prepareRequest(scriptRuntime, url, options) + + return handleAsyncOperation( + scriptRuntime, + operation = { + // Execute network call in background thread. + // zh-CN: 在后台线程执行网络请求. + prepared.call.execute() + }, + uiMapper = { response -> + // Wrap response into Rhino object on UI thread with Rhino Context. + // zh-CN: 在 UI 线程进入 Rhino Context, 将 Response 包装为 Rhino 对象. + withRhinoContext { + val wrapped = wrapResponse(scriptRuntime, response, prepared.cacheBody, prepared.cacheThreshold) + + // Optional: if user provided callback, call it on UI thread. + // zh-CN: 可选: 如果用户提供了 callback, 则在 UI 线程调用. + if (callback is BaseFunction) { + scriptRuntime.bridges.call(callback, callback, arrayOf(wrapped, null)) + } + wrapped + } + }, + ) + } + @JvmStatic @RhinoRuntimeFunctionInterface fun get(scriptRuntime: ScriptRuntime, args: Array): Any = ensureArgumentsLengthInRange(args, 1..3) { @@ -168,6 +216,57 @@ class Http(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) { getRhinoWithRuntime(scriptRuntime, url, options, callback) } + @JvmStatic + @RhinoRuntimeFunctionInterface + fun head(scriptRuntime: ScriptRuntime, args: Array): Any = ensureArgumentsLengthInRange(args, 1..3) { + val (url, options, callback) = it + headRhinoWithRuntime(scriptRuntime, url, options, callback) + } + + @JvmStatic + @RhinoRuntimeFunctionInterface + fun headAsync(scriptRuntime: ScriptRuntime, args: Array): NativeObject = ensureArgumentsLengthInRange(args, 1..3) { + val (url, options, callback) = it + headAsyncRhinoWithRuntime(scriptRuntime, url, options, callback) + } + + @JvmStatic + @JvmOverloads + @RhinoFunctionBody + fun headRhinoWithRuntime(scriptRuntime: ScriptRuntime, url: Any?, options: Any? = null, callback: Any? = null): Any { + val niceOptions = if (options.isJsNullish()) newNativeObject() else options + require(niceOptions is NativeObject) { "Argument \"options\" ${options.jsBrief()} for http.head must be a JavaScript Object" } + put(niceOptions, KEY_METHOD to METHOD_HEAD) + return requestRhinoWithRuntime(scriptRuntime, url, niceOptions, callback) + } + + @JvmStatic + @JvmOverloads + @RhinoFunctionBody + fun headAsyncRhinoWithRuntime(scriptRuntime: ScriptRuntime, url: Any?, options: Any? = null, callback: Any? = null): NativeObject { + val niceOptions = if (options.isJsNullish()) newNativeObject() else options + require(niceOptions is NativeObject) { "Argument \"options\" ${options.jsBrief()} for http.headAsync must be a JavaScript Object" } + put(niceOptions, KEY_METHOD to METHOD_HEAD) + return requestAsyncRhinoWithRuntime(scriptRuntime, url, niceOptions, callback) + } + + @JvmStatic + @RhinoRuntimeFunctionInterface + fun getAsync(scriptRuntime: ScriptRuntime, args: Array): NativeObject = ensureArgumentsLengthInRange(args, 1..3) { + val (url, options, callback) = it + getAsyncRhinoWithRuntime(scriptRuntime, url, options, callback) + } + + @JvmStatic + @JvmOverloads + @RhinoFunctionBody + fun getAsyncRhinoWithRuntime(scriptRuntime: ScriptRuntime, url: Any?, options: Any? = null, callback: Any? = null): NativeObject { + val niceOptions = if (options.isJsNullish()) newNativeObject() else options + require(niceOptions is NativeObject) { "Argument \"options\" ${options.jsBrief()} for http.getAsync must be a JavaScript Object" } + put(niceOptions, KEY_METHOD to METHOD_GET) + return requestAsyncRhinoWithRuntime(scriptRuntime, url, niceOptions, callback) + } + @JvmStatic @JvmOverloads @RhinoFunctionBody @@ -178,6 +277,96 @@ class Http(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) { return requestRhinoWithRuntime(scriptRuntime, url, niceOptions, callback) } + @JvmStatic + @RhinoRuntimeFunctionInterface + fun put(scriptRuntime: ScriptRuntime, args: Array): Any = ensureArgumentsLengthInRange(args, 1..4) { + val (url, data, options, callback) = it + putRhinoWithRuntime(scriptRuntime, url, data, options, callback) + } + + @JvmStatic + @RhinoRuntimeFunctionInterface + fun putAsync(scriptRuntime: ScriptRuntime, args: Array): NativeObject = ensureArgumentsLengthInRange(args, 1..4) { + val (url, data, options, callback) = it + putAsyncRhinoWithRuntime(scriptRuntime, url, data, options, callback) + } + + @JvmStatic + @JvmOverloads + @RhinoFunctionBody + fun putRhinoWithRuntime(scriptRuntime: ScriptRuntime, url: Any?, data: Any? = null, options: Any? = null, callback: Any? = null): Any { + val niceOptions = if (options.isJsNullish()) newNativeObject() else options + require(niceOptions is NativeObject) { "Argument \"options\" ${options.jsBrief()} for http.put must be a JavaScript Object" } + put(niceOptions, KEY_METHOD to METHOD_PUT) + putIfAbsent(niceOptions, KEY_CONTENT_TYPE to DEFAULT_CONTENT_TYPE) + fillPostData(niceOptions, data) + return requestRhinoWithRuntime(scriptRuntime, url, niceOptions, callback) + } + + @JvmStatic + @JvmOverloads + @RhinoFunctionBody + fun putAsyncRhinoWithRuntime(scriptRuntime: ScriptRuntime, url: Any?, data: Any? = null, options: Any? = null, callback: Any? = null): NativeObject { + val niceOptions = if (options.isJsNullish()) newNativeObject() else options + require(niceOptions is NativeObject) { "Argument \"options\" ${options.jsBrief()} for http.putAsync must be a JavaScript Object" } + put(niceOptions, KEY_METHOD to METHOD_PUT) + putIfAbsent(niceOptions, KEY_CONTENT_TYPE to DEFAULT_CONTENT_TYPE) + fillPostData(niceOptions, data) + return requestAsyncRhinoWithRuntime(scriptRuntime, url, niceOptions, callback) + } + + @JvmStatic + @RhinoRuntimeFunctionInterface + fun delete(scriptRuntime: ScriptRuntime, args: Array): Any = ensureArgumentsLengthInRange(args, 1..4) { + val (url, data, options, callback) = it + deleteRhinoWithRuntime(scriptRuntime, url, data, options, callback) + } + + @JvmStatic + @RhinoRuntimeFunctionInterface + fun del(scriptRuntime: ScriptRuntime, args: Array): Any = ensureArgumentsLengthInRange(args, 1..4) { + val (url, data, options, callback) = it + deleteRhinoWithRuntime(scriptRuntime, url, data, options, callback) + } + + @JvmStatic + @RhinoRuntimeFunctionInterface + fun deleteAsync(scriptRuntime: ScriptRuntime, args: Array): NativeObject = ensureArgumentsLengthInRange(args, 1..4) { + val (url, data, options, callback) = it + deleteAsyncRhinoWithRuntime(scriptRuntime, url, data, options, callback) + } + + @JvmStatic + @RhinoRuntimeFunctionInterface + fun delAsync(scriptRuntime: ScriptRuntime, args: Array): NativeObject = ensureArgumentsLengthInRange(args, 1..4) { + val (url, data, options, callback) = it + deleteAsyncRhinoWithRuntime(scriptRuntime, url, data, options, callback) + } + + @JvmStatic + @JvmOverloads + @RhinoFunctionBody + fun deleteRhinoWithRuntime(scriptRuntime: ScriptRuntime, url: Any?, data: Any? = null, options: Any? = null, callback: Any? = null): Any { + val niceOptions = if (options.isJsNullish()) newNativeObject() else options + require(niceOptions is NativeObject) { "Argument \"options\" ${options.jsBrief()} for http.delete must be a JavaScript Object" } + put(niceOptions, KEY_METHOD to METHOD_DELETE) + putIfAbsent(niceOptions, KEY_CONTENT_TYPE to DEFAULT_CONTENT_TYPE) + fillPostData(niceOptions, data) + return requestRhinoWithRuntime(scriptRuntime, url, niceOptions, callback) + } + + @JvmStatic + @JvmOverloads + @RhinoFunctionBody + fun deleteAsyncRhinoWithRuntime(scriptRuntime: ScriptRuntime, url: Any?, data: Any? = null, options: Any? = null, callback: Any? = null): NativeObject { + val niceOptions = if (options.isJsNullish()) newNativeObject() else options + require(niceOptions is NativeObject) { "Argument \"options\" ${options.jsBrief()} for http.deleteAsync must be a JavaScript Object" } + put(niceOptions, KEY_METHOD to METHOD_DELETE) + putIfAbsent(niceOptions, KEY_CONTENT_TYPE to DEFAULT_CONTENT_TYPE) + fillPostData(niceOptions, data) + return requestAsyncRhinoWithRuntime(scriptRuntime, url, niceOptions, callback) + } + @JvmStatic @RhinoRuntimeFunctionInterface fun post(scriptRuntime: ScriptRuntime, args: Array): Any = ensureArgumentsLengthInRange(args, 1..4) { @@ -185,6 +374,25 @@ class Http(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) { postRhinoWithRuntime(scriptRuntime, url, data, options, callback) } + @JvmStatic + @RhinoRuntimeFunctionInterface + fun postAsync(scriptRuntime: ScriptRuntime, args: Array): NativeObject = ensureArgumentsLengthInRange(args, 1..4) { + val (url, data, options, callback) = it + postAsyncRhinoWithRuntime(scriptRuntime, url, data, options, callback) + } + + @JvmStatic + @JvmOverloads + @RhinoFunctionBody + fun postAsyncRhinoWithRuntime(scriptRuntime: ScriptRuntime, url: Any?, data: Any? = null, options: Any? = null, callback: Any? = null): NativeObject { + val niceOptions = if (options.isJsNullish()) newNativeObject() else options + require(niceOptions is NativeObject) { "Argument \"options\" ${options.jsBrief()} for http.postAsync must be a JavaScript Object" } + put(niceOptions, KEY_METHOD to METHOD_POST) + putIfAbsent(niceOptions, KEY_CONTENT_TYPE to DEFAULT_CONTENT_TYPE) + fillPostData(niceOptions, data) + return requestAsyncRhinoWithRuntime(scriptRuntime, url, niceOptions, callback) + } + @JvmStatic @JvmOverloads @RhinoFunctionBody @@ -204,6 +412,23 @@ class Http(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) { postJsonRhinoWithRuntime(scriptRuntime, url, data, options, callback) } + @JvmStatic + @RhinoRuntimeFunctionInterface + fun postJsonAsync(scriptRuntime: ScriptRuntime, args: Array): NativeObject = ensureArgumentsLengthInRange(args, 1..4) { + val (url, data, options, callback) = it + postJsonAsyncRhinoWithRuntime(scriptRuntime, url, data, options, callback) + } + + @JvmStatic + @JvmOverloads + @RhinoFunctionBody + fun postJsonAsyncRhinoWithRuntime(scriptRuntime: ScriptRuntime, url: Any?, data: Any?, options: Any? = null, callback: Any? = null): NativeObject { + val niceOptions = if (options.isJsNullish()) newNativeObject() else options + require(niceOptions is NativeObject) { "Argument \"options\" ${options.jsBrief()} for http.postJsonAsync must be a JavaScript Object" } + put(niceOptions, KEY_CONTENT_TYPE to Mime.APPLICATION_JSON) + return postAsyncRhinoWithRuntime(scriptRuntime, url, data, niceOptions, callback) + } + @JvmStatic @JvmOverloads @RhinoFunctionBody @@ -221,6 +446,27 @@ class Http(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) { postMultipartRhinoWithRuntime(scriptRuntime, url, files, options, callback) } + @JvmStatic + @RhinoRuntimeFunctionInterface + fun postMultipartAsync(scriptRuntime: ScriptRuntime, args: Array): NativeObject = ensureArgumentsLengthInRange(args, 1..4) { + val (url, files, options, callback) = it + postMultipartAsyncRhinoWithRuntime(scriptRuntime, url, files, options, callback) + } + + @JvmStatic + @JvmOverloads + @RhinoFunctionBody + fun postMultipartAsyncRhinoWithRuntime(scriptRuntime: ScriptRuntime, url: Any?, files: Any?, options: Any? = null, callback: Any? = null): NativeObject { + val niceOptions = if (options.isJsNullish()) newNativeObject() else options + require(niceOptions is NativeObject) { "Argument \"options\" ${options.jsBrief()} for http.postMultipartAsync must be a JavaScript Object" } + listOf( + KEY_METHOD to METHOD_POST, + KEY_CONTENT_TYPE to Mime.MULTIPART_FORM_DATA, + KEY_FILES to files, + ).let { list -> put(niceOptions, list) } + return requestAsyncRhinoWithRuntime(scriptRuntime, url, niceOptions, callback) + } + @JvmStatic @JvmOverloads @RhinoFunctionBody @@ -253,6 +499,49 @@ class Http(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) { } } + private data class PreparedRequest( + val call: Call, + val cacheBody: Boolean, + val cacheThreshold: Long, + ) + + private fun prepareRequest(scriptRuntime: ScriptRuntime, url: Any?, options: Any?): PreparedRequest { + + // Normalize options as a NativeObject. + // zh-CN: 将 options 规范化为 NativeObject. + val opt = coerceObject(options, newNativeObject()) + + // Apply OkHttp runtime configs (retries/client builder). + // zh-CN: 应用 OkHttp 运行时配置 (重试/Client Builder). + scriptRuntime.http.okhttp.apply { + maxRetries = coerceIntNumber(opt.prop(KEY_MAX_RETRIES), DEFAULT_MAX_RETRIES) + applyOkHttpClientBuilder(opt) + } + + // Cache policy for response body wrapper. + // zh-CN: Response body wrapper 的缓存策略. + val cacheBody = opt.inquire(KEY_CACHE_BODY, ::coerceBoolean, DEFAULT_CACHE_BODY) + val cacheThreshold = coerceLongNumber(opt.prop(KEY_BODY_CACHE_THRESHOLD_BYTES), DEFAULT_BODY_CACHE_THRESHOLD_BYTES) + + // Build request with normalized options and create call. + // zh-CN: 使用规范化后的 options 构建 Request 并创建 Call. + val request = buildRequestRhinoWithRuntime(scriptRuntime, url, opt) + val call = scriptRuntime.http.client().newCall(request) + + return PreparedRequest( + call = call, + cacheBody = cacheBody, + cacheThreshold = cacheThreshold, + ) + } + + private fun wrapResponse(scriptRuntime: ScriptRuntime, response: Response, cacheBody: Boolean, cacheThreshold: Long): Any { + + // Wrap okhttp3.Response into a Rhino-friendly object. + // zh-CN: 将 okhttp3.Response 包装为 Rhino 可用对象. + return ResponseWrapper(scriptRuntime, response, cacheBody, cacheThreshold).wrap() + } + } } diff --git a/app/src/main/java/org/autojs/autojs/util/RhinoUtils.kt b/app/src/main/java/org/autojs/autojs/util/RhinoUtils.kt index 6e7e702b..2da001f7 100644 --- a/app/src/main/java/org/autojs/autojs/util/RhinoUtils.kt +++ b/app/src/main/java/org/autojs/autojs/util/RhinoUtils.kt @@ -913,6 +913,14 @@ object RhinoUtils { } fun handleAsyncOperation(scriptRuntime: ScriptRuntime, func: () -> Any?): NativeObject { + return handleAsyncOperation(scriptRuntime, func) { it } + } + + fun handleAsyncOperation( + scriptRuntime: ScriptRuntime, + operation: () -> T, + uiMapper: (T) -> Any?, + ): NativeObject { val promiseAdapter = ScriptPromiseAdapter() // Use Android Thread directly to avoid coupling with JS threads/loopers here. @@ -921,12 +929,19 @@ object RhinoUtils { try { // Do the blocking work (e.g. network request) in background thread. // zh-CN: 在后台线程执行阻塞式操作 (如网络请求). - val result = func.invoke() + val result = operation.invoke() // Resolve on UI thread so that UI scripts can safely touch `ui.*` in then(). // zh-CN: 在 UI 线程 resolve, 让 UI 脚本在 then() 中可安全操作 `ui.*`. Handler(Looper.getMainLooper()).post { - promiseAdapter.resolve(result) + try { + // Map result on UI thread (e.g. wrap Java objects into Rhino objects). + // zh-CN: 在 UI 线程映射结果 (例如将 Java 对象包装为 Rhino 对象). + val mapped = uiMapper.invoke(result) + promiseAdapter.resolve(mapped) + } catch (t: Throwable) { + promiseAdapter.reject(t) + } } } catch (t: Throwable) { // Reject on UI thread for consistent callback threading. @@ -942,7 +957,6 @@ object RhinoUtils { return callFunction(scriptRuntime, scriptRuntime.js_ResultAdapter, "promise", arrayOf(promiseAdapter)) as NativeObject } - @JvmStatic fun Class<*>.getRhinoStandardFunctionMethods(): List { return this.declaredMethods.filter { diff --git a/version.properties b/version.properties index 7b655998..99d57296 100644 --- a/version.properties +++ b/version.properties @@ -1,5 +1,5 @@ -#Mon Jan 12 21:46:16 CST 2026 -BUILD_TIME=1768225576059 +#Mon Jan 12 23:37:01 CST 2026 +BUILD_TIME=1768232221256 COMPILE_SDK_VERSION=36 IMAGE_QUANT_CMAKE_VERSION=3.22.1 IMAGE_QUANT_NDK_VERSION=26.1.10909125 @@ -27,6 +27,6 @@ RAPID_OCR_OPENCV_MOBILE_LABEL_VERSION=13 RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3 TARGET_SDK_VERSION=36 TARGET_SDK_VERSION_INRT=29 -VERSION_BUILD=3599 +VERSION_BUILD=3601 VERSION_NAME=6.7.0 Alpha14 VSCODE_EXT_REQUIRED_VERSION=1.0.13