6.7.0 - Alpha18 - 修复 toast 方法因持有 Toast 实例强引用而导致可能得内存泄露问题
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$data": {
|
"$data": {
|
||||||
"v6.7.0": {
|
"v6.7.0": {
|
||||||
"released_date": "2026/01/29",
|
"released_date": "2026/01/30",
|
||||||
"feature": [
|
"feature": [
|
||||||
"插件中心功能, 支持插件的安装/卸载/更新等操作 (入口: 主页抽屉按钮/主页标签页)",
|
"插件中心功能, 支持插件的安装/卸载/更新等操作 (入口: 主页抽屉按钮/主页标签页)",
|
||||||
"Paddle OCR (PP-OCRv5) 插件, 用于光学字符识别",
|
"Paddle OCR (PP-OCRv5) 插件, 用于光学字符识别",
|
||||||
@@ -61,6 +61,7 @@
|
|||||||
"console.build 方法的 (title/content)BackgroundColor 选项导致透明度或着色选项被覆盖的问题 _[`issue #458`](http://issues.autojs6.com/458)_",
|
"console.build 方法的 (title/content)BackgroundColor 选项导致透明度或着色选项被覆盖的问题 _[`issue #458`](http://issues.autojs6.com/458)_",
|
||||||
"console/toast 等方法显示 Java Double 时可能丢失末尾零的问题",
|
"console/toast 等方法显示 Java Double 时可能丢失末尾零的问题",
|
||||||
"无法使用 console/toast 等方法显示 BigInt 数据类型的问题",
|
"无法使用 console/toast 等方法显示 BigInt 数据类型的问题",
|
||||||
|
"toast 方法因持有 Toast 实例强引用而导致可能得内存泄露问题",
|
||||||
"部分全局对象可能丢失 JavaScript 原型属性及方法的问题",
|
"部分全局对象可能丢失 JavaScript 原型属性及方法的问题",
|
||||||
"使用 XML 语法将 JavaScript 表达式作为属性值时, this 对象可能出现指向错误的问题",
|
"使用 XML 语法将 JavaScript 表达式作为属性值时, this 对象可能出现指向错误的问题",
|
||||||
"dialogs.build 方法 linkify 选项参数功能失效的问题",
|
"dialogs.build 方法 linkify 选项参数功能失效的问题",
|
||||||
|
|||||||
@@ -18,19 +18,18 @@ import org.autojs.autojs.core.console.GlobalConsole
|
|||||||
import org.autojs.autojs.core.image.capture.ScreenCaptureRequester
|
import org.autojs.autojs.core.image.capture.ScreenCaptureRequester
|
||||||
import org.autojs.autojs.core.looper.Loopers
|
import org.autojs.autojs.core.looper.Loopers
|
||||||
import org.autojs.autojs.core.pref.Pref
|
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.lang.ThreadCompat
|
||||||
import org.autojs.autojs.pio.PFiles
|
import org.autojs.autojs.pio.PFiles
|
||||||
import org.autojs.autojs.pio.UncheckedIOException
|
import org.autojs.autojs.pio.UncheckedIOException
|
||||||
import org.autojs.autojs.rhino.AndroidClassLoader
|
import org.autojs.autojs.rhino.AndroidClassLoader
|
||||||
import org.autojs.autojs.rhino.ProxyObject
|
import org.autojs.autojs.rhino.ProxyObject
|
||||||
import org.autojs.autojs.rhino.TopLevelScope
|
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.AbstractShell
|
||||||
import org.autojs.autojs.runtime.api.AppUtils
|
import org.autojs.autojs.runtime.api.AppUtils
|
||||||
import org.autojs.autojs.runtime.api.ProcessShell
|
import org.autojs.autojs.runtime.api.ProcessShell
|
||||||
import org.autojs.autojs.runtime.api.ScreenMetrics
|
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.WrappedShizuku
|
||||||
import org.autojs.autojs.runtime.api.augment.app.App
|
import org.autojs.autojs.runtime.api.augment.app.App
|
||||||
import org.autojs.autojs.runtime.api.augment.autojs.Autojs
|
import org.autojs.autojs.runtime.api.augment.autojs.Autojs
|
||||||
@@ -587,13 +586,13 @@ class ScriptRuntime private constructor(builder: Builder) {
|
|||||||
Log.d(TAG, "on exit")
|
Log.d(TAG, "on exit")
|
||||||
this.isExiting = true
|
this.isExiting = true
|
||||||
|
|
||||||
ignoresException({
|
ignoresException {
|
||||||
if (console.configurator.isExitOnClose) {
|
if (console.configurator.isExitOnClose) {
|
||||||
console.hideDelayed()
|
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.
|
// @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.
|
// ! by the current script to avoid affecting other still-running scripts.
|
||||||
// ! zh-CN: 只清理当前脚本注册的无障碍事件回调,避免影响其他仍在运行的脚本.
|
// ! zh-CN: 只清理当前脚本注册的无障碍事件回调,避免影响其他仍在运行的脚本.
|
||||||
// # ignoresException({ AccessibilityService.clearAccessibilityEventCallback() })
|
// # ignoresException({ AccessibilityService.clearAccessibilityEventCallback() })
|
||||||
ignoresException({ automator.removeAllEventsForThisRuntime() })
|
ignoresException { automator.removeAllEventsForThisRuntime() }
|
||||||
|
|
||||||
ignoresException({ RootUtils.resetRuntimeOverriddenRootModeState() })
|
ignoresException { RootUtils.resetRuntimeOverriddenRootModeState() }
|
||||||
|
|
||||||
/* 回收全部记录的 ImageWrapper 实例. */
|
// Recycle all recorded ImageWrapper instances.
|
||||||
ignoresException({ CoreImageWrapper.recycleAll() })
|
// zh-CN: 回收全部记录的 ImageWrapper 实例.
|
||||||
|
ignoresException { CoreImageWrapper.recycleAll() }
|
||||||
|
|
||||||
/* 清除 interrupt 状态. */
|
// Clear interrupt status.
|
||||||
ignoresException({ ThreadCompat.interrupted() })
|
// zh-CN: 清除 interrupt 状态.
|
||||||
|
ignoresException { ThreadCompat.interrupted() }
|
||||||
|
|
||||||
/* 浮动窗口需要第一时间关闭. */
|
// Floating windows need to be closed immediately.
|
||||||
/* 以免出现恶意脚本全屏浮动窗口遮蔽屏幕并且在 exit 中写死循环的问题. */
|
// To prevent malicious scripts from creating fullscreen floating windows
|
||||||
ignoresException({ floaty.closeAll() })
|
// 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("Exception on exit: %s") {
|
||||||
ignoresException({ ScriptToast.clear(this) }, 500)
|
events.emit("exit")
|
||||||
|
}
|
||||||
|
|
||||||
ignoresException({ threads.shutDownAll() })
|
ignoresException { threads.shutDownAll() }
|
||||||
ignoresException({ events.recycle() })
|
ignoresException { events.recycle() }
|
||||||
ignoresException({ media.recycle() })
|
ignoresException { media.recycle() }
|
||||||
ignoresException({ loopers.recycle() })
|
ignoresException { loopers.recycle() }
|
||||||
ignoresException({ this.recycleShell() })
|
ignoresException { recycleShell() }
|
||||||
ignoresException({ images.releaseScreenCapturer() })
|
ignoresException { images.releaseScreenCapturer() }
|
||||||
ignoresException({ images.stopScreenCapturerForegroundService() })
|
ignoresException { images.stopScreenCapturerForegroundService() }
|
||||||
ignoresException({ ocrMLKit.release() })
|
ignoresException { ocrMLKit.release() }
|
||||||
ignoresException({ sensors.unregisterAll() })
|
ignoresException { sensors.unregisterAll() }
|
||||||
ignoresException({ timers.recycle() })
|
ignoresException { timers.recycle() }
|
||||||
ignoresException({ ui.recycle() })
|
ignoresException { ui.recycle() }
|
||||||
ignoresException({ closeableManager.recycleAll() })
|
ignoresException { closeableManager.recycleAll() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setScreenMetrics(width: Int, height: Int) {
|
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) {
|
private fun ignoresException(r: Runnable) {
|
||||||
uiHandler.postDelayed(r, delay.toLong())
|
ignoresException(null, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ignoresException(r: Runnable, consoleMessage: String? = null) {
|
private fun ignoresException(consoleMessage: String?, r: Runnable) {
|
||||||
try {
|
try {
|
||||||
r.run()
|
r.run()
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
|
|||||||
@@ -1,40 +1,155 @@
|
|||||||
package org.autojs.autojs.runtime.api
|
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 android.widget.Toast
|
||||||
import org.autojs.autojs.runtime.ScriptRuntime
|
import org.autojs.autojs.runtime.ScriptRuntime
|
||||||
import java.lang.ref.WeakReference
|
import java.util.ArrayDeque
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 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.
|
* Created by SuperMonster003 on Aug 3, 2023.
|
||||||
* Modified by SuperMonster003 as of Oct 24, 2023.
|
|
||||||
* Transformed by SuperMonster003 on 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 {
|
object ScriptToast {
|
||||||
|
|
||||||
@JvmField
|
private const val MAX_GLOBAL_QUEUE_SIZE = 17
|
||||||
val pool = ConcurrentHashMap<Toast, WeakReference<ScriptRuntime>>()
|
|
||||||
|
private val mainHandler = Handler(Looper.getMainLooper())
|
||||||
|
private val queue = ArrayDeque<ToastTask>(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
|
@JvmStatic
|
||||||
fun add(t: Toast, scriptRuntime: ScriptRuntime) {
|
fun enqueueToast(context: Context, scriptRuntime: ScriptRuntime, message: String, isLong: Boolean = false) {
|
||||||
pool[t] = WeakReference(scriptRuntime)
|
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
|
@JvmStatic
|
||||||
fun dismissAll(scriptRuntime: ScriptRuntime) {
|
fun dismissAll(scriptRuntime: ScriptRuntime) {
|
||||||
pool.entries.forEach { entry ->
|
val ownerId = scriptRuntime.ownerId
|
||||||
if (entry.value.get() == scriptRuntime) {
|
mainHandler.post {
|
||||||
pool.remove(entry.key)
|
queue.removeAll { it.ownerId == ownerId }
|
||||||
entry.key.cancel()
|
|
||||||
|
if (currentTask?.ownerId == ownerId) {
|
||||||
|
currentFinishRunnable?.let { mainHandler.removeCallbacks(it) }
|
||||||
|
currentFinishRunnable = null
|
||||||
|
|
||||||
|
currentToast?.cancel()
|
||||||
|
currentToast = null
|
||||||
|
currentTask = null
|
||||||
|
isShowing = false
|
||||||
|
|
||||||
|
tryShowNextLocked(scriptRuntime.uiHandler.applicationContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun clear(scriptRuntime: ScriptRuntime) {
|
fun clear(scriptRuntime: ScriptRuntime) {
|
||||||
pool.entries
|
val ownerId = scriptRuntime.ownerId
|
||||||
.filter { it.value.get() == scriptRuntime }
|
mainHandler.post {
|
||||||
.forEach { pool.remove(it.key) }
|
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 <E> ArrayDeque<E>.removeFirstOrNull(): E? =
|
||||||
|
if (isEmpty()) null else removeFirst()
|
||||||
|
|
||||||
|
private inline fun <E> ArrayDeque<E>.removeAll(predicate: (E) -> Boolean) {
|
||||||
|
if (isEmpty()) return
|
||||||
|
val it = iterator()
|
||||||
|
while (it.hasNext()) {
|
||||||
|
if (predicate(it.next())) it.remove()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package org.autojs.autojs.runtime.api.augment.toast
|
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.annotation.RhinoRuntimeFunctionInterface
|
||||||
import org.autojs.autojs.rhino.ArgumentGuards
|
import org.autojs.autojs.rhino.ArgumentGuards
|
||||||
import org.autojs.autojs.runtime.ScriptRuntime
|
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.Augmentable
|
||||||
import org.autojs.autojs.runtime.api.augment.Invokable
|
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
|
import org.mozilla.javascript.Undefined
|
||||||
|
|
||||||
class Toast(private val scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime), Invokable {
|
class Toast(private val scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime), Invokable {
|
||||||
@@ -35,26 +35,22 @@ class Toast(private val scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntim
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
@RhinoRuntimeFunctionInterface
|
@RhinoRuntimeFunctionInterface
|
||||||
fun call(scriptRuntime: ScriptRuntime, args: Array<out Any?>): Undefined = ensureArgumentsLengthInRange(args, 0..3) {
|
fun call(scriptRuntime: ScriptRuntime, args: Array<out Any?>): Undefined = ensureArgumentsLengthInRange(args, 0..3) {
|
||||||
undefined {
|
val toastParser = when (it.size) {
|
||||||
when (it.size) {
|
0 -> ToastParser(globalContext, "")
|
||||||
0 -> ToastParser("").show(scriptRuntime)
|
1 -> ToastParser(globalContext, args[0])
|
||||||
1 -> ToastParser(args[0]).show(scriptRuntime)
|
2 -> ToastParser(globalContext, args[0], args[1])
|
||||||
2 -> ToastParser(args[0], args[1]).show(scriptRuntime)
|
3 -> ToastParser(globalContext, args[0], args[1], args[2])
|
||||||
3 -> ToastParser(args[0], args[1], args[2]).show(scriptRuntime)
|
else -> throw WrappedIllegalArgumentException("Invalid arguments length ${args.size} for global.toast")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
toastParser.show(scriptRuntime)
|
||||||
|
return@ensureArgumentsLengthInRange UNDEFINED
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@RhinoRuntimeFunctionInterface
|
@RhinoRuntimeFunctionInterface
|
||||||
fun dismissAll(scriptRuntime: ScriptRuntime, args: Array<out Any?>): Undefined = ensureArgumentsIsEmpty(args) {
|
fun dismissAll(scriptRuntime: ScriptRuntime, args: Array<out Any?>): Undefined = ensureArgumentsIsEmpty(args) {
|
||||||
dismissAllRhino(scriptRuntime)
|
ScriptToast.dismissAll(scriptRuntime)
|
||||||
}
|
return@ensureArgumentsIsEmpty UNDEFINED
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@RhinoFunctionBody
|
|
||||||
fun dismissAllRhino(scriptRuntime: ScriptRuntime) = undefined {
|
|
||||||
AutoJs.instance.uiHandler.dismissAllToasts(scriptRuntime)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
package org.autojs.autojs.runtime.api.augment.toast
|
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.rhino.extension.AnyExtensions.jsBrief
|
||||||
import org.autojs.autojs.runtime.ScriptRuntime
|
import org.autojs.autojs.runtime.ScriptRuntime
|
||||||
import org.autojs.autojs.util.RhinoUtils.coerceString
|
import org.autojs.autojs.runtime.api.ScriptToast
|
||||||
import org.mozilla.javascript.Context
|
|
||||||
import org.mozilla.javascript.Scriptable.NOT_FOUND
|
import org.mozilla.javascript.Scriptable.NOT_FOUND
|
||||||
import org.mozilla.javascript.Undefined
|
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 {
|
private val badge = object {
|
||||||
val long = Regex("^l(ong)?$", RegexOption.IGNORE_CASE)
|
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
|
var isForcible = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
parseIsLong(long)
|
parseIsLong(isLong)
|
||||||
parseIsForcible(forcible)
|
parseIsForcible(isForcible)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun show(scriptRuntime: ScriptRuntime) {
|
fun show(scriptRuntime: ScriptRuntime) {
|
||||||
if (isForcible) Toast.dismissAllRhino(scriptRuntime)
|
if (this@ToastParser.isForcible) ScriptToast.dismissAll(scriptRuntime)
|
||||||
val niceMsg = when {
|
val niceMsg = when {
|
||||||
Undefined.isUndefined(msg) || msg == NOT_FOUND -> "undefined"
|
Undefined.isUndefined(msg) || msg == NOT_FOUND -> "undefined"
|
||||||
msg == null -> "null"
|
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) {
|
private fun parseIsLong(o: Any?) = when (o) {
|
||||||
is Boolean -> o
|
is Boolean -> o
|
||||||
is Number -> Context.toBoolean(o)
|
is Number -> RhinoContext.toBoolean(o)
|
||||||
is String -> when {
|
is String -> when {
|
||||||
badge.long.matches(o) -> true
|
badge.long.matches(o) -> true
|
||||||
badge.short.matches(o) -> false
|
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 -> throw IllegalArgumentException("Argument \"isLong\" ${o.jsBrief()} for Toast.Parser is invalid")
|
||||||
}
|
}
|
||||||
else -> isLong
|
else -> this@ToastParser.isLong
|
||||||
}.also { isLong = it }
|
}.also { this@ToastParser.isLong = it }
|
||||||
|
|
||||||
private fun parseIsForcible(o: Any?) = when (o) {
|
private fun parseIsForcible(o: Any?) = when (o) {
|
||||||
is Boolean -> o
|
is Boolean -> o
|
||||||
is Number -> Context.toBoolean(o)
|
is Number -> RhinoContext.toBoolean(o)
|
||||||
is String -> when {
|
is String -> when {
|
||||||
badge.forcible.matches(o) -> true
|
badge.forcible.matches(o) -> true
|
||||||
badge.long.matches(o) -> isForcible.also { this.isLong = true }
|
badge.long.matches(o) -> this@ToastParser.isForcible.also { this.isLong = true }
|
||||||
badge.short.matches(o) -> isForcible.also { this.isLong = false }
|
badge.short.matches(o) -> this@ToastParser.isForcible.also { this.isLong = false }
|
||||||
else -> throw IllegalArgumentException("Argument \"isForcible\" ${o.jsBrief()} for Toast.Parser is invalid")
|
else -> throw IllegalArgumentException("Argument \"isForcible\" ${o.jsBrief()} for Toast.Parser is invalid")
|
||||||
}
|
}
|
||||||
else -> isForcible
|
else -> this@ToastParser.isForcible
|
||||||
}.also { isForcible = it }
|
}.also { this@ToastParser.isForcible = it }
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -3,33 +3,10 @@ package org.autojs.autojs.tool
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
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.
|
* Created by Stardust on May 2, 2017.
|
||||||
* Transformed by SuperMonster003 on Oct 25, 2023.
|
* Transformed by SuperMonster003 on Oct 25, 2023.
|
||||||
|
* Modified by SuperMonster003 as of Jan 30, 2026.
|
||||||
*/
|
*/
|
||||||
class UiHandler(val applicationContext: Context) : Handler(Looper.getMainLooper()) {
|
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)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
@@ -347,12 +345,9 @@ class MainActivity : BaseActivity(), DelegateHost, HostActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpStatusBarIconLight() {
|
private fun setUpStatusBarIconLight() {
|
||||||
Handler(Looper.getMainLooper()).post {
|
when (sIsActionBarDrawerOpened) {
|
||||||
if (sIsActionBarDrawerOpened) {
|
true -> setUpStatusBarIconLightByNightMode()
|
||||||
setUpStatusBarIconLightByNightMode()
|
else -> setUpStatusBarIconLightByThemeColor()
|
||||||
} else {
|
|
||||||
setUpStatusBarIconLightByThemeColor()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#Wed Jan 28 21:05:38 CST 2026
|
#Fri Jan 30 20:56:33 CST 2026
|
||||||
BUILD_TIME=1769605538543
|
BUILD_TIME=1769777793096
|
||||||
COMPILE_SDK_VERSION=36
|
COMPILE_SDK_VERSION=36
|
||||||
IMAGE_QUANT_CMAKE_VERSION=3.22.1
|
IMAGE_QUANT_CMAKE_VERSION=3.22.1
|
||||||
IMAGE_QUANT_NDK_VERSION=26.1.10909125
|
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
|
RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3
|
||||||
TARGET_SDK_VERSION=36
|
TARGET_SDK_VERSION=36
|
||||||
TARGET_SDK_VERSION_INRT=29
|
TARGET_SDK_VERSION_INRT=29
|
||||||
VERSION_BUILD=3666
|
VERSION_BUILD=3673
|
||||||
VERSION_NAME=6.7.0 Alpha18
|
VERSION_NAME=6.7.0 Alpha18
|
||||||
VSCODE_EXT_REQUIRED_VERSION=1.0.13
|
VSCODE_EXT_REQUIRED_VERSION=1.0.13
|
||||||
|
|||||||
Reference in New Issue
Block a user