From 8205409c8c23232a7443c8351307893a2651eb18 Mon Sep 17 00:00:00 2001 From: SuperMonster003 Date: Fri, 30 Jan 2026 20:57:09 +0800 Subject: [PATCH] =?UTF-8?q?6.7.0=20-=20Alpha18=20-=20=E4=BF=AE=E5=A4=8D=20?= =?UTF-8?q?toast=20=E6=96=B9=E6=B3=95=E5=9B=A0=E6=8C=81=E6=9C=89=20Toast?= =?UTF-8?q?=20=E5=AE=9E=E4=BE=8B=E5=BC=BA=E5=BC=95=E7=94=A8=E8=80=8C?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E5=8F=AF=E8=83=BD=E5=BE=97=E5=86=85=E5=AD=98?= =?UTF-8?q?=E6=B3=84=E9=9C=B2=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changelog/lang_zh-Hans.json | 3 +- .../autojs/autojs/runtime/ScriptRuntime.kt | 72 +++++---- .../autojs/autojs/runtime/api/ScriptToast.kt | 143 ++++++++++++++++-- .../autojs/runtime/api/augment/toast/Toast.kt | 30 ++-- .../runtime/api/augment/toast/ToastParser.kt | 40 ++--- .../java/org/autojs/autojs/tool/UiHandler.kt | 27 +--- .../org/autojs/autojs/ui/main/MainActivity.kt | 11 +- version.properties | 6 +- 8 files changed, 213 insertions(+), 119 deletions(-) diff --git a/.changelog/lang_zh-Hans.json b/.changelog/lang_zh-Hans.json index 56149eff..11922a00 100644 --- a/.changelog/lang_zh-Hans.json +++ b/.changelog/lang_zh-Hans.json @@ -1,7 +1,7 @@ { "$data": { "v6.7.0": { - "released_date": "2026/01/29", + "released_date": "2026/01/30", "feature": [ "插件中心功能, 支持插件的安装/卸载/更新等操作 (入口: 主页抽屉按钮/主页标签页)", "Paddle OCR (PP-OCRv5) 插件, 用于光学字符识别", @@ -61,6 +61,7 @@ "console.build 方法的 (title/content)BackgroundColor 选项导致透明度或着色选项被覆盖的问题 _[`issue #458`](http://issues.autojs6.com/458)_", "console/toast 等方法显示 Java Double 时可能丢失末尾零的问题", "无法使用 console/toast 等方法显示 BigInt 数据类型的问题", + "toast 方法因持有 Toast 实例强引用而导致可能得内存泄露问题", "部分全局对象可能丢失 JavaScript 原型属性及方法的问题", "使用 XML 语法将 JavaScript 表达式作为属性值时, this 对象可能出现指向错误的问题", "dialogs.build 方法 linkify 选项参数功能失效的问题", diff --git a/app/src/main/java/org/autojs/autojs/runtime/ScriptRuntime.kt b/app/src/main/java/org/autojs/autojs/runtime/ScriptRuntime.kt index b816c37d..503a8db6 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/ScriptRuntime.kt +++ b/app/src/main/java/org/autojs/autojs/runtime/ScriptRuntime.kt @@ -18,19 +18,18 @@ import org.autojs.autojs.core.console.GlobalConsole import org.autojs.autojs.core.image.capture.ScreenCaptureRequester import org.autojs.autojs.core.looper.Loopers import org.autojs.autojs.core.pref.Pref -import org.autojs.autojs.rhino.extension.ScriptableExtensions.defineProp -import org.autojs.autojs.rhino.extension.ScriptableExtensions.prop import org.autojs.autojs.lang.ThreadCompat import org.autojs.autojs.pio.PFiles import org.autojs.autojs.pio.UncheckedIOException import org.autojs.autojs.rhino.AndroidClassLoader import org.autojs.autojs.rhino.ProxyObject import org.autojs.autojs.rhino.TopLevelScope +import org.autojs.autojs.rhino.extension.ScriptableExtensions.defineProp +import org.autojs.autojs.rhino.extension.ScriptableExtensions.prop import org.autojs.autojs.runtime.api.AbstractShell import org.autojs.autojs.runtime.api.AppUtils import org.autojs.autojs.runtime.api.ProcessShell import org.autojs.autojs.runtime.api.ScreenMetrics -import org.autojs.autojs.runtime.api.ScriptToast import org.autojs.autojs.runtime.api.WrappedShizuku import org.autojs.autojs.runtime.api.augment.app.App import org.autojs.autojs.runtime.api.augment.autojs.Autojs @@ -183,7 +182,7 @@ class ScriptRuntime private constructor(builder: Builder) { private val mJob = SupervisorJob() val coroutineScope = CoroutineScope(Dispatchers.Default + mJob) val coroutineContext = coroutineScope.coroutineContext - + val ownerId = "runtime@${System.identityHashCode(this)}" private var mUiHandlerAppContext: Context @@ -587,13 +586,13 @@ class ScriptRuntime private constructor(builder: Builder) { Log.d(TAG, "on exit") this.isExiting = true - ignoresException({ + ignoresException { if (console.configurator.isExitOnClose) { console.hideDelayed() } - }) + } - ignoresException({ CoreWebSocket.onExit("Triggered by $TAG") }) + ignoresException { CoreWebSocket.onExit("Triggered by $TAG") } // @Hint by 抠脚本人 (https://github.com/little-alei) on Jul 10, 2023. // ! 清空无障碍事件. @@ -604,35 +603,42 @@ class ScriptRuntime private constructor(builder: Builder) { // ! by the current script to avoid affecting other still-running scripts. // ! zh-CN: 只清理当前脚本注册的无障碍事件回调,避免影响其他仍在运行的脚本. // # ignoresException({ AccessibilityService.clearAccessibilityEventCallback() }) - ignoresException({ automator.removeAllEventsForThisRuntime() }) + ignoresException { automator.removeAllEventsForThisRuntime() } - ignoresException({ RootUtils.resetRuntimeOverriddenRootModeState() }) + ignoresException { RootUtils.resetRuntimeOverriddenRootModeState() } - /* 回收全部记录的 ImageWrapper 实例. */ - ignoresException({ CoreImageWrapper.recycleAll() }) + // Recycle all recorded ImageWrapper instances. + // zh-CN: 回收全部记录的 ImageWrapper 实例. + ignoresException { CoreImageWrapper.recycleAll() } - /* 清除 interrupt 状态. */ - ignoresException({ ThreadCompat.interrupted() }) + // Clear interrupt status. + // zh-CN: 清除 interrupt 状态. + ignoresException { ThreadCompat.interrupted() } - /* 浮动窗口需要第一时间关闭. */ - /* 以免出现恶意脚本全屏浮动窗口遮蔽屏幕并且在 exit 中写死循环的问题. */ - ignoresException({ floaty.closeAll() }) + // Floating windows need to be closed immediately. + // To prevent malicious scripts from creating fullscreen floating windows + // that obscure the screen and write infinite loops in exit. + // zh-CN: + // 浮动窗口需要第一时间关闭. + // 以免出现恶意脚本全屏浮动窗口遮蔽屏幕并且在 exit 中写死循环的问题. + ignoresException { floaty.closeAll() } - ignoresException({ events.emit("exit") }, "Exception on exit: %s") - ignoresException({ ScriptToast.clear(this) }, 500) + ignoresException("Exception on exit: %s") { + events.emit("exit") + } - ignoresException({ threads.shutDownAll() }) - ignoresException({ events.recycle() }) - ignoresException({ media.recycle() }) - ignoresException({ loopers.recycle() }) - ignoresException({ this.recycleShell() }) - ignoresException({ images.releaseScreenCapturer() }) - ignoresException({ images.stopScreenCapturerForegroundService() }) - ignoresException({ ocrMLKit.release() }) - ignoresException({ sensors.unregisterAll() }) - ignoresException({ timers.recycle() }) - ignoresException({ ui.recycle() }) - ignoresException({ closeableManager.recycleAll() }) + ignoresException { threads.shutDownAll() } + ignoresException { events.recycle() } + ignoresException { media.recycle() } + ignoresException { loopers.recycle() } + ignoresException { recycleShell() } + ignoresException { images.releaseScreenCapturer() } + ignoresException { images.stopScreenCapturerForegroundService() } + ignoresException { ocrMLKit.release() } + ignoresException { sensors.unregisterAll() } + ignoresException { timers.recycle() } + ignoresException { ui.recycle() } + ignoresException { closeableManager.recycleAll() } } fun setScreenMetrics(width: Int, height: Int) { @@ -709,11 +715,11 @@ class ScriptRuntime private constructor(builder: Builder) { } } - private fun ignoresException(r: Runnable, @Suppress("SameParameterValue") delay: Int) { - uiHandler.postDelayed(r, delay.toLong()) + private fun ignoresException(r: Runnable) { + ignoresException(null, r) } - private fun ignoresException(r: Runnable, consoleMessage: String? = null) { + private fun ignoresException(consoleMessage: String?, r: Runnable) { try { r.run() } catch (e: Throwable) { diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/ScriptToast.kt b/app/src/main/java/org/autojs/autojs/runtime/api/ScriptToast.kt index 2956298d..81abbd92 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/ScriptToast.kt +++ b/app/src/main/java/org/autojs/autojs/runtime/api/ScriptToast.kt @@ -1,40 +1,155 @@ package org.autojs.autojs.runtime.api +import android.content.Context +import android.os.Build +import android.os.Handler +import android.os.Looper import android.widget.Toast import org.autojs.autojs.runtime.ScriptRuntime -import java.lang.ref.WeakReference -import java.util.concurrent.ConcurrentHashMap +import java.util.ArrayDeque /** + * Toast global queue manager. + * All scripts share a single global queue because only one toast can be shown at a time. + * + * zh-CN: + * + * Toast 全局队列管理器. + * 所有脚本共享同一个全局队列, 因为同一时间通常只能显示一个 Toast. + * * Created by SuperMonster003 on Aug 3, 2023. - * Modified by SuperMonster003 as of Oct 24, 2023. * Transformed by SuperMonster003 on Oct 24, 2023. + * Modified by JetBrains AI Assistant (GPT-5.2) as of Jan 30, 2026. + * Modified by SuperMonster003 as of Jan 30, 2026. */ object ScriptToast { - @JvmField - val pool = ConcurrentHashMap>() + private const val MAX_GLOBAL_QUEUE_SIZE = 17 + + private val mainHandler = Handler(Looper.getMainLooper()) + private val queue = ArrayDeque(MAX_GLOBAL_QUEUE_SIZE) + + private var isShowing = false + private var currentTask: ToastTask? = null + private var currentToast: Toast? = null + private var currentFinishRunnable: Runnable? = null + + private data class ToastTask( + val message: String, + val duration: Int, + val ownerId: String, + ) @JvmStatic - fun add(t: Toast, scriptRuntime: ScriptRuntime) { - pool[t] = WeakReference(scriptRuntime) + fun enqueueToast(context: Context, scriptRuntime: ScriptRuntime, message: String, isLong: Boolean = false) { + val duration = if (isLong) Toast.LENGTH_LONG else Toast.LENGTH_SHORT + val ownerId = scriptRuntime.ownerId + + // Must be called on the main thread. + // zh-CN: 必须在主线程调用. + if (Looper.myLooper() != Looper.getMainLooper()) { + mainHandler.post { enqueueToast(context, scriptRuntime, message, isLong) } + return + } + + if (queue.size >= MAX_GLOBAL_QUEUE_SIZE) { + // Discard newest task when the queue is full. + // zh-CN: 当队列已满时丢弃最新任务. + return + } + + queue.addLast(ToastTask(message, duration, ownerId)) + + tryShowNextLocked(context.applicationContext) } @JvmStatic fun dismissAll(scriptRuntime: ScriptRuntime) { - pool.entries.forEach { entry -> - if (entry.value.get() == scriptRuntime) { - pool.remove(entry.key) - entry.key.cancel() + val ownerId = scriptRuntime.ownerId + mainHandler.post { + queue.removeAll { it.ownerId == ownerId } + + if (currentTask?.ownerId == ownerId) { + currentFinishRunnable?.let { mainHandler.removeCallbacks(it) } + currentFinishRunnable = null + + currentToast?.cancel() + currentToast = null + currentTask = null + isShowing = false + + tryShowNextLocked(scriptRuntime.uiHandler.applicationContext) } } } @JvmStatic fun clear(scriptRuntime: ScriptRuntime) { - pool.entries - .filter { it.value.get() == scriptRuntime } - .forEach { pool.remove(it.key) } + val ownerId = scriptRuntime.ownerId + mainHandler.post { + queue.removeAll { it.ownerId == ownerId } + } + } + + @JvmStatic + fun destroy() { + mainHandler.post { + queue.clear() + + currentFinishRunnable?.let { mainHandler.removeCallbacks(it) } + currentFinishRunnable = null + + currentToast?.cancel() + currentToast = null + currentTask = null + isShowing = false + } + } + + private fun tryShowNextLocked(appCtx: Context) { + if (isShowing) return + + val task = queue.removeFirstOrNull() ?: return + + isShowing = true + currentTask = task + + val toast = Toast.makeText(appCtx, task.message, task.duration) + currentToast = toast + + val finish = Runnable { + currentFinishRunnable = null + currentToast = null + currentTask = null + isShowing = false + tryShowNextLocked(appCtx) + } + currentFinishRunnable = finish + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + toast.addCallback(object : Toast.Callback() { + override fun onToastHidden() { + super.onToastHidden() + finish.run() + } + }) + } else { + val delay = if (task.duration == Toast.LENGTH_LONG) 3_500L else 2_000L + mainHandler.postDelayed(finish, delay) + } + + toast.show() + } + + private fun ArrayDeque.removeFirstOrNull(): E? = + if (isEmpty()) null else removeFirst() + + private inline fun ArrayDeque.removeAll(predicate: (E) -> Boolean) { + if (isEmpty()) return + val it = iterator() + while (it.hasNext()) { + if (predicate(it.next())) it.remove() + } } } diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/augment/toast/Toast.kt b/app/src/main/java/org/autojs/autojs/runtime/api/augment/toast/Toast.kt index c15d7827..a6e7b579 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/augment/toast/Toast.kt +++ b/app/src/main/java/org/autojs/autojs/runtime/api/augment/toast/Toast.kt @@ -1,13 +1,13 @@ package org.autojs.autojs.runtime.api.augment.toast -import org.autojs.autojs.AutoJs -import org.autojs.autojs.annotation.RhinoFunctionBody import org.autojs.autojs.annotation.RhinoRuntimeFunctionInterface import org.autojs.autojs.rhino.ArgumentGuards import org.autojs.autojs.runtime.ScriptRuntime +import org.autojs.autojs.runtime.api.ScriptToast import org.autojs.autojs.runtime.api.augment.Augmentable import org.autojs.autojs.runtime.api.augment.Invokable -import org.autojs.autojs.util.RhinoUtils.undefined +import org.autojs.autojs.runtime.exception.WrappedIllegalArgumentException +import org.autojs.autojs.util.RhinoUtils.UNDEFINED import org.mozilla.javascript.Undefined class Toast(private val scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime), Invokable { @@ -35,26 +35,22 @@ class Toast(private val scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntim @JvmStatic @RhinoRuntimeFunctionInterface fun call(scriptRuntime: ScriptRuntime, args: Array): Undefined = ensureArgumentsLengthInRange(args, 0..3) { - undefined { - when (it.size) { - 0 -> ToastParser("").show(scriptRuntime) - 1 -> ToastParser(args[0]).show(scriptRuntime) - 2 -> ToastParser(args[0], args[1]).show(scriptRuntime) - 3 -> ToastParser(args[0], args[1], args[2]).show(scriptRuntime) - } + val toastParser = when (it.size) { + 0 -> ToastParser(globalContext, "") + 1 -> ToastParser(globalContext, args[0]) + 2 -> ToastParser(globalContext, args[0], args[1]) + 3 -> ToastParser(globalContext, args[0], args[1], args[2]) + else -> throw WrappedIllegalArgumentException("Invalid arguments length ${args.size} for global.toast") } + toastParser.show(scriptRuntime) + return@ensureArgumentsLengthInRange UNDEFINED } @JvmStatic @RhinoRuntimeFunctionInterface fun dismissAll(scriptRuntime: ScriptRuntime, args: Array): Undefined = ensureArgumentsIsEmpty(args) { - dismissAllRhino(scriptRuntime) - } - - @JvmStatic - @RhinoFunctionBody - fun dismissAllRhino(scriptRuntime: ScriptRuntime) = undefined { - AutoJs.instance.uiHandler.dismissAllToasts(scriptRuntime) + ScriptToast.dismissAll(scriptRuntime) + return@ensureArgumentsIsEmpty UNDEFINED } } diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/augment/toast/ToastParser.kt b/app/src/main/java/org/autojs/autojs/runtime/api/augment/toast/ToastParser.kt index c1ecada1..725714c1 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/augment/toast/ToastParser.kt +++ b/app/src/main/java/org/autojs/autojs/runtime/api/augment/toast/ToastParser.kt @@ -1,14 +1,18 @@ package org.autojs.autojs.runtime.api.augment.toast -import org.autojs.autojs.AutoJs import org.autojs.autojs.rhino.extension.AnyExtensions.jsBrief import org.autojs.autojs.runtime.ScriptRuntime -import org.autojs.autojs.util.RhinoUtils.coerceString -import org.mozilla.javascript.Context +import org.autojs.autojs.runtime.api.ScriptToast import org.mozilla.javascript.Scriptable.NOT_FOUND import org.mozilla.javascript.Undefined +import android.content.Context as AndroidContext +import org.mozilla.javascript.Context as RhinoContext -class ToastParser(private val msg: Any?, long: Any? = null, forcible: Any? = null) { +/** + * Created by SuperMonster003 on Dec 2, 2024. + * Modified by SuperMonster003 as of Jan 30, 2026. + */ +class ToastParser(private val context: AndroidContext, private val msg: Any?, isLong: Any? = null, isForcible: Any? = null) { private val badge = object { val long = Regex("^l(ong)?$", RegexOption.IGNORE_CASE) @@ -20,42 +24,42 @@ class ToastParser(private val msg: Any?, long: Any? = null, forcible: Any? = nul var isForcible = false init { - parseIsLong(long) - parseIsForcible(forcible) + parseIsLong(isLong) + parseIsForcible(isForcible) } fun show(scriptRuntime: ScriptRuntime) { - if (isForcible) Toast.dismissAllRhino(scriptRuntime) + if (this@ToastParser.isForcible) ScriptToast.dismissAll(scriptRuntime) val niceMsg = when { Undefined.isUndefined(msg) || msg == NOT_FOUND -> "undefined" msg == null -> "null" - else -> Context.toString(msg) + else -> RhinoContext.toString(msg) } - AutoJs.instance.uiHandler.toast(scriptRuntime, niceMsg, isLong) + ScriptToast.enqueueToast(context, scriptRuntime, niceMsg, this@ToastParser.isLong) } private fun parseIsLong(o: Any?) = when (o) { is Boolean -> o - is Number -> Context.toBoolean(o) + is Number -> RhinoContext.toBoolean(o) is String -> when { badge.long.matches(o) -> true badge.short.matches(o) -> false - badge.forcible.matches(o) -> isLong.also { this.isForcible = true } + badge.forcible.matches(o) -> this@ToastParser.isLong.also { this.isForcible = true } else -> throw IllegalArgumentException("Argument \"isLong\" ${o.jsBrief()} for Toast.Parser is invalid") } - else -> isLong - }.also { isLong = it } + else -> this@ToastParser.isLong + }.also { this@ToastParser.isLong = it } private fun parseIsForcible(o: Any?) = when (o) { is Boolean -> o - is Number -> Context.toBoolean(o) + is Number -> RhinoContext.toBoolean(o) is String -> when { badge.forcible.matches(o) -> true - badge.long.matches(o) -> isForcible.also { this.isLong = true } - badge.short.matches(o) -> isForcible.also { this.isLong = false } + badge.long.matches(o) -> this@ToastParser.isForcible.also { this.isLong = true } + badge.short.matches(o) -> this@ToastParser.isForcible.also { this.isLong = false } else -> throw IllegalArgumentException("Argument \"isForcible\" ${o.jsBrief()} for Toast.Parser is invalid") } - else -> isForcible - }.also { isForcible = it } + else -> this@ToastParser.isForcible + }.also { this@ToastParser.isForcible = it } } \ No newline at end of file diff --git a/app/src/main/java/org/autojs/autojs/tool/UiHandler.kt b/app/src/main/java/org/autojs/autojs/tool/UiHandler.kt index c767a23c..1bf199e4 100644 --- a/app/src/main/java/org/autojs/autojs/tool/UiHandler.kt +++ b/app/src/main/java/org/autojs/autojs/tool/UiHandler.kt @@ -3,33 +3,10 @@ package org.autojs.autojs.tool import android.content.Context import android.os.Handler import android.os.Looper -import android.widget.Toast -import android.widget.Toast.LENGTH_LONG -import android.widget.Toast.LENGTH_SHORT -import org.autojs.autojs.AutoJs -import org.autojs.autojs.runtime.ScriptRuntime -import org.autojs.autojs.runtime.api.ScriptToast -import org.autojs.autojs.util.RhinoUtils.isMainThread /** * Created by Stardust on May 2, 2017. * Transformed by SuperMonster003 on Oct 25, 2023. + * Modified by SuperMonster003 as of Jan 30, 2026. */ -class UiHandler(val applicationContext: Context) : Handler(Looper.getMainLooper()) { - - fun toast(scriptRuntime: ScriptRuntime, message: String, isLong: Boolean = false) { - val showRunnable = Runnable { - val toastLength = if (isLong) LENGTH_LONG else LENGTH_SHORT - Toast.makeText(AutoJs.instance.application, message, toastLength) - .also { ScriptToast.add(it, scriptRuntime) } - .show() - } - when { - isMainThread() -> showRunnable.run() - else -> this@UiHandler.post(showRunnable) - } - } - - fun dismissAllToasts(scriptRuntime: ScriptRuntime) = ScriptToast.dismissAll(scriptRuntime) - -} +class UiHandler(val applicationContext: Context) : Handler(Looper.getMainLooper()) diff --git a/app/src/main/java/org/autojs/autojs/ui/main/MainActivity.kt b/app/src/main/java/org/autojs/autojs/ui/main/MainActivity.kt index 7b3dd5a5..83dfcb99 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/MainActivity.kt +++ b/app/src/main/java/org/autojs/autojs/ui/main/MainActivity.kt @@ -6,8 +6,6 @@ import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.os.Bundle -import android.os.Handler -import android.os.Looper import android.view.Gravity import android.view.Menu import android.view.MenuItem @@ -347,12 +345,9 @@ class MainActivity : BaseActivity(), DelegateHost, HostActivity { } private fun setUpStatusBarIconLight() { - Handler(Looper.getMainLooper()).post { - if (sIsActionBarDrawerOpened) { - setUpStatusBarIconLightByNightMode() - } else { - setUpStatusBarIconLightByThemeColor() - } + when (sIsActionBarDrawerOpened) { + true -> setUpStatusBarIconLightByNightMode() + else -> setUpStatusBarIconLightByThemeColor() } } diff --git a/version.properties b/version.properties index 51fa6049..e6caa69e 100644 --- a/version.properties +++ b/version.properties @@ -1,5 +1,5 @@ -#Wed Jan 28 21:05:38 CST 2026 -BUILD_TIME=1769605538543 +#Fri Jan 30 20:56:33 CST 2026 +BUILD_TIME=1769777793096 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=3666 +VERSION_BUILD=3673 VERSION_NAME=6.7.0 Alpha18 VSCODE_EXT_REQUIRED_VERSION=1.0.13