From f0bbfce3f651de781c61489d19b85c5ba5176613 Mon Sep 17 00:00:00 2001 From: SuperMonster003 Date: Sat, 1 Nov 2025 13:34:33 +0800 Subject: [PATCH] =?UTF-8?q?6.7.0=20-=20Alpha10=20-=20=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=E5=8F=B0=E6=B5=AE=E5=8A=A8=E7=AA=97=E5=8F=A3=E5=86=85=E9=83=A8?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E8=BF=9B=E8=A1=8C=E6=97=A0=E9=94=81=E5=8C=96?= =?UTF-8?q?=E5=8F=8A=E9=98=9F=E5=88=97=E5=8C=96=E5=A4=84=E7=90=86=E4=BB=A5?= =?UTF-8?q?=E6=8F=90=E5=8D=87=E5=85=B6=E5=8F=82=E6=95=B0=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E6=95=88=E7=8E=87=E4=B8=8E=E6=88=90=E5=8A=9F=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changelog/lang_zh-Hans.json | 4 +- .../autojs/core/console/ConsoleView.java | 2 +- .../autojs/core/console/ConsoleFloaty.kt | 12 +- .../autojs/autojs/core/console/ConsoleImpl.kt | 392 ++++++++++-------- .../autojs/core/console/ConsoleView.java | 57 --- .../org/autojs/autojs/inrt/LogActivity.kt | 1 - .../org/autojs/autojs/runtime/api/Console.kt | 3 - .../drawable-xhdpi/ic_looks_7_black_48dp.png | Bin 0 -> 1459 bytes .../drawable-xhdpi/ic_looks_7_white_48dp.png | Bin 0 -> 3083 bytes .../drawable-xhdpi/ic_looks_8_black_48dp.png | Bin 0 -> 3123 bytes .../drawable-xhdpi/ic_looks_8_white_48dp.png | Bin 0 -> 3754 bytes .../drawable-xhdpi/ic_looks_9_black_48dp.png | Bin 0 -> 2098 bytes .../drawable-xhdpi/ic_looks_9_white_48dp.png | Bin 0 -> 2415 bytes app/src/main/res/layout/console_view.xml | 29 -- .../main/res/layout/console_view_legacy.xml | 43 ++ .../res/layout/floating_console_expand.xml | 2 +- 16 files changed, 271 insertions(+), 274 deletions(-) create mode 100644 app/src/main/res/drawable-xhdpi/ic_looks_7_black_48dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_looks_7_white_48dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_looks_8_black_48dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_looks_8_white_48dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_looks_9_black_48dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_looks_9_white_48dp.png create mode 100644 app/src/main/res/layout/console_view_legacy.xml diff --git a/.changelog/lang_zh-Hans.json b/.changelog/lang_zh-Hans.json index c68368c6..e3a978a5 100644 --- a/.changelog/lang_zh-Hans.json +++ b/.changelog/lang_zh-Hans.json @@ -1,7 +1,7 @@ { "$data": { "v6.7.0": { - "released_date": "2025/10/27", + "released_date": "2025/11/01", "feature": [ "cvt 模块, 用于数据单位转换 (参阅 项目文档 > [单位转换](https://docs.autojs6.com/#/cvt))", "fmt 模块, 用于数据格式化 (参阅 项目文档 > [格式化](https://docs.autojs6.com/#/fmt))", @@ -58,6 +58,8 @@ "console.log 等方法打印全局对象 (images, app, ocr 等) 时支持显示详细信息", "android.graphics.Paint#setColor 支持正常解析 ColorInt/ColorHex/ColorName 等颜色参数", "内置模块相关方法实参类型的异常消息增加类型摘要信息", + "控制台浮动窗口倒计时起始值由 6 秒增加到 9 秒", + "控制台浮动窗口内部实现进行无锁化及队列化处理以提升其参数设置效率与成功率", "文件管理器浮动按钮展开后点击菜单项时优化菜单收起时机", "崩溃报告页面支持双指缩放调整字体大小并添加常用功能按钮", "应用启动器图标支持自适应图标特性 _[`issue #405`](http://issues.autojs6.com/405)_", diff --git a/app/src/main/java/com/stardust/autojs/core/console/ConsoleView.java b/app/src/main/java/com/stardust/autojs/core/console/ConsoleView.java index 33887b13..793c5633 100644 --- a/app/src/main/java/com/stardust/autojs/core/console/ConsoleView.java +++ b/app/src/main/java/com/stardust/autojs/core/console/ConsoleView.java @@ -81,7 +81,7 @@ public class ConsoleView extends FrameLayout implements ConsoleImpl.LogListener } private void init(AttributeSet attrs) { - inflate(getContext(), R.layout.console_view, this); + inflate(getContext(), R.layout.console_view_legacy, this); if (attrs != null) { TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ConsoleView); for (Map.Entry attr : ATTRS.entrySet()) { diff --git a/app/src/main/java/org/autojs/autojs/core/console/ConsoleFloaty.kt b/app/src/main/java/org/autojs/autojs/core/console/ConsoleFloaty.kt index a2ba681a..5bf68c2e 100644 --- a/app/src/main/java/org/autojs/autojs/core/console/ConsoleFloaty.kt +++ b/app/src/main/java/org/autojs/autojs/core/console/ConsoleFloaty.kt @@ -12,6 +12,7 @@ import android.widget.LinearLayout import android.widget.TextView import androidx.annotation.ColorInt import androidx.core.content.res.ResourcesCompat +import androidx.core.view.isVisible import org.autojs.autojs.runtime.api.ScreenMetrics import org.autojs.autojs.ui.enhancedfloaty.FloatyService import org.autojs.autojs.ui.enhancedfloaty.ResizableExpandableFloaty.AbstractResizableExpandableFloaty @@ -22,7 +23,6 @@ import org.autojs.autojs6.R import org.autojs.autojs6.databinding.FloatingConsoleExpandBinding import org.autojs.autojs6.databinding.FloatingWindowCollapseBinding import kotlin.math.roundToInt -import androidx.core.view.isVisible /** * Created by Stardust on Apr 20, 2017. @@ -143,7 +143,6 @@ class ConsoleFloaty(private val console: ConsoleImpl) : AbstractResizableExpanda private fun setUpConsole(window: ResizableExpandableFloatyWindow) { expandedBinding.console.apply { setConsole(console) - setWindow(window) } initConsoleTitleBar(window) } @@ -204,8 +203,13 @@ class ConsoleFloaty(private val console: ConsoleImpl) : AbstractResizableExpanda } internal fun setCloseButton(resId: Int) { - mCloseButton?.apply { - post { setImageDrawable(ResourcesCompat.getDrawable(console.uiHandler.applicationContext.resources, resId, null)) } + mCloseButton?.let { + val res = console.uiHandler.applicationContext.resources + val padding = (7 * res.displayMetrics.density).toInt() + it.post { + it.setImageDrawable(ResourcesCompat.getDrawable(res, resId, null)) + it.setPadding(padding, padding, padding, padding) + } } } diff --git a/app/src/main/java/org/autojs/autojs/core/console/ConsoleImpl.kt b/app/src/main/java/org/autojs/autojs/core/console/ConsoleImpl.kt index a4438e0e..89111554 100644 --- a/app/src/main/java/org/autojs/autojs/core/console/ConsoleImpl.kt +++ b/app/src/main/java/org/autojs/autojs/core/console/ConsoleImpl.kt @@ -22,6 +22,7 @@ import androidx.core.graphics.alpha import androidx.core.graphics.blue import androidx.core.graphics.green import androidx.core.graphics.red +import androidx.core.view.doOnLayout import com.afollestad.materialdialogs.DialogAction import com.afollestad.materialdialogs.MaterialDialog import org.autojs.autojs.annotation.ScriptInterface @@ -32,7 +33,6 @@ import org.autojs.autojs.permission.DisplayOverOtherAppsPermission import org.autojs.autojs.runtime.ScriptRuntime import org.autojs.autojs.runtime.api.AbstractConsole import org.autojs.autojs.runtime.api.Mime -import org.autojs.autojs.runtime.exception.ScriptInterruptedException import org.autojs.autojs.tool.UiHandler import org.autojs.autojs.ui.common.NotAskAgainDialog import org.autojs.autojs.ui.enhancedfloaty.FloatyService @@ -48,8 +48,6 @@ import org.opencv.core.Point import org.opencv.core.Size import java.io.IOException import java.lang.ref.WeakReference -import java.util.concurrent.ArrayBlockingQueue -import java.util.concurrent.BlockingQueue import java.util.concurrent.atomic.AtomicInteger import kotlin.math.ceil import kotlin.math.pow @@ -63,17 +61,8 @@ import android.graphics.Point as AndroidPoint */ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() { - private val lock = Object() + private val applicationContext = uiHandler.applicationContext - var configurator = Configurator() - - val logEntries = ArrayList() - - @Volatile - var isShowing = false - private set - - private val mDefaultSafeDelay: Long = 360 private val mSafeSizeToSend: Int = 2.0.pow(16).toInt() private val mSafeSizeToCopy: Int = 2.0.pow(19).toInt() @@ -83,31 +72,30 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() { @get:Synchronized private var mCountDownTimer: CountDownTimer? = null - private var mFloatyWindow: ResizableExpandableFloatyWindow? = null + private val mIdCounter = AtomicInteger(0) + private val mConsoleFloaty = ConsoleFloaty(this) + private val mFloatyWindow = ResizableExpandableFloatyWindow(mConsoleFloaty) + private val mDisplayOverOtherAppsPerm = DisplayOverOtherAppsPermission(applicationContext) + + private var mStage = Stage.INIT + private val mPendingMutations = ArrayDeque() + private var mIsDraining = false private val context: Context - get() = mConsoleView?.get()?.context ?: uiHandler.applicationContext - - private val mLockWindowShow = Object() - private val mLockWindowCreated = Object() - private val mLockConsoleView = Object() - private val mIdCounter = AtomicInteger(0) - private val mConsoleFloaty: ConsoleFloaty - private val mInput: BlockingQueue = ArrayBlockingQueue(1) - private val mDisplayOverOtherAppsPerm = DisplayOverOtherAppsPermission(uiHandler.applicationContext) + get() = mConsoleView?.get()?.context ?: applicationContext private val logEntriesJoint get() = synchronized(logEntries) { logEntries.joinToString("\n") { it.content } } - init { - synchronized(mLockWindowCreated) { - mConsoleFloaty = ConsoleFloaty(this) - mFloatyWindow = ResizableExpandableFloatyWindow(mConsoleFloaty) - mLockWindowCreated.notify() - } - } + val configurator = Configurator() + + val logEntries = ArrayList() + + @Volatile + var isShowing = false + private set override fun setTitle(title: CharSequence?) { val niceTitle = title ?: "" @@ -117,11 +105,6 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() { fun getTitle() = mConsoleFloaty.getTitle() - // TODO by SuperMonster003 on Oct 24, 2024. - // ! Add an option in preferences, - // ! such as "Show detailed error stack trace in console". - // ! zh-CN: - // ! 增加设置选项, 如 "控制台打印详细的错误堆栈信息". override fun error(data: Any?, vararg formatArgs: Any?) { var niceData = data if (niceData is Throwable) { @@ -148,29 +131,19 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() { } } - @ScriptInterface - override fun rawInput(): String? { - if (getFloatingConsoleView() == null) { - if (!isShowing) show() - waitForConsoleView() - } - getFloatingConsoleView()?.showEditText() - return try { - mInput.take() - } catch (_: InterruptedException) { - throw ScriptInterruptedException() - } - } - - @ScriptInterface - fun rawInput(data: Any?, vararg param: Any?) = rawInput().also { log(data, *param) } - fun setConsoleView(consoleView: ConsoleView?) { consoleView.let { mConsoleView = WeakReference(it) addLogListener(it) } - synchronized(mLockConsoleView) { mLockConsoleView.notify() } + // Stage advancement: View available. + // zh-CN: 阶段推进: 视图可用. + advanceStage(Stage.VIEW_ATTACHED) + // First frame layout completion advancement. + // zh-CN: 首帧布局完成推进. + (getFloatingConsoleView() ?: consoleView)?.doOnLayout { + advanceStage(Stage.LAYOUT_DONE) + } } fun printAllStackTrace(t: Throwable) { @@ -314,7 +287,7 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() { putExtra(Intent.EXTRA_TEXT, message) type = Mime.TEXT_PLAIN } - uiHandler.applicationContext.startActivity(Intent.createChooser(sendIntent, null).apply { + applicationContext.startActivity(Intent.createChooser(sendIntent, null).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }) } @@ -363,47 +336,61 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() { setCloseButton(R.drawable.ic_close_white_24dp) if (isReset) clearStates() - synchronized(mLockWindowShow) { - if (!mDisplayOverOtherAppsPerm.has()) { - mDisplayOverOtherAppsPerm.config() - ViewUtils.showToast(context, R.string.error_no_display_over_other_apps_permission) - return - } - if (isReset) reset() - if (isShowing) return + if (!mDisplayOverOtherAppsPerm.has()) { + mDisplayOverOtherAppsPerm.config() + ViewUtils.showToast(context, R.string.error_no_display_over_other_apps_permission) + return + } + if (isReset) reset() + if (isShowing) return - runWithWindow({ startFloatyService() }) { - try { - FloatyService.addWindow(mFloatyWindow) - configurator.size?.let { FloatyService.setInitialMeasure(it) } - setTitleBarGestureListener() - // SecurityException: https://github.com/hyb1996-guest/AutoJsIssueReport/issues/4781 - } catch (e: Exception) { - e.printStackTrace() - ScriptRuntime.popException(e.message) - ViewUtils.showToast(context, R.string.error_no_display_over_other_apps_permission) + // Reset stage. + // zh-CN: 重置阶段. + mStage = Stage.INIT + + startFloatyService() + uiHandler.post { + try { + FloatyService.addWindow(mFloatyWindow) + configurator.size?.let { FloatyService.setInitialMeasure(it) } + setTitleBarGestureListener() + + // Window created. + // zh-CN: 窗口已创建. + advanceStage(Stage.WINDOW_CREATED) + + mFloatyWindow.addOnViewAttachedTask { + // View attached -> VIEW_ATTACHED . + // zh-CN: 视图 attach -> VIEW_ATTACHED. + advanceStage(Stage.VIEW_ATTACHED) + // First frame layout -> LAYOUT_DONE. + // zh-CN: 首帧布局 -> LAYOUT_DONE. + mConsoleFloaty.expandedView?.doOnLayout { advanceStage(Stage.LAYOUT_DONE) } } - } - synchronized(mLockWindowCreated) { - isShowing = true - configurator.title?.let { setTitle(it) } - configurator.position?.run { setPosition(x, y, true) } - setGravity(getGravity()) - configurator.titleTextSize?.let { setTitleTextSize(it) } - configurator.titleTextColor?.let { setTitleTextColor(it) } - setTitleBackgroundColor(getTitleBackgroundColor()) - configurator.titleIconsTint?.let { setTitleIconsTint(it) } - configurator.contentTextSize?.let { setContentTextSize(it) } - configurator.contentTextColors?.let { setContentTextColors(it) } - setContentBackgroundColor(getContentBackgroundColor()) - setTouchable(isTouchable()) - mLockWindowCreated.wait(mDefaultSafeDelay) + } catch (e: Exception) { + e.printStackTrace() + ScriptRuntime.popException(e.message) + ViewUtils.showToast(context, R.string.error_no_display_over_other_apps_permission) } } + + isShowing = true + + configurator.title?.let { setTitle(it) } + configurator.position?.run { setPosition(x, y, true) } + setGravity(getGravity()) + configurator.titleTextSize?.let { setTitleTextSize(it) } + configurator.titleTextColor?.let { setTitleTextColor(it) } + setTitleBackgroundColor(getTitleBackgroundColor()) + configurator.titleIconsTint?.let { setTitleIconsTint(it) } + configurator.contentTextSize?.let { setContentTextSize(it) } + configurator.contentTextColors?.let { setContentTextColors(it) } + setContentBackgroundColor(getContentBackgroundColor()) + setTouchable(isTouchable()) } private fun setTitleBarGestureListener() { - mFloatyWindow?.windowBridge?.let { windowBridge -> + mFloatyWindow.windowBridge?.let { windowBridge -> mConsoleFloaty.titleBarView?.let { view -> DragGesture(windowBridge, view).apply { pressedAlpha = 1.0f } } @@ -413,17 +400,17 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() { private fun clearStates() { FloatyService.setInitialMeasure(null) mConsoleFloaty.clearStates() - mFloatyWindow?.clearOnViewAttachedTask() - configurator = Configurator() + mFloatyWindow.clearOnViewAttachedTask() + configurator.clearStates() } fun reset() { if (isShowing) { hide(true) - uiHandler.postDelayed({ + uiHandler.post { clearStates() show() - }, mDefaultSafeDelay) + } } else { clearStates() } @@ -437,17 +424,17 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() { } private fun startFloatyService() { - uiHandler.applicationContext.startService(Intent(uiHandler.applicationContext, FloatyService::class.java)) + applicationContext.startService(Intent(applicationContext, FloatyService::class.java)) } @ScriptInterface override fun hide() = hide(false) - private fun hide(ignoreSavingStates: Boolean) = runWithWindow { - synchronized(mLockWindowShow) { + private fun hide(ignoreSavingStates: Boolean) { + uiHandler.post { try { if (!ignoreSavingStates) saveStates() - mFloatyWindow!!.close() + mFloatyWindow.close() } catch (_: IllegalArgumentException) { /* Ignored. */ } @@ -455,12 +442,12 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() { } } - private fun saveStates() = runWithWindow { + private fun saveStates() = uiHandler.post { // @Hint by SuperMonster003 on Feb 9, 2024. // ! Position and size need to be saved as user may cause their changes. // ! zh-CN: 需要保存位置和大小, 因为用户可能会改变它们. - val bridge = mFloatyWindow!!.windowBridge ?: return@runWithWindow + val bridge = mFloatyWindow.windowBridge ?: return@post configurator.setPosition(bridge.x.toDouble(), bridge.y.toDouble()) configurator.setGravity(Gravity.NO_GRAVITY) @@ -468,8 +455,9 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() { } @ScriptInterface - override fun setSize(w: Double, h: Double) = runWithWindow({ configurator.setSize(w, h) }) { - trySetting { + override fun setSize(w: Double, h: Double) { + configurator.setSize(w, h) + enqueueMutation(Stage.VIEW_ATTACHED) { mConsoleFloaty.expandedView?.let { ViewUtils.setViewMeasure(it, w.toInt(), h.toInt()) } @@ -487,9 +475,10 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() { fun setTouchable() = setTouchable(DEFAULT_TOUCHABLE) @ScriptInterface - override fun setTouchable(touchable: Boolean) = runWithWindow({ configurator.setTouchable(touchable) }) { - trySetting { - mFloatyWindow!!.setTouchable(touchable) + override fun setTouchable(touchable: Boolean) { + configurator.setTouchable(touchable) + enqueueMutation(Stage.WINDOW_CREATED) { + mFloatyWindow.setTouchable(touchable) } } @@ -516,31 +505,44 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() { @ScriptInterface override fun setPosition(x: Double, y: Double) = setPosition(x, y, false) - private fun setPosition(x: Double, y: Double, isSaveGravity: Boolean) = runWithWindow({ + private fun setPosition(x: Double, y: Double, shouldSaveGravity: Boolean) { configurator.setPosition(x, y) - if (!isSaveGravity) { + if (!shouldSaveGravity) { configurator.setGravity(Gravity.NO_GRAVITY) } - }) { - trySetting { mFloatyWindow!!.windowBridge?.updatePosition(x.toInt(), y.toInt()) } + // Wait for VIEW_ATTACHED before setting to avoid attach backfill override. + // zh-CN: 等待 VIEW_ATTACHED 再设置, 避免 attach 回填覆盖. + enqueueMutation(Stage.VIEW_ATTACHED) { + mFloatyWindow.windowBridge?.updatePosition(x.toInt(), y.toInt()) + } + // After first frame layout is complete, if gravity is not set and position is overwritten, set position again to avoid layout backfill override. + // zh-CN: 首帧布局完成后, 若未设置重力且位置被改写, 再设置一次位置, 避免 layout 回填覆盖. + enqueueMutation(Stage.LAYOUT_DONE) { + val bridge = mFloatyWindow.windowBridge ?: return@enqueueMutation + if (configurator.gravity == Gravity.NO_GRAVITY) { + val needFix = bridge.x != x.toInt() || bridge.y != y.toInt() + if (needFix) bridge.updatePosition(x.toInt(), y.toInt()) + } + } } @ScriptInterface fun getPosition(): Point { return mFloatyWindow.let { floatyWindow -> - floatyWindow?.windowBridge ?: return Point() + floatyWindow.windowBridge ?: return Point() Point(floatyWindow.windowBridge!!.x.toDouble(), floatyWindow.windowBridge!!.y.toDouble()) } } @SuppressLint("RtlHardcoded") @ScriptInterface - fun setGravity(gravity: Int) = runWithWindow({ configurator.setGravity(gravity) }) { - trySetting { - if (gravity <= Gravity.NO_GRAVITY) return@trySetting - val bridge = mFloatyWindow!!.windowBridge ?: return@trySetting + fun setGravity(gravity: Int) { + configurator.setGravity(gravity) + enqueueMutation(Stage.LAYOUT_DONE) { + if (gravity <= Gravity.NO_GRAVITY) return@enqueueMutation + val bridge = mFloatyWindow.windowBridge ?: return@enqueueMutation - val isLtr = mFloatyWindow!!.windowView!!.layoutDirection == View.LAYOUT_DIRECTION_LTR + val isLtr = mFloatyWindow.windowView!!.layoutDirection == View.LAYOUT_DIRECTION_LTR val w = if (bridge.width > 0) bridge.width else mConsoleFloaty.defaultViewWidth.roundToInt() val h = if (bridge.height > 0) bridge.height else mConsoleFloaty.defaultViewHeight.roundToInt() val pt = AndroidPoint( @@ -586,58 +588,82 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() { fun getGravity() = configurator.gravity @ScriptInterface - fun setTitleTextSize(size: Float) = runWithWindow({ configurator.setTitleTextSize(size) }) { - mConsoleFloaty.setTitleTextSize(size) + fun setTitleTextSize(size: Float) { + configurator.setTitleTextSize(size) + uiHandler.post { + mConsoleFloaty.setTitleTextSize(size) + } } @ScriptInterface fun getTitleTextSize() = mConsoleFloaty.getTitleTextSize() @ScriptInterface - fun setTitleTextColor(color: Int) = runWithWindow({ configurator.setTitleTextColor(color) }) { - mConsoleFloaty.setTitleTextColor(color) + fun setTitleTextColor(color: Int) { + configurator.setTitleTextColor(color) + uiHandler.post { + mConsoleFloaty.setTitleTextColor(color) + } } @ScriptInterface fun getTitleTextColor() = mConsoleFloaty.getTitleTextColor() @ScriptInterface - fun setTitleBackgroundColor(@ColorInt color: Int) = runWithWindow({ configurator.setTitleBackgroundColor(color) }) { - mConsoleFloaty.setTitleBackgroundColor(getTitleBackgroundColor()) + fun setTitleBackgroundColor(@ColorInt color: Int) { + configurator.setTitleBackgroundColor(color) + uiHandler.post { + mConsoleFloaty.setTitleBackgroundColor(getTitleBackgroundColor()) + } } @ScriptInterface fun getTitleBackgroundColor() = configurator.titleBackgroundColor @ScriptInterface - fun setTitleBackgroundTint(@ColorInt tint: Int) = runWithWindow({ configurator.setTitleBackgroundTint(tint) }) { - mConsoleFloaty.setTitleBackgroundTint(tint) + fun setTitleBackgroundTint(@ColorInt tint: Int) { + configurator.setTitleBackgroundTint(tint) + uiHandler.post { + mConsoleFloaty.setTitleBackgroundTint(tint) + } } @ScriptInterface - fun setTitleBackgroundAlpha(alpha: Int) = runWithWindow({ configurator.setTitleBackgroundAlpha(alpha) }) { - mConsoleFloaty.setTitleBackgroundAlpha(alpha) + fun setTitleBackgroundAlpha(alpha: Int) { + configurator.setTitleBackgroundAlpha(alpha) + uiHandler.post { + mConsoleFloaty.setTitleBackgroundAlpha(alpha) + } } @ScriptInterface fun resetTitleBackgroundAlpha() = setTitleBackgroundAlpha(-1) @ScriptInterface - fun setTitleIconsTint(color: Int) = runWithWindow({ configurator.setTitleIconsTint(color) }) { - mConsoleFloaty.setTitleIconsTint(color) + fun setTitleIconsTint(color: Int) { + configurator.setTitleIconsTint(color) + uiHandler.post { + mConsoleFloaty.setTitleIconsTint(color) + } } @ScriptInterface - fun setContentTextSize(size: Float) = runWithWindow({ configurator.setContentTextSize(size) }) { - getFloatingConsoleView()?.textSize = size + fun setContentTextSize(size: Float) { + configurator.setContentTextSize(size) + uiHandler.post { + getFloatingConsoleView()?.textSize = size + } } @ScriptInterface fun getContentTextSize() = getFloatingConsoleView()?.textSize ?: 0f @ScriptInterface - fun setContentTextColors(colors: Array) = runWithWindow({ configurator.setContentTextColors(colors) }) { - getFloatingConsoleView()?.setTextColors(colors) + fun setContentTextColors(colors: Array) { + configurator.setContentTextColors(colors) + uiHandler.post { + getFloatingConsoleView()?.setTextColors(colors) + } } @ScriptInterface @@ -646,21 +672,30 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() { } @ScriptInterface - fun setContentBackgroundColor(@ColorInt color: Int) = runWithWindow({ configurator.setContentBackgroundColor(color) }) { - setContentViewBackgroundColor(getFloatingConsoleView(), getContentBackgroundColor()) + fun setContentBackgroundColor(@ColorInt color: Int) { + configurator.setContentBackgroundColor(color) + uiHandler.post { + setContentViewBackgroundColor(getFloatingConsoleView(), getContentBackgroundColor()) + } } @ScriptInterface fun getContentBackgroundColor() = configurator.contentBackgroundColor @ScriptInterface - fun setContentBackgroundTint(@ColorInt tint: Int) = runWithWindow({ configurator.setContentBackgroundTint(tint) }) { - setContentViewBackgroundColor(getFloatingConsoleView(), getContentBackgroundColor()) + fun setContentBackgroundTint(@ColorInt tint: Int) { + configurator.setContentBackgroundTint(tint) + enqueueMutation(Stage.VIEW_ATTACHED) { + setContentViewBackgroundColor(getFloatingConsoleView(), getContentBackgroundColor()) + } } @ScriptInterface - fun setContentBackgroundAlpha(alpha: Int) = runWithWindow({ configurator.setContentBackgroundAlpha(alpha) }) { - setContentViewBackgroundColor(getFloatingConsoleView(), getContentBackgroundColor()) + fun setContentBackgroundAlpha(alpha: Int) { + configurator.setContentBackgroundAlpha(alpha) + enqueueMutation(Stage.VIEW_ATTACHED) { + setContentViewBackgroundColor(getFloatingConsoleView(), getContentBackgroundColor()) + } } @ScriptInterface @@ -719,11 +754,9 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() { else -> configurator.exitOnCloseTimeout } - fun submitInput(input: CharSequence) = mInput.offer(input.toString()) + fun expand() = uiHandler.post { mFloatyWindow.expand() } - fun expand() = uiHandler.post { mFloatyWindow?.expand() } - - fun collapse() = uiHandler.post { mFloatyWindow?.collapse() } + fun collapse() = uiHandler.post { mFloatyWindow.collapse() } @JvmOverloads fun hideDelayed(exitOnCloseTimeout: Long = configurator.exitOnCloseTimeout) { @@ -734,6 +767,9 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() { if (!isShowing) { cancel() } else when (/* remaining = */ ceil(millisUntilFinished / 1.0e3).toInt()) { + 9 -> setCloseButton(R.drawable.ic_looks_9_white_48dp) + 8 -> setCloseButton(R.drawable.ic_looks_8_white_48dp) + 7 -> setCloseButton(R.drawable.ic_looks_7_white_48dp) 6 -> setCloseButton(R.drawable.ic_looks_6_white_48dp) 5 -> setCloseButton(R.drawable.ic_looks_5_white_48dp) 4 -> setCloseButton(R.drawable.ic_looks_4_white_48dp) @@ -759,46 +795,8 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() { private fun getFloatingConsoleView() = mConsoleView?.get() as? FloatingConsoleView - private fun waitForConsoleView() { - synchronized(mLockConsoleView) { - try { - mLockConsoleView.wait() - } catch (_: InterruptedException) { - throw ScriptInterruptedException() - } - } - } - private fun setCloseButton(resId: Int) = mConsoleFloaty.setCloseButton(resId) - private fun runWithWindow(r: Runnable) { - mFloatyWindow ?: return - uiHandler.post(r) - } - - private fun runWithWindow(initializer: () -> Unit, r: Runnable) { - initializer() - runWithWindow(r) - } - - private fun trySetting(setter: () -> Unit) { - val f = Runnable { - synchronized(lock) { - var retries = 5 - while (retries-- > 0) { - try { - setter() - break - } catch (e: Exception) { - e.printStackTrace() - } - runCatching { Thread.sleep(mDefaultSafeDelay) } - } - } - } - if (isShowing) f.run() else mFloatyWindow!!.addOnViewAttachedTask { f.run() } - } - private fun setContentViewBackgroundColor(view: FloatingConsoleView?, color: Int) { view?.apply { background = DrawableUtils.setDrawableColorFilterSrc(background, color) } } @@ -808,6 +806,46 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() { else -> alpha } + private fun advanceStage(newStage: Stage) { + if (newStage.ordinal <= mStage.ordinal) return + mStage = newStage + drainMutations() + } + + private fun enqueueMutation(required: Stage, action: () -> Unit) { + uiHandler.post { + if (mStage.ordinal >= required.ordinal) { + runCatching { action() }.onFailure { it.printStackTrace() } + } else { + mPendingMutations.addLast(Mutation(required, action)) + } + } + } + + private fun drainMutations() { + if (mIsDraining) return + mIsDraining = true + try { + while (mPendingMutations.isNotEmpty()) { + val m = mPendingMutations.first() + if (mStage.ordinal >= m.required.ordinal) { + mPendingMutations.removeFirst() + runCatching { m.action() }.onFailure { it.printStackTrace() } + } else break + } + } finally { + mIsDraining = false + } + } + + /** + * Lock-free phased mutation queue. + * zh-CN: 无锁阶段化变更队列. + */ + private enum class Stage { INIT, WINDOW_CREATED, VIEW_ATTACHED, LAYOUT_DONE } + + private data class Mutation(val required: Stage, val action: () -> Unit) + interface LogListener { fun onNewLog(logEntry: LogEntry?) fun onLogClear() diff --git a/app/src/main/java/org/autojs/autojs/core/console/ConsoleView.java b/app/src/main/java/org/autojs/autojs/core/console/ConsoleView.java index ac739568..19fb1f3b 100644 --- a/app/src/main/java/org/autojs/autojs/core/console/ConsoleView.java +++ b/app/src/main/java/org/autojs/autojs/core/console/ConsoleView.java @@ -11,10 +11,7 @@ import android.view.LayoutInflater; import android.view.ScaleGestureDetector; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; -import android.widget.EditText; import android.widget.FrameLayout; -import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -22,7 +19,6 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import org.autojs.autojs.theme.ThemeColorHelper; import org.autojs.autojs.tool.MapBuilder; -import org.autojs.autojs.ui.enhancedfloaty.ResizableExpandableFloatyWindow; import org.autojs.autojs.ui.log.LogActivity; import org.autojs.autojs.util.DisplayUtils; import org.autojs.autojs.util.ViewUtils; @@ -45,9 +41,6 @@ public class ConsoleView extends FrameLayout implements ConsoleImpl.LogListener private ConsoleImpl mConsole; private WeakReference mLogActivity = null; private RecyclerView mLogListRecyclerView; - private EditText mEditText; - private LinearLayout mInputContainer; - private ResizableExpandableFloatyWindow mWindow; private boolean mShouldStopRefresh = false; private final ArrayList mLogEntries = new ArrayList<>(); @@ -129,9 +122,6 @@ public class ConsoleView extends FrameLayout implements ConsoleImpl.LogListener return !mScaleGestureDetector.isInProgress() && super.onTouchEvent(event); }); - initEditText(); - initSubmitButton(); - if (isExcludeFromNavigationBar) { ViewUtils.excludePaddingClippableViewFromBottomNavigationBar(mLogListRecyclerView); } @@ -249,46 +239,11 @@ public class ConsoleView extends FrameLayout implements ConsoleImpl.LogListener .build(); } - private void initSubmitButton() { - final Button submit = findViewById(R.id.submit); - submit.setOnClickListener(v -> { - CharSequence input = mEditText.getText(); - submitInput(input); - }); - } - - private void submitInput(CharSequence input) { - if (android.text.TextUtils.isEmpty(input)) { - return; - } - if (mConsole.submitInput(input)) { - mEditText.setText(""); - } - } - - private void initEditText() { - mEditText = findViewById(R.id.input); - mEditText.setFocusableInTouchMode(true); - mInputContainer = findViewById(R.id.input_container); - OnClickListener listener = v -> { - if (mWindow != null) { - mWindow.requestWindowFocus(); - mEditText.requestFocus(); - } - }; - mEditText.setOnClickListener(listener); - mInputContainer.setOnClickListener(listener); - } - public void setConsole(ConsoleImpl console) { mConsole = console; mConsole.setConsoleView(this); } - public void setInputContainerVisibility(int visibility) { - mInputContainer.setVisibility(visibility); - } - public void setLogActivity(LogActivity activity) { mLogActivity = new WeakReference<>(activity); } @@ -361,18 +316,6 @@ public class ConsoleView extends FrameLayout implements ConsoleImpl.LogListener } } - public void setWindow(ResizableExpandableFloatyWindow window) { - mWindow = window; - } - - public void showEditText() { - post(() -> { - mWindow.requestWindowFocus(); - // mInputContainer.setVisibility(VISIBLE); - mEditText.requestFocus(); - }); - } - private class ViewHolder extends RecyclerView.ViewHolder { TextView textView; diff --git a/app/src/main/java/org/autojs/autojs/inrt/LogActivity.kt b/app/src/main/java/org/autojs/autojs/inrt/LogActivity.kt index 1f7c4a8c..42fd90a6 100644 --- a/app/src/main/java/org/autojs/autojs/inrt/LogActivity.kt +++ b/app/src/main/java/org/autojs/autojs/inrt/LogActivity.kt @@ -32,7 +32,6 @@ class LogActivity : BaseActivity() { } binding.console.apply { setConsole(AutoJs.instance.globalConsole as ConsoleImpl) - findViewById(R.id.input_container).visibility = View.GONE } } } diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/Console.kt b/app/src/main/java/org/autojs/autojs/runtime/api/Console.kt index 0ca58a9c..ee477981 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/Console.kt +++ b/app/src/main/java/org/autojs/autojs/runtime/api/Console.kt @@ -74,7 +74,4 @@ interface Console { @ScriptInterface fun setTouchable(touchable: Boolean) - @ScriptInterface - fun rawInput(): String? - } \ No newline at end of file diff --git a/app/src/main/res/drawable-xhdpi/ic_looks_7_black_48dp.png b/app/src/main/res/drawable-xhdpi/ic_looks_7_black_48dp.png new file mode 100644 index 0000000000000000000000000000000000000000..89b757b1a37c3977d8c6a7aa70d4702f9bc092ad GIT binary patch literal 1459 zcmZ`(X)v1!0R6tW;s}-0YU_4?btDoz8MUIiDVIjliHG=Uci)skf>x=J z5X^ZOc$<2d8MY%B-NZ(qHdaS9o>i8gI2D#uc=i5()G-x>G?T3j(2jEkicxhi2lBPU zyu9@jLQ`s*igPO#&(ifxW8PGa)VY}C?yzviQ~S&}RhL$(=x(UYhw9Sv@`*TqV4MN| zuq4q_pe!)?hM8{iCFpxyT2+ll4v1SR@KjR9>FPi~n<})V=||5@cI9!-OP5H-0y?(1 zUM^c&Gg#Hd9(*qD!WIDo?#oY5L>&Q4Dt4~VBoM`ZF1pQiUxDro2WJs-wFDB1)b{$Wj=VjwcQ71}xbv;9 z5qv3PHsC9;udd(zB`eAXrd`8~iSbhs#tAKi{-25HvcQm#>}bqc7Z(KhCO5&! zZCbLV$04O6Q49uyd9?D$>D;-SLqn<~BPegrOPK0;3ot7&@p#+9y)hek1%=yveNCdM z0VikY^RM?(4VlG3)|xWl9g9cU!s24=@!j~Uni{P#3%{YcO~{NQEjqfBK5AT1RfTL3 zk2gc6NHLl{4Xj&lNjC)U?e9~4edik_g4y@=k*}@_#R`gw?z|mD4;ZlDUc4*SHORo! zX*If1%S!Y+Y6P@P2 z&KD1n7`e?<92MR^t3sdv6x|@1cd-rsU=0loy|OX{9le^n=mDJM>9H8v>5|s;C~}+J zCY7gNXR9i~7%n=%74~&E!?ED`%k@#na`$4f52WP@eNGL4UHgMiTFjA5fYX$B2br#) zlUul{iVVx3`c=3tXd}6CO<{Xy$52E;{k_Z`z$rWpO$iLVVOdUAYc`gYlyqo0e8X$z z?v>FLo15NEiup}kS-obM#7xH<1T2=qUIdbe{_UZjIUyGVv;Dz~%JEP*wUlPp4 z8O;q#{D~+r?JFR+=|W#tXf;mz2q>_*uIWi5VG+m5YrK_91|L8Vz{N6{HH@;ey$#h} zcrx!HKRFyVVG5h;lUSFNm$$LN<6R8OaaL6h+79S4?J`FU7W)Ab-k99!;Un^k(nc`d0LdU$wz$*j*E*kN>dO{wx_Ahxv@2XNTUHDfVe_l>TLwDm$g_K9Ccxz zULBpAj1a0R&gLw=Ulyx9i+$BtC%$V>95K@Hbi}5B8pKsMM8wfbavGg{K4lIMiEP&K zYr3!u_LF$V%%FsX61y9=6pHd}G=|WVx27mNqV|3F8pxm7qK}mZsNGcW(M6f*dXd{~ zggej!ixTrNdptk*&;)Mmlf7ot0IgLj$u(m-!kHyg$iOc`_(9wc756>-Lhz{AlbIpt}gv5$a~P3=K7$ z5}$uLo{^yk!S_B>6Z2DQh{>DRR81QKu5CfQf0LyljoG12;t{jTt-gWW4<8c!4o`gW rxyQ}&`k<24#Dl49qyPV}|B^#6h?%sZ!t$s*dSSqk{ literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_looks_7_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_looks_7_white_48dp.png new file mode 100644 index 0000000000000000000000000000000000000000..ce9725216e6de3d79aeb4bb048ba19122f01a301 GIT binary patch literal 3083 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_looks_8_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_looks_8_white_48dp.png new file mode 100644 index 0000000000000000000000000000000000000000..375a831a1b3b66774f929799fa6f1e239e685605 GIT binary patch literal 3754 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_looks_9_black_48dp.png b/app/src/main/res/drawable-xhdpi/ic_looks_9_black_48dp.png new file mode 100644 index 0000000000000000000000000000000000000000..2f8b8b7f4fee2d6c6f8685e0cea075310a037b96 GIT binary patch literal 2098 zcmbVN4NMbf7%qrGBt%fyOx1BaqM~@cySB8wlPW(&i=zx{Bglqwy}QCm+dJWM0qDkjHjv4eAo@|zF=j@Iox$G|B{u&$6)s^}@oFHF_kGF7D- zf0}a?%7wOElIA@cis7BC#_tgTTBS-`=ocuuoRuLbTgrLO@IZGD3~`JZE=aXuHX(~G z!d>SzugAw=tyf^`BFL;aM1;u=iJDF9^X5KgEN-GSf?jk0dm!+K>2VDS)EDC1>b zD&wC)9eVsk08m?-EfnLaT0EXm2uaSa1Ze~d@>I0ss1jJz&PsfRNVD0MKxUGXjX-9J zEG6@zgXi62PUVkDhO~MO1|=78UWWHca~|8kS}2({!@xBig5wCTb!c^@o*?z66b#sh zjY4fa!?~(L&{PD+5FPHo^@_pxlTgqz3?)-91T!@0;zbVy66QQqDT@l;QWy%Ak<8-V zya)mVb$CF9%|_;UC7JTlY>veYgPb)S$B>LZ6>}N&6r$G=7?R5BS;VAcX~aMq^(Mk; zNTs#8_2NHq`?g=lF)hqw%S1|et)9E=1;yNpgft}~3W zy(x0wMWfu0vnr|>z(|`6xQU{jh`~VX5Fmj@C>>ZiF&5K0jV9c{YL#vdGze+sBw$@t z@cmfEt_iN(92DpYlA@JfH^a1IGRwfh+uT#?@luh4`+a3BAbpPcqtPYaCHp9m%_s%6 zdtq~-&k`?DmCu*{a#oIN3Q2!fGyWgx!Lrb0l(&=xlM{uNWKdN&_Ien4Q2}|y( z>=)YObKCstl$i6!cK#Z+e{S*!yt23Bof{*qyCzTHF~jGxxA2L^y?>uzvSzJUWiO2f z-@1&o2Nl(-&NuQPYNUDxKncfa2>IKQ{PR62A+ zqBg}JPwyLikhx(&YIbzX+ReS!w>Gy9iQ78%B&Y3msEbbexAfLom!NG(#`yh9!#GE} zdMLc%cwd~;QWw|$e%PJ8j}oH}+&qIP-)x2VHyv9Y8QZ_6s8ihYVQhLy{^ylNi)$n6 zAI$GPyQ`-AgW+$SC_d2j$C1lfo{w)K1&dD>&!0h4T@|NRm)GRp%4;p`9DaDT>gSOw zH+B`O6B-wRkhO_hChglYG;W_TTex~?N5Y+m{sVjLmke*|zGdy*m1M@PL%XNAhZ?_{ zUfghh+=QuTw=Z95{VX2Cn%Wv(QMX0(ewq0F!`dsO(i0k8KdzYBz5MW>nFVp3Nk;|_ zHttNX>5l9&PP!eJTV*0q?H&+>Kf*RINmQ*cPq)tLJ3r KSq^8Gy!#JXeA!e0 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_looks_9_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_looks_9_white_48dp.png new file mode 100644 index 0000000000000000000000000000000000000000..b537e83f050b95a544bf39c80120784b43707a12 GIT binary patch literal 2415 zcmb_e3vd%v6b=0Vtu2*li-=Trmj7q7pZqpU1BHY(#io=tQ!0pMv-{GlO|mh&ZPM^9 z@+(sqK>-IV;Mj3+EEPwfpd$qaq(Y&<00V+pi;CzVpwvMG#KzAvtt*>9hqnZu^%Y5_dL~u5dZ^ab^;fBS zt`Y1da)~XE;Y5#`zALd34-ZBs0iB10Eo=NYLZ4q#h$_hM*X&28n!7=+4hOnL&w+`RkmevI8Xsf! z16CIOc2RV9SE`_!GD2z91TuW8;N``DG~%8Gz{1MFgu<$6Fp|Vb%1&t*t&!2vqX<|& zq6=yhdBIr~h3YVpz%-urOf3!p2a^QCbdTHE`r006{qnG^q!aqUNwb7^@(`(p5$F zM-g}37l1||QT#6NlIWBJtRIXogSmUG zxbTOOm)OcjQ|Glh!fDX5m{wyXFdfhWjMe}S({lzbZFJ~$Fpu4#N2%W4SMPU3Rg2P%!uHtp6_^zVN$ydMhkscnUUbWf(mw z;6wDV0bZCVKRinI{E#T9R59~%E#vLMy36a@UO$`l?2>tjdx|&gy+kfT&dnk#`*hCg z-;vcj?&iSi3DdX4&RsEh3H|Q*a?3{3_NryW$MTZzoAD}fHA&7x%}LEylXHc+r-pm}Jem1??)uh! z`?e1pbjZ7P`ODX59K2K$zqn;n_SrNh1)Z~TNt--qMs;lc(#-mzUT><8cMSa^CTCB5 ziiQ4YovYf6e|!RD>5di+H=muX-c~Z@%+Z(+GS<{2+)ga+*gd6(V9y$meq+g)x{eHo zWoK#aoVtQB>ynYcw-qg|H8l;xYFla#n>U}fSn{h|uaC;u9@>_+)KypXOR9HN=k)ek zv0&F%M>AF(Y_pUU)0cnVFk<1SG0*im*fX_}{X5QE7i!zN_^0_>S{voIi)9VtS;$dm^EP9?#_u@OIymRY3)sGEx-CF>yE~_OEp`voaQUp zsiR|W&b@6~vp-pmFU&Iyxp1U*()Y#HofTDWIb&DMK9G}HuzY*-u_SZe{$|I9Gsl~z z&a^GylC~TO^etSO{yTZ#o%NaJ2jgQdj(e@ - - - - -