6.7.0 - Alpha10 - 控制台浮动窗口内部实现进行无锁化及队列化处理以提升其参数设置效率与成功率

This commit is contained in:
SuperMonster003 committed 2025-11-01 13:34:33 +08:00
1 parent b8ecf3cc11
commit f0bbfce3f6
16 files changed
+271 -274

No files matched your search

+3 -1
View File
@@ -1,7 +1,7 @@
{ {
"$data": { "$data": {
"v6.7.0": { "v6.7.0": {
"released_date": "2025/10/27", "released_date": "2025/11/01",
"feature": [ "feature": [
"cvt 模块, 用于数据单位转换 (参阅 项目文档 > [单位转换](https://docs.autojs6.com/#/cvt))", "cvt 模块, 用于数据单位转换 (参阅 项目文档 > [单位转换](https://docs.autojs6.com/#/cvt))",
"fmt 模块, 用于数据格式化 (参阅 项目文档 > [格式化](https://docs.autojs6.com/#/fmt))", "fmt 模块, 用于数据格式化 (参阅 项目文档 > [格式化](https://docs.autojs6.com/#/fmt))",
@@ -58,6 +58,8 @@
"console.log 等方法打印全局对象 (images, app, ocr 等) 时支持显示详细信息", "console.log 等方法打印全局对象 (images, app, ocr 等) 时支持显示详细信息",
"android.graphics.Paint#setColor 支持正常解析 ColorInt/ColorHex/ColorName 等颜色参数", "android.graphics.Paint#setColor 支持正常解析 ColorInt/ColorHex/ColorName 等颜色参数",
"内置模块相关方法实参类型的异常消息增加类型摘要信息", "内置模块相关方法实参类型的异常消息增加类型摘要信息",
"控制台浮动窗口倒计时起始值由 6 秒增加到 9 秒",
"控制台浮动窗口内部实现进行无锁化及队列化处理以提升其参数设置效率与成功率",
"文件管理器浮动按钮展开后点击菜单项时优化菜单收起时机", "文件管理器浮动按钮展开后点击菜单项时优化菜单收起时机",
"崩溃报告页面支持双指缩放调整字体大小并添加常用功能按钮", "崩溃报告页面支持双指缩放调整字体大小并添加常用功能按钮",
"应用启动器图标支持自适应图标特性 _[`issue #405`](http://issues.autojs6.com/405)_", "应用启动器图标支持自适应图标特性 _[`issue #405`](http://issues.autojs6.com/405)_",
@@ -81,7 +81,7 @@ public class ConsoleView extends FrameLayout implements ConsoleImpl.LogListener
} }
private void init(AttributeSet attrs) { private void init(AttributeSet attrs) {
inflate(getContext(), R.layout.console_view, this); inflate(getContext(), R.layout.console_view_legacy, this);
if (attrs != null) { if (attrs != null) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ConsoleView); TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ConsoleView);
for (Map.Entry<Integer, Integer> attr : ATTRS.entrySet()) { for (Map.Entry<Integer, Integer> attr : ATTRS.entrySet()) {
@@ -12,6 +12,7 @@ import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.view.isVisible
import org.autojs.autojs.runtime.api.ScreenMetrics import org.autojs.autojs.runtime.api.ScreenMetrics
import org.autojs.autojs.ui.enhancedfloaty.FloatyService import org.autojs.autojs.ui.enhancedfloaty.FloatyService
import org.autojs.autojs.ui.enhancedfloaty.ResizableExpandableFloaty.AbstractResizableExpandableFloaty 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.FloatingConsoleExpandBinding
import org.autojs.autojs6.databinding.FloatingWindowCollapseBinding import org.autojs.autojs6.databinding.FloatingWindowCollapseBinding
import kotlin.math.roundToInt import kotlin.math.roundToInt
import androidx.core.view.isVisible
/** /**
* Created by Stardust on Apr 20, 2017. * Created by Stardust on Apr 20, 2017.
@@ -143,7 +143,6 @@ class ConsoleFloaty(private val console: ConsoleImpl) : AbstractResizableExpanda
private fun setUpConsole(window: ResizableExpandableFloatyWindow) { private fun setUpConsole(window: ResizableExpandableFloatyWindow) {
expandedBinding.console.apply { expandedBinding.console.apply {
setConsole(console) setConsole(console)
setWindow(window)
} }
initConsoleTitleBar(window) initConsoleTitleBar(window)
} }
@@ -204,8 +203,13 @@ class ConsoleFloaty(private val console: ConsoleImpl) : AbstractResizableExpanda
} }
internal fun setCloseButton(resId: Int) { internal fun setCloseButton(resId: Int) {
mCloseButton?.apply { mCloseButton?.let {
post { setImageDrawable(ResourcesCompat.getDrawable(console.uiHandler.applicationContext.resources, resId, null)) } 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)
}
} }
} }
@@ -22,6 +22,7 @@ import androidx.core.graphics.alpha
import androidx.core.graphics.blue import androidx.core.graphics.blue
import androidx.core.graphics.green import androidx.core.graphics.green
import androidx.core.graphics.red import androidx.core.graphics.red
import androidx.core.view.doOnLayout
import com.afollestad.materialdialogs.DialogAction import com.afollestad.materialdialogs.DialogAction
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import org.autojs.autojs.annotation.ScriptInterface 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.ScriptRuntime
import org.autojs.autojs.runtime.api.AbstractConsole import org.autojs.autojs.runtime.api.AbstractConsole
import org.autojs.autojs.runtime.api.Mime import org.autojs.autojs.runtime.api.Mime
import org.autojs.autojs.runtime.exception.ScriptInterruptedException
import org.autojs.autojs.tool.UiHandler import org.autojs.autojs.tool.UiHandler
import org.autojs.autojs.ui.common.NotAskAgainDialog import org.autojs.autojs.ui.common.NotAskAgainDialog
import org.autojs.autojs.ui.enhancedfloaty.FloatyService import org.autojs.autojs.ui.enhancedfloaty.FloatyService
@@ -48,8 +48,6 @@ import org.opencv.core.Point
import org.opencv.core.Size import org.opencv.core.Size
import java.io.IOException import java.io.IOException
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.BlockingQueue
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.pow import kotlin.math.pow
@@ -63,17 +61,8 @@ import android.graphics.Point as AndroidPoint
*/ */
open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() { open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
private val lock = Object() private val applicationContext = uiHandler.applicationContext
var configurator = Configurator()
val logEntries = ArrayList<LogEntry>()
@Volatile
var isShowing = false
private set
private val mDefaultSafeDelay: Long = 360
private val mSafeSizeToSend: Int = 2.0.pow(16).toInt() private val mSafeSizeToSend: Int = 2.0.pow(16).toInt()
private val mSafeSizeToCopy: Int = 2.0.pow(19).toInt() private val mSafeSizeToCopy: Int = 2.0.pow(19).toInt()
@@ -83,31 +72,30 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
@get:Synchronized @get:Synchronized
private var mCountDownTimer: CountDownTimer? = null 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<Mutation>()
private var mIsDraining = false
private val context: Context private val context: Context
get() = mConsoleView?.get()?.context ?: uiHandler.applicationContext get() = mConsoleView?.get()?.context ?: 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<String> = ArrayBlockingQueue(1)
private val mDisplayOverOtherAppsPerm = DisplayOverOtherAppsPermission(uiHandler.applicationContext)
private val logEntriesJoint private val logEntriesJoint
get() = synchronized(logEntries) { get() = synchronized(logEntries) {
logEntries.joinToString("\n") { it.content } logEntries.joinToString("\n") { it.content }
} }
init { val configurator = Configurator()
synchronized(mLockWindowCreated) {
mConsoleFloaty = ConsoleFloaty(this) val logEntries = ArrayList<LogEntry>()
mFloatyWindow = ResizableExpandableFloatyWindow(mConsoleFloaty)
mLockWindowCreated.notify() @Volatile
} var isShowing = false
} private set
override fun setTitle(title: CharSequence?) { override fun setTitle(title: CharSequence?) {
val niceTitle = title ?: "" val niceTitle = title ?: ""
@@ -117,11 +105,6 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
fun getTitle() = mConsoleFloaty.getTitle() 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?) { override fun error(data: Any?, vararg formatArgs: Any?) {
var niceData = data var niceData = data
if (niceData is Throwable) { 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?) { fun setConsoleView(consoleView: ConsoleView?) {
consoleView.let { consoleView.let {
mConsoleView = WeakReference(it) mConsoleView = WeakReference(it)
addLogListener(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) { fun printAllStackTrace(t: Throwable) {
@@ -314,7 +287,7 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
putExtra(Intent.EXTRA_TEXT, message) putExtra(Intent.EXTRA_TEXT, message)
type = Mime.TEXT_PLAIN 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) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}) })
} }
@@ -363,47 +336,61 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
setCloseButton(R.drawable.ic_close_white_24dp) setCloseButton(R.drawable.ic_close_white_24dp)
if (isReset) clearStates() if (isReset) clearStates()
synchronized(mLockWindowShow) { if (!mDisplayOverOtherAppsPerm.has()) {
if (!mDisplayOverOtherAppsPerm.has()) { mDisplayOverOtherAppsPerm.config()
mDisplayOverOtherAppsPerm.config() ViewUtils.showToast(context, R.string.error_no_display_over_other_apps_permission)
ViewUtils.showToast(context, R.string.error_no_display_over_other_apps_permission) return
return }
} if (isReset) reset()
if (isReset) reset() if (isShowing) return
if (isShowing) return
runWithWindow({ startFloatyService() }) { // Reset stage.
try { // zh-CN: 重置阶段.
FloatyService.addWindow(mFloatyWindow) mStage = Stage.INIT
configurator.size?.let { FloatyService.setInitialMeasure(it) }
setTitleBarGestureListener() startFloatyService()
// SecurityException: https://github.com/hyb1996-guest/AutoJsIssueReport/issues/4781 uiHandler.post {
} catch (e: Exception) { try {
e.printStackTrace() FloatyService.addWindow(mFloatyWindow)
ScriptRuntime.popException(e.message) configurator.size?.let { FloatyService.setInitialMeasure(it) }
ViewUtils.showToast(context, R.string.error_no_display_over_other_apps_permission) 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) }
} }
} } catch (e: Exception) {
synchronized(mLockWindowCreated) { e.printStackTrace()
isShowing = true ScriptRuntime.popException(e.message)
configurator.title?.let { setTitle(it) } ViewUtils.showToast(context, R.string.error_no_display_over_other_apps_permission)
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)
} }
} }
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() { private fun setTitleBarGestureListener() {
mFloatyWindow?.windowBridge?.let { windowBridge -> mFloatyWindow.windowBridge?.let { windowBridge ->
mConsoleFloaty.titleBarView?.let { view -> mConsoleFloaty.titleBarView?.let { view ->
DragGesture(windowBridge, view).apply { pressedAlpha = 1.0f } DragGesture(windowBridge, view).apply { pressedAlpha = 1.0f }
} }
@@ -413,17 +400,17 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
private fun clearStates() { private fun clearStates() {
FloatyService.setInitialMeasure(null) FloatyService.setInitialMeasure(null)
mConsoleFloaty.clearStates() mConsoleFloaty.clearStates()
mFloatyWindow?.clearOnViewAttachedTask() mFloatyWindow.clearOnViewAttachedTask()
configurator = Configurator() configurator.clearStates()
} }
fun reset() { fun reset() {
if (isShowing) { if (isShowing) {
hide(true) hide(true)
uiHandler.postDelayed({ uiHandler.post {
clearStates() clearStates()
show() show()
}, mDefaultSafeDelay) }
} else { } else {
clearStates() clearStates()
} }
@@ -437,17 +424,17 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
} }
private fun startFloatyService() { private fun startFloatyService() {
uiHandler.applicationContext.startService(Intent(uiHandler.applicationContext, FloatyService::class.java)) applicationContext.startService(Intent(applicationContext, FloatyService::class.java))
} }
@ScriptInterface @ScriptInterface
override fun hide() = hide(false) override fun hide() = hide(false)
private fun hide(ignoreSavingStates: Boolean) = runWithWindow { private fun hide(ignoreSavingStates: Boolean) {
synchronized(mLockWindowShow) { uiHandler.post {
try { try {
if (!ignoreSavingStates) saveStates() if (!ignoreSavingStates) saveStates()
mFloatyWindow!!.close() mFloatyWindow.close()
} catch (_: IllegalArgumentException) { } catch (_: IllegalArgumentException) {
/* Ignored. */ /* 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. // @Hint by SuperMonster003 on Feb 9, 2024.
// ! Position and size need to be saved as user may cause their changes. // ! Position and size need to be saved as user may cause their changes.
// ! zh-CN: 需要保存位置和大小, 因为用户可能会改变它们. // ! zh-CN: 需要保存位置和大小, 因为用户可能会改变它们.
val bridge = mFloatyWindow!!.windowBridge ?: return@runWithWindow val bridge = mFloatyWindow.windowBridge ?: return@post
configurator.setPosition(bridge.x.toDouble(), bridge.y.toDouble()) configurator.setPosition(bridge.x.toDouble(), bridge.y.toDouble())
configurator.setGravity(Gravity.NO_GRAVITY) configurator.setGravity(Gravity.NO_GRAVITY)
@@ -468,8 +455,9 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
} }
@ScriptInterface @ScriptInterface
override fun setSize(w: Double, h: Double) = runWithWindow({ configurator.setSize(w, h) }) { override fun setSize(w: Double, h: Double) {
trySetting { configurator.setSize(w, h)
enqueueMutation(Stage.VIEW_ATTACHED) {
mConsoleFloaty.expandedView?.let { mConsoleFloaty.expandedView?.let {
ViewUtils.setViewMeasure(it, w.toInt(), h.toInt()) ViewUtils.setViewMeasure(it, w.toInt(), h.toInt())
} }
@@ -487,9 +475,10 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
fun setTouchable() = setTouchable(DEFAULT_TOUCHABLE) fun setTouchable() = setTouchable(DEFAULT_TOUCHABLE)
@ScriptInterface @ScriptInterface
override fun setTouchable(touchable: Boolean) = runWithWindow({ configurator.setTouchable(touchable) }) { override fun setTouchable(touchable: Boolean) {
trySetting { configurator.setTouchable(touchable)
mFloatyWindow!!.setTouchable(touchable) enqueueMutation(Stage.WINDOW_CREATED) {
mFloatyWindow.setTouchable(touchable)
} }
} }
@@ -516,31 +505,44 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
@ScriptInterface @ScriptInterface
override fun setPosition(x: Double, y: Double) = setPosition(x, y, false) 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) configurator.setPosition(x, y)
if (!isSaveGravity) { if (!shouldSaveGravity) {
configurator.setGravity(Gravity.NO_GRAVITY) configurator.setGravity(Gravity.NO_GRAVITY)
} }
}) { // Wait for VIEW_ATTACHED before setting to avoid attach backfill override.
trySetting { mFloatyWindow!!.windowBridge?.updatePosition(x.toInt(), y.toInt()) } // 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 @ScriptInterface
fun getPosition(): Point { fun getPosition(): Point {
return mFloatyWindow.let { floatyWindow -> return mFloatyWindow.let { floatyWindow ->
floatyWindow?.windowBridge ?: return Point() floatyWindow.windowBridge ?: return Point()
Point(floatyWindow.windowBridge!!.x.toDouble(), floatyWindow.windowBridge!!.y.toDouble()) Point(floatyWindow.windowBridge!!.x.toDouble(), floatyWindow.windowBridge!!.y.toDouble())
} }
} }
@SuppressLint("RtlHardcoded") @SuppressLint("RtlHardcoded")
@ScriptInterface @ScriptInterface
fun setGravity(gravity: Int) = runWithWindow({ configurator.setGravity(gravity) }) { fun setGravity(gravity: Int) {
trySetting { configurator.setGravity(gravity)
if (gravity <= Gravity.NO_GRAVITY) return@trySetting enqueueMutation(Stage.LAYOUT_DONE) {
val bridge = mFloatyWindow!!.windowBridge ?: return@trySetting 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 w = if (bridge.width > 0) bridge.width else mConsoleFloaty.defaultViewWidth.roundToInt()
val h = if (bridge.height > 0) bridge.height else mConsoleFloaty.defaultViewHeight.roundToInt() val h = if (bridge.height > 0) bridge.height else mConsoleFloaty.defaultViewHeight.roundToInt()
val pt = AndroidPoint( val pt = AndroidPoint(
@@ -586,58 +588,82 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
fun getGravity() = configurator.gravity fun getGravity() = configurator.gravity
@ScriptInterface @ScriptInterface
fun setTitleTextSize(size: Float) = runWithWindow({ configurator.setTitleTextSize(size) }) { fun setTitleTextSize(size: Float) {
mConsoleFloaty.setTitleTextSize(size) configurator.setTitleTextSize(size)
uiHandler.post {
mConsoleFloaty.setTitleTextSize(size)
}
} }
@ScriptInterface @ScriptInterface
fun getTitleTextSize() = mConsoleFloaty.getTitleTextSize() fun getTitleTextSize() = mConsoleFloaty.getTitleTextSize()
@ScriptInterface @ScriptInterface
fun setTitleTextColor(color: Int) = runWithWindow({ configurator.setTitleTextColor(color) }) { fun setTitleTextColor(color: Int) {
mConsoleFloaty.setTitleTextColor(color) configurator.setTitleTextColor(color)
uiHandler.post {
mConsoleFloaty.setTitleTextColor(color)
}
} }
@ScriptInterface @ScriptInterface
fun getTitleTextColor() = mConsoleFloaty.getTitleTextColor() fun getTitleTextColor() = mConsoleFloaty.getTitleTextColor()
@ScriptInterface @ScriptInterface
fun setTitleBackgroundColor(@ColorInt color: Int) = runWithWindow({ configurator.setTitleBackgroundColor(color) }) { fun setTitleBackgroundColor(@ColorInt color: Int) {
mConsoleFloaty.setTitleBackgroundColor(getTitleBackgroundColor()) configurator.setTitleBackgroundColor(color)
uiHandler.post {
mConsoleFloaty.setTitleBackgroundColor(getTitleBackgroundColor())
}
} }
@ScriptInterface @ScriptInterface
fun getTitleBackgroundColor() = configurator.titleBackgroundColor fun getTitleBackgroundColor() = configurator.titleBackgroundColor
@ScriptInterface @ScriptInterface
fun setTitleBackgroundTint(@ColorInt tint: Int) = runWithWindow({ configurator.setTitleBackgroundTint(tint) }) { fun setTitleBackgroundTint(@ColorInt tint: Int) {
mConsoleFloaty.setTitleBackgroundTint(tint) configurator.setTitleBackgroundTint(tint)
uiHandler.post {
mConsoleFloaty.setTitleBackgroundTint(tint)
}
} }
@ScriptInterface @ScriptInterface
fun setTitleBackgroundAlpha(alpha: Int) = runWithWindow({ configurator.setTitleBackgroundAlpha(alpha) }) { fun setTitleBackgroundAlpha(alpha: Int) {
mConsoleFloaty.setTitleBackgroundAlpha(alpha) configurator.setTitleBackgroundAlpha(alpha)
uiHandler.post {
mConsoleFloaty.setTitleBackgroundAlpha(alpha)
}
} }
@ScriptInterface @ScriptInterface
fun resetTitleBackgroundAlpha() = setTitleBackgroundAlpha(-1) fun resetTitleBackgroundAlpha() = setTitleBackgroundAlpha(-1)
@ScriptInterface @ScriptInterface
fun setTitleIconsTint(color: Int) = runWithWindow({ configurator.setTitleIconsTint(color) }) { fun setTitleIconsTint(color: Int) {
mConsoleFloaty.setTitleIconsTint(color) configurator.setTitleIconsTint(color)
uiHandler.post {
mConsoleFloaty.setTitleIconsTint(color)
}
} }
@ScriptInterface @ScriptInterface
fun setContentTextSize(size: Float) = runWithWindow({ configurator.setContentTextSize(size) }) { fun setContentTextSize(size: Float) {
getFloatingConsoleView()?.textSize = size configurator.setContentTextSize(size)
uiHandler.post {
getFloatingConsoleView()?.textSize = size
}
} }
@ScriptInterface @ScriptInterface
fun getContentTextSize() = getFloatingConsoleView()?.textSize ?: 0f fun getContentTextSize() = getFloatingConsoleView()?.textSize ?: 0f
@ScriptInterface @ScriptInterface
fun setContentTextColors(colors: Array<out Int?>) = runWithWindow({ configurator.setContentTextColors(colors) }) { fun setContentTextColors(colors: Array<out Int?>) {
getFloatingConsoleView()?.setTextColors(colors) configurator.setContentTextColors(colors)
uiHandler.post {
getFloatingConsoleView()?.setTextColors(colors)
}
} }
@ScriptInterface @ScriptInterface
@@ -646,21 +672,30 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
} }
@ScriptInterface @ScriptInterface
fun setContentBackgroundColor(@ColorInt color: Int) = runWithWindow({ configurator.setContentBackgroundColor(color) }) { fun setContentBackgroundColor(@ColorInt color: Int) {
setContentViewBackgroundColor(getFloatingConsoleView(), getContentBackgroundColor()) configurator.setContentBackgroundColor(color)
uiHandler.post {
setContentViewBackgroundColor(getFloatingConsoleView(), getContentBackgroundColor())
}
} }
@ScriptInterface @ScriptInterface
fun getContentBackgroundColor() = configurator.contentBackgroundColor fun getContentBackgroundColor() = configurator.contentBackgroundColor
@ScriptInterface @ScriptInterface
fun setContentBackgroundTint(@ColorInt tint: Int) = runWithWindow({ configurator.setContentBackgroundTint(tint) }) { fun setContentBackgroundTint(@ColorInt tint: Int) {
setContentViewBackgroundColor(getFloatingConsoleView(), getContentBackgroundColor()) configurator.setContentBackgroundTint(tint)
enqueueMutation(Stage.VIEW_ATTACHED) {
setContentViewBackgroundColor(getFloatingConsoleView(), getContentBackgroundColor())
}
} }
@ScriptInterface @ScriptInterface
fun setContentBackgroundAlpha(alpha: Int) = runWithWindow({ configurator.setContentBackgroundAlpha(alpha) }) { fun setContentBackgroundAlpha(alpha: Int) {
setContentViewBackgroundColor(getFloatingConsoleView(), getContentBackgroundColor()) configurator.setContentBackgroundAlpha(alpha)
enqueueMutation(Stage.VIEW_ATTACHED) {
setContentViewBackgroundColor(getFloatingConsoleView(), getContentBackgroundColor())
}
} }
@ScriptInterface @ScriptInterface
@@ -719,11 +754,9 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
else -> configurator.exitOnCloseTimeout 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 @JvmOverloads
fun hideDelayed(exitOnCloseTimeout: Long = configurator.exitOnCloseTimeout) { fun hideDelayed(exitOnCloseTimeout: Long = configurator.exitOnCloseTimeout) {
@@ -734,6 +767,9 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
if (!isShowing) { if (!isShowing) {
cancel() cancel()
} else when (/* remaining = */ ceil(millisUntilFinished / 1.0e3).toInt()) { } 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) 6 -> setCloseButton(R.drawable.ic_looks_6_white_48dp)
5 -> setCloseButton(R.drawable.ic_looks_5_white_48dp) 5 -> setCloseButton(R.drawable.ic_looks_5_white_48dp)
4 -> setCloseButton(R.drawable.ic_looks_4_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 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 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) { private fun setContentViewBackgroundColor(view: FloatingConsoleView?, color: Int) {
view?.apply { background = DrawableUtils.setDrawableColorFilterSrc(background, color) } view?.apply { background = DrawableUtils.setDrawableColorFilterSrc(background, color) }
} }
@@ -808,6 +806,46 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
else -> alpha 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 { interface LogListener {
fun onNewLog(logEntry: LogEntry?) fun onNewLog(logEntry: LogEntry?)
fun onLogClear() fun onLogClear()
@@ -11,10 +11,7 @@ import android.view.LayoutInflater;
import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@@ -22,7 +19,6 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import org.autojs.autojs.theme.ThemeColorHelper; import org.autojs.autojs.theme.ThemeColorHelper;
import org.autojs.autojs.tool.MapBuilder; import org.autojs.autojs.tool.MapBuilder;
import org.autojs.autojs.ui.enhancedfloaty.ResizableExpandableFloatyWindow;
import org.autojs.autojs.ui.log.LogActivity; import org.autojs.autojs.ui.log.LogActivity;
import org.autojs.autojs.util.DisplayUtils; import org.autojs.autojs.util.DisplayUtils;
import org.autojs.autojs.util.ViewUtils; import org.autojs.autojs.util.ViewUtils;
@@ -45,9 +41,6 @@ public class ConsoleView extends FrameLayout implements ConsoleImpl.LogListener
private ConsoleImpl mConsole; private ConsoleImpl mConsole;
private WeakReference<LogActivity> mLogActivity = null; private WeakReference<LogActivity> mLogActivity = null;
private RecyclerView mLogListRecyclerView; private RecyclerView mLogListRecyclerView;
private EditText mEditText;
private LinearLayout mInputContainer;
private ResizableExpandableFloatyWindow mWindow;
private boolean mShouldStopRefresh = false; private boolean mShouldStopRefresh = false;
private final ArrayList<ConsoleImpl.LogEntry> mLogEntries = new ArrayList<>(); private final ArrayList<ConsoleImpl.LogEntry> mLogEntries = new ArrayList<>();
@@ -129,9 +122,6 @@ public class ConsoleView extends FrameLayout implements ConsoleImpl.LogListener
return !mScaleGestureDetector.isInProgress() && super.onTouchEvent(event); return !mScaleGestureDetector.isInProgress() && super.onTouchEvent(event);
}); });
initEditText();
initSubmitButton();
if (isExcludeFromNavigationBar) { if (isExcludeFromNavigationBar) {
ViewUtils.excludePaddingClippableViewFromBottomNavigationBar(mLogListRecyclerView); ViewUtils.excludePaddingClippableViewFromBottomNavigationBar(mLogListRecyclerView);
} }
@@ -249,46 +239,11 @@ public class ConsoleView extends FrameLayout implements ConsoleImpl.LogListener
.build(); .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) { public void setConsole(ConsoleImpl console) {
mConsole = console; mConsole = console;
mConsole.setConsoleView(this); mConsole.setConsoleView(this);
} }
public void setInputContainerVisibility(int visibility) {
mInputContainer.setVisibility(visibility);
}
public void setLogActivity(LogActivity activity) { public void setLogActivity(LogActivity activity) {
mLogActivity = new WeakReference<>(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 { private class ViewHolder extends RecyclerView.ViewHolder {
TextView textView; TextView textView;
@@ -32,7 +32,6 @@ class LogActivity : BaseActivity() {
} }
binding.console.apply { binding.console.apply {
setConsole(AutoJs.instance.globalConsole as ConsoleImpl) setConsole(AutoJs.instance.globalConsole as ConsoleImpl)
findViewById<View>(R.id.input_container).visibility = View.GONE
} }
} }
} }
@@ -74,7 +74,4 @@ interface Console {
@ScriptInterface @ScriptInterface
fun setTouchable(touchable: Boolean) fun setTouchable(touchable: Boolean)
@ScriptInterface
fun rawInput(): String?
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

-29
View File
@@ -11,33 +11,4 @@
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" /> android:layout_weight="1" />
<LinearLayout
android:id="@+id/input_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:orientation="horizontal"
android:visibility="visible">
<EditText
android:id="@+id/input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:lines="1"
android:textColor="@android:color/white"
android:textSize="14sp"
android:visibility="gone"/>
<Button
android:id="@+id/submit"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/text_ok"
android:textColor="@android:color/white"
android:theme="@style/ConsoleTheme"
android:visibility="gone"/>
</LinearLayout>
</LinearLayout> </LinearLayout>
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/log_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<LinearLayout
android:id="@+id/input_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:orientation="horizontal"
android:visibility="visible">
<EditText
android:id="@+id/input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:lines="1"
android:textColor="@android:color/white"
android:textSize="14sp"
android:visibility="gone"/>
<Button
android:id="@+id/submit"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/text_ok"
android:textColor="@android:color/white"
android:theme="@style/ConsoleTheme"
android:visibility="gone"/>
</LinearLayout>
</LinearLayout>
@@ -43,7 +43,7 @@
android:paddingBottom="8dp" android:paddingBottom="8dp"
android:paddingStart="8dp" android:paddingStart="8dp"
android:paddingEnd="8dp" android:paddingEnd="8dp"
android:paddingTop="24dp" android:paddingTop="20dp"
android:scaleType="center" android:scaleType="center"
android:src="@drawable/ic_remove_white_24dp" android:src="@drawable/ic_remove_white_24dp"
app:tint="@android:color/white" /> app:tint="@android:color/white" />