Merge branch 'little-alei_master'
# Conflicts: # version.properties
This commit is contained in:
@@ -61,6 +61,23 @@ module.exports = function (scriptRuntime, scope) {
|
||||
return util.java.toJsArray(a11yBridge.windowRoots(), false)
|
||||
.map(root => UiObject.createRoot(root));
|
||||
},
|
||||
stateListener(listener) {
|
||||
return a11yBridge.setAccessibilityListener(listener);
|
||||
},
|
||||
registerEvent(name, listener) {
|
||||
return rtAutomator.registerEvent(name, listener);
|
||||
},
|
||||
/** @deprecated */
|
||||
registerEvents(name, listener) {
|
||||
return rtAutomator.registerEvent(name, listener);
|
||||
},
|
||||
removeEvent(name) {
|
||||
return rtAutomator.removeEvent(name);
|
||||
},
|
||||
/** @deprecated */
|
||||
removeEvents(name) {
|
||||
return rtAutomator.removeEvent(name);
|
||||
},
|
||||
waitFor(timeout) {
|
||||
automator.waitForService(timeout);
|
||||
},
|
||||
|
||||
@@ -70,7 +70,7 @@ public class ReadOnlyUiObject extends UiObject {
|
||||
|
||||
@Override
|
||||
public String id() {
|
||||
return mNodeInfo.getSimpleId();
|
||||
return mNodeInfo.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -150,4 +150,7 @@ public abstract class AccessibilityBridge {
|
||||
return mConfig;
|
||||
}
|
||||
|
||||
public void setAccessibilityListener(AccessibilityServiceCallback listener) {
|
||||
AccessibilityService.Companion.setCallback(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,33 +8,57 @@ import android.view.accessibility.AccessibilityEvent
|
||||
import android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED
|
||||
import android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
|
||||
import android.view.accessibility.AccessibilityNodeInfo
|
||||
import org.autojs.autojs.core.accessibility.SimpleActionAutomator.Companion.AccessibilityEventCallback
|
||||
import org.autojs.autojs.core.automator.AccessibilityEventWrapper
|
||||
import org.autojs.autojs.event.EventDispatcher
|
||||
import org.autojs.autojs.pref.Pref
|
||||
import java.util.TreeMap
|
||||
import java.util.*
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
|
||||
interface AccessibilityServiceCallback {
|
||||
fun onConnected()
|
||||
fun onDisconnected()
|
||||
}
|
||||
/**
|
||||
* Created by Stardust on 2017/5/2.
|
||||
*/
|
||||
open class AccessibilityService : android.accessibilityservice.AccessibilityService() {
|
||||
|
||||
val onKeyObserver = OnKeyListener.Observer()
|
||||
val keyInterrupterObserver = KeyInterceptor.Observer()
|
||||
var fastRootInActiveWindow: AccessibilityNodeInfo? = null
|
||||
|
||||
var bridge: AccessibilityBridge? = null
|
||||
private val eventBox = TreeMap<Int, AccessibilityEventCallback>()
|
||||
|
||||
private val gestureEventDispatcher = EventDispatcher<GestureListener>()
|
||||
|
||||
private val eventExecutor by lazy { Executors.newSingleThreadExecutor() }
|
||||
|
||||
override fun onAccessibilityEvent(event: AccessibilityEvent) {
|
||||
if (instance != this) {
|
||||
instance = this
|
||||
private fun eventNameToType(str: String): Int {
|
||||
return try {
|
||||
val sb = StringBuilder()
|
||||
sb.append("TYPE_")
|
||||
val upperCase = str.uppercase(Locale.getDefault())
|
||||
sb.append(upperCase)
|
||||
AccessibilityEvent::class.java.getField(sb.toString()).get(null) as Int
|
||||
} catch (unused: NoSuchFieldException) {
|
||||
throw IllegalArgumentException("unknown event type: $str")
|
||||
}
|
||||
}
|
||||
|
||||
fun addAccessibilityEventCallback(name: String, callback: AccessibilityEventCallback) {
|
||||
eventBox[eventNameToType(name)] = callback
|
||||
}
|
||||
|
||||
fun removeAccessibilityEventCallback(name: String) {
|
||||
eventBox.remove(eventNameToType(name))
|
||||
}
|
||||
|
||||
override fun onAccessibilityEvent(event: AccessibilityEvent) {
|
||||
if (instance != this) instance = this
|
||||
val type = event.eventType
|
||||
eventBox[type]?.onAccessibilityEvent(AccessibilityEventWrapper(event))
|
||||
if (containsAllEventTypes || eventTypes.contains(type)) {
|
||||
if (type == TYPE_WINDOW_STATE_CHANGED || type == TYPE_VIEW_FOCUSED) {
|
||||
rootInActiveWindow?.also { fastRootInActiveWindow = it }
|
||||
@@ -86,12 +110,12 @@ open class AccessibilityService : android.accessibilityservice.AccessibilityServ
|
||||
instance = null
|
||||
bridge = null
|
||||
eventExecutor.shutdownNow()
|
||||
callback?.onDisconnected()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
instance = this
|
||||
|
||||
serviceInfo = serviceInfo.apply {
|
||||
AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS.let {
|
||||
flags = (if (Pref.isStableModeEnabled) flags and it.inv() else flags or it)
|
||||
@@ -102,7 +126,7 @@ open class AccessibilityService : android.accessibilityservice.AccessibilityServ
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
callback?.onConnected()
|
||||
super.onServiceConnected()
|
||||
|
||||
LOCK.lock()
|
||||
@@ -122,6 +146,7 @@ open class AccessibilityService : android.accessibilityservice.AccessibilityServ
|
||||
|
||||
private val LOCK = ReentrantLock()
|
||||
private val ENABLED = LOCK.newCondition()
|
||||
private var callback: AccessibilityServiceCallback? = null
|
||||
|
||||
var instance: AccessibilityService? = null
|
||||
private set
|
||||
@@ -141,6 +166,7 @@ open class AccessibilityService : android.accessibilityservice.AccessibilityServ
|
||||
fun isNotRunning() = !isRunning()
|
||||
|
||||
fun addDelegate(uniquePriority: Int, delegate: AccessibilityDelegate) {
|
||||
// 用于记录eventTypes中的事件id
|
||||
delegates[uniquePriority] = delegate
|
||||
val set = delegate.eventTypes
|
||||
if (set == null) {
|
||||
@@ -178,13 +204,16 @@ open class AccessibilityService : android.accessibilityservice.AccessibilityServ
|
||||
LOCK.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
interface GestureListener {
|
||||
|
||||
fun onGesture(gestureId: Int)
|
||||
|
||||
@JvmStatic
|
||||
fun clearAccessibilityEventCallback() {
|
||||
instance?.eventBox?.clear()
|
||||
}
|
||||
fun setCallback(listener: AccessibilityServiceCallback) {
|
||||
callback = listener
|
||||
}
|
||||
|
||||
interface GestureListener {
|
||||
fun onGesture(gestureId: Int)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -61,8 +61,7 @@ class LayoutInspector(private val mContext: Context) {
|
||||
}
|
||||
|
||||
private fun refreshChildList(root: AccessibilityNodeInfo?) {
|
||||
if (root == null)
|
||||
return
|
||||
if (root == null) return
|
||||
root.refresh()
|
||||
val childCount = root.childCount
|
||||
for (i in 0 until childCount) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.autojs.autojs.core.accessibility
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Resources
|
||||
@@ -17,7 +18,7 @@ import java.util.HashMap
|
||||
* Modified by SuperMonster003 as of Jun 17, 2022.
|
||||
*/
|
||||
|
||||
@Suppress("unused")
|
||||
@Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||
@Keep
|
||||
class NodeInfo(private val resources: Resources?, private val node: UiObject, var parent: NodeInfo?) {
|
||||
|
||||
@@ -27,8 +28,8 @@ class NodeInfo(private val resources: Resources?, private val node: UiObject, va
|
||||
val bounds = boundsToString(boundsInScreen)
|
||||
|
||||
val children = ArrayList<NodeInfo>()
|
||||
var fullId: String? = node.viewIdResourceName
|
||||
val simpleId = node.simpleId()
|
||||
val fullId = node.fullId()
|
||||
val id = node.simpleId()
|
||||
val desc = node.desc()
|
||||
val text = node.text()
|
||||
val className = node.className()
|
||||
@@ -59,6 +60,7 @@ class NodeInfo(private val resources: Resources?, private val node: UiObject, va
|
||||
val childCount = node.childCount()
|
||||
val actionNames = node.actionNames()
|
||||
|
||||
@SuppressLint("DiscouragedApi")
|
||||
val idHex = takeIf { resources != null && packageName != null && fullId != null }?.let {
|
||||
"0x${Integer.toHexString(resources!!.getIdentifier(fullId, null, null))}"
|
||||
}
|
||||
@@ -68,7 +70,7 @@ class NodeInfo(private val resources: Resources?, private val node: UiObject, va
|
||||
"childCount=${children.size}, " +
|
||||
"boundsInScreen=$boundsInScreen, " +
|
||||
"boundsInParent=$boundsInParent, " +
|
||||
"id='$simpleId', " +
|
||||
"id='$id', " +
|
||||
"desc='$desc', " +
|
||||
"packageName='$packageName', " +
|
||||
"text='$text', " +
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
package org.autojs.autojs.core.accessibility
|
||||
|
||||
import android.accessibilityservice.AccessibilityService
|
||||
import android.accessibilityservice.GestureDescription
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Rect
|
||||
@@ -15,7 +14,7 @@ import android.view.accessibility.AccessibilityNodeInfo
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
|
||||
import org.autojs.autojs.annotation.ScriptInterface
|
||||
import org.autojs.autojs.core.accessibility.AccessibilityService.Companion.isRunning
|
||||
import org.autojs.autojs.core.automator.AccessibilityEventWrapper
|
||||
import org.autojs.autojs.core.automator.GlobalActionAutomator
|
||||
import org.autojs.autojs.core.automator.UiObject
|
||||
import org.autojs.autojs.core.automator.action.ActionFactory
|
||||
@@ -27,7 +26,8 @@ import org.autojs.autojs.runtime.accessibility.AccessibilityConfig
|
||||
import org.autojs.autojs.runtime.api.ScreenMetrics
|
||||
import org.autojs.autojs.runtime.api.ScriptPromiseAdapter
|
||||
import org.autojs.autojs.util.DeveloperUtils
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import android.accessibilityservice.AccessibilityService as AndroidAccessibilityService
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/4/2.
|
||||
@@ -177,11 +177,23 @@ class SimpleActionAutomator(private val accessibilityBridge: AccessibilityBridge
|
||||
fun paste(target: ActionTarget) = performAction(target.createAction(AccessibilityNodeInfo.ACTION_PASTE))
|
||||
|
||||
@ScriptInterface
|
||||
fun isServiceRunning() = isRunning()
|
||||
fun isServiceRunning() = AccessibilityService.isRunning()
|
||||
|
||||
@ScriptInterface
|
||||
fun ensureService() = accessibilityBridge.ensureServiceEnabled()
|
||||
|
||||
//todo:优化实现方式
|
||||
// TODO by SuperMonster003 on Jul 12, 2023.
|
||||
// ! Ref to Auto.js Pro
|
||||
fun registerEvent(eventName: String, callback: AccessibilityEventCallback) {
|
||||
ensureService()
|
||||
AccessibilityService.instance?.addAccessibilityEventCallback(eventName, callback)
|
||||
}
|
||||
|
||||
fun removeEvent(eventName: String) {
|
||||
AccessibilityService.instance?.removeAccessibilityEventCallback(eventName)
|
||||
}
|
||||
|
||||
private fun performAction(simpleAction: SimpleAction): Boolean {
|
||||
ensureService()
|
||||
if (AccessibilityConfig.isUnintendedGuardEnabled() && isRunningPackageSelf) {
|
||||
@@ -204,8 +216,8 @@ class SimpleActionAutomator(private val accessibilityBridge: AccessibilityBridge
|
||||
val promiseAdapter = mPromiseAdapter ?: ScriptPromiseAdapter().also { mPromiseAdapter = it }
|
||||
val service = accessibilityBridge.service!!
|
||||
val executor = service.mainExecutor
|
||||
val callback = object : AccessibilityService.TakeScreenshotCallback {
|
||||
override fun onSuccess(screenshot: AccessibilityService.ScreenshotResult) {
|
||||
val callback = object : AndroidAccessibilityService.TakeScreenshotCallback {
|
||||
override fun onSuccess(screenshot: AndroidAccessibilityService.ScreenshotResult) {
|
||||
val hardwareBuffer = Bitmap.wrapHardwareBuffer(screenshot.hardwareBuffer, screenshot.colorSpace)
|
||||
|
||||
// @Hint by SuperMonster003 on Jun 9, 2023.
|
||||
@@ -222,7 +234,7 @@ class SimpleActionAutomator(private val accessibilityBridge: AccessibilityBridge
|
||||
}
|
||||
|
||||
override fun onFailure(errorCode: Int) {
|
||||
if (errorCode == AccessibilityService.ERROR_TAKE_SCREENSHOT_INTERVAL_TIME_SHORT) {
|
||||
if (errorCode == AndroidAccessibilityService.ERROR_TAKE_SCREENSHOT_INTERVAL_TIME_SHORT) {
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
captureScreen()
|
||||
}, 50)
|
||||
@@ -239,9 +251,12 @@ class SimpleActionAutomator(private val accessibilityBridge: AccessibilityBridge
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val accessibilityDelegateCounter = AtomicInteger(1000)
|
||||
val TAG: String = SimpleActionAutomator::class.java.name
|
||||
|
||||
interface AccessibilityEventCallback {
|
||||
fun onAccessibilityEvent(event: AccessibilityEventWrapper)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.autojs.autojs.core.automator
|
||||
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import android.view.accessibility.AccessibilityNodeInfo
|
||||
|
||||
/**
|
||||
* Created by 抠脚本人 on Jul 10, 2023.
|
||||
*/
|
||||
class AccessibilityEventWrapper(event: AccessibilityEvent) {
|
||||
val raw = event
|
||||
val packageName: CharSequence? = event.packageName
|
||||
val eventType = event.eventType
|
||||
val eventTime = event.eventTime
|
||||
val action = event.action
|
||||
val isFullScreen = event.isFullScreen
|
||||
val className = event.className
|
||||
val source = event.source?.let { UiObject(it, getDepth(it), getIndexInParent(it)) }
|
||||
|
||||
private fun getDepth(node: AccessibilityNodeInfo): Int {
|
||||
var depth = 0
|
||||
var father = node.parent
|
||||
while (father != null) {
|
||||
depth++
|
||||
father = father.parent
|
||||
}
|
||||
return depth
|
||||
}
|
||||
|
||||
private fun getIndexInParent(node: AccessibilityNodeInfo): Int {
|
||||
var index = 0
|
||||
val parent = node.parent ?: return 0
|
||||
while (parent.getChild(index) != node) {
|
||||
index++
|
||||
}
|
||||
return index
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,11 +31,21 @@ import org.opencv.core.Size
|
||||
*/
|
||||
|
||||
@Suppress("unused", "DEPRECATION")
|
||||
open class UiObject constructor(info: Any?, private val allocator: AccessibilityNodeInfoAllocator?, private val depth: Int, private val indexInParent: Int) : AccessibilityNodeInfoCompat(info), UiObjectActions {
|
||||
open class UiObject(
|
||||
info: Any?,
|
||||
private val allocator: AccessibilityNodeInfoAllocator?,
|
||||
private val depth: Int,
|
||||
private val indexInParent: Int
|
||||
) : AccessibilityNodeInfoCompat(info), UiObjectActions {
|
||||
|
||||
private val bounds by lazy { AccessibilityNodeInfoHelper.getBoundsInScreen(this) }
|
||||
|
||||
constructor(info: Any?, allocator: AccessibilityNodeInfoAllocator, indexInParent: Int) : this(info, allocator, 0, indexInParent)
|
||||
constructor(info: Any?, allocator: AccessibilityNodeInfoAllocator, indexInParent: Int) : this(
|
||||
info,
|
||||
allocator,
|
||||
0,
|
||||
indexInParent
|
||||
)
|
||||
|
||||
@JvmOverloads
|
||||
constructor(info: Any?, depth: Int = 0, indexInParent: Int = -1) : this(info, null, depth, indexInParent)
|
||||
@@ -50,16 +60,35 @@ open class UiObject constructor(info: Any?, private val allocator: Accessibility
|
||||
}
|
||||
} catch (e: IllegalStateException) {
|
||||
// FIXME: 2017/5/5
|
||||
null
|
||||
null.also { e.printStackTrace() }
|
||||
}
|
||||
|
||||
open fun child(i: Int): UiObject? = try {
|
||||
super.getChild(i)?.run { UiObject(unwrap(), depth + 1, i) }
|
||||
} catch (e: IllegalStateException) {
|
||||
// FIXME: 2017/5/5
|
||||
null
|
||||
null.also { e.printStackTrace() }
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java", ReplaceWith("offset(i)"))
|
||||
open fun brother(i: Int): UiObject? = offset(i)
|
||||
|
||||
open fun offset(i: Int): UiObject? = try {
|
||||
parent()?.child(indexInParent + i)
|
||||
} catch (e: ArrayIndexOutOfBoundsException) {
|
||||
null.also { e.printStackTrace() }
|
||||
}
|
||||
|
||||
open fun sibling(i: Int): UiObject? = try {
|
||||
parent()?.child(i)
|
||||
} catch (e: ArrayIndexOutOfBoundsException) {
|
||||
null.also { e.printStackTrace() }
|
||||
}
|
||||
|
||||
open fun nextSibling() = sibling(1)
|
||||
|
||||
open fun previousSibling() = sibling(-1)
|
||||
|
||||
open fun childCount() = childCount
|
||||
|
||||
fun hasChildren() = childCount > 0
|
||||
@@ -332,28 +361,29 @@ open class UiObject constructor(info: Any?, private val allocator: Accessibility
|
||||
}
|
||||
} catch (e: IllegalStateException) {
|
||||
// FIXME: 2017/5/5
|
||||
false
|
||||
false.also { e.printStackTrace() }
|
||||
}
|
||||
|
||||
override fun performAction(action: Int): Boolean = try {
|
||||
super.performAction(action)
|
||||
} catch (e: IllegalStateException) {
|
||||
// FIXME: 2017/5/5
|
||||
false
|
||||
false.also { e.printStackTrace() }
|
||||
}
|
||||
|
||||
override fun getChild(index: Int): AccessibilityNodeInfoCompat = allocator?.getChild(this, index) ?: super.getChild(index)
|
||||
override fun getChild(index: Int): AccessibilityNodeInfoCompat =
|
||||
allocator?.getChild(this, index) ?: super.getChild(index)
|
||||
|
||||
override fun getParent(): AccessibilityNodeInfoCompat = allocator?.getParent(this) ?: super.getParent()
|
||||
|
||||
override fun findAccessibilityNodeInfosByText(text: String): List<AccessibilityNodeInfoCompat> {
|
||||
return allocator?.findAccessibilityNodeInfosByText(this, text)
|
||||
?: super.findAccessibilityNodeInfosByText(text)
|
||||
?: super.findAccessibilityNodeInfosByText(text)
|
||||
}
|
||||
|
||||
override fun findAccessibilityNodeInfosByViewId(viewId: String): List<AccessibilityNodeInfoCompat> {
|
||||
return allocator?.findAccessibilityNodeInfosByViewId(this, viewId)
|
||||
?: super.findAccessibilityNodeInfosByViewId(viewId)
|
||||
?: super.findAccessibilityNodeInfosByViewId(viewId)
|
||||
}
|
||||
|
||||
override fun recycle() {
|
||||
@@ -375,7 +405,14 @@ open class UiObject constructor(info: Any?, private val allocator: Accessibility
|
||||
private val cArray = Array::class.java
|
||||
|
||||
private val RESULT_GROUP_WIDGET by lazy { arrayOf("#", "w", RESULT_TYPE_WIDGET) }
|
||||
private val RESULT_GROUP_WIDGET_COLLECTION by lazy { arrayOf("{}", "wc", "collection", "list").plus(listAliases("#", "w")) }
|
||||
private val RESULT_GROUP_WIDGET_COLLECTION by lazy {
|
||||
arrayOf("{}", "wc", "collection", "list").plus(
|
||||
listAliases(
|
||||
"#",
|
||||
"w"
|
||||
)
|
||||
)
|
||||
}
|
||||
private val RESULT_GROUP_WIDGETS by lazy { arrayOf("[]", "ws", "widgets").plus(arrayAliases("#", "w")) }
|
||||
private val RESULT_GROUP_CONTENT by lazy { arrayOf("$", "txt", "content") }
|
||||
private val RESULT_GROUP_CONTENTS by lazy { arrayOf("contents").plus(arrayAliases("$", "txt", "content")) }
|
||||
@@ -386,7 +423,8 @@ open class UiObject constructor(info: Any?, private val allocator: Accessibility
|
||||
|
||||
@JvmStatic
|
||||
fun isCompass(s: Any?): Boolean {
|
||||
return (s as? CharSequence)?.run { this == COMPASS_PASS_ON || isEmpty() || contains("^(([pkc>]|s[<>]?)-?\\d*)+$".toRegex()) } ?: false
|
||||
return (s as? CharSequence)?.run { this == COMPASS_PASS_ON || isEmpty() || contains("^(([pkc>]|s[<>]?)-?\\d*)+$".toRegex()) }
|
||||
?: false
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@@ -417,13 +455,18 @@ open class UiObject constructor(info: Any?, private val allocator: Accessibility
|
||||
@JvmStatic
|
||||
fun createRoot(root: AccessibilityNodeInfo?) = UiObject(root, null, 0, -1)
|
||||
|
||||
internal fun createRoot(root: AccessibilityNodeInfo?, allocator: AccessibilityNodeInfoAllocator?) = UiObject(root, allocator, 0, -1)
|
||||
internal fun createRoot(root: AccessibilityNodeInfo?, allocator: AccessibilityNodeInfoAllocator?) =
|
||||
UiObject(root, allocator, 0, -1)
|
||||
|
||||
private fun arrayAliases(symbol: String, vararg others: String): Array<String> {
|
||||
return arrayOf("$symbol$symbol", "$symbol[]", "[$symbol]").plus(others.map { listOf("$it[]", "[$it]") }.flatten())
|
||||
return arrayOf("$symbol$symbol", "$symbol[]", "[$symbol]").plus(others.map { listOf("$it[]", "[$it]") }
|
||||
.flatten())
|
||||
}
|
||||
|
||||
private fun listAliases(@Suppress("SameParameterValue") symbol: String, vararg others: String): Array<String> {
|
||||
private fun listAliases(
|
||||
@Suppress("SameParameterValue") symbol: String,
|
||||
@Suppress("SameParameterValue") vararg others: String
|
||||
): Array<String> {
|
||||
return arrayOf("$symbol{}", "{$symbol}").plus(others.map { listOf("$it{}", "{$it}") }.flatten())
|
||||
}
|
||||
|
||||
@@ -478,7 +521,12 @@ open class UiObject constructor(info: Any?, private val allocator: Accessibility
|
||||
}
|
||||
}
|
||||
|
||||
internal class Detector(private val compass: CharSequence? = COMPASS_PASS_ON, private val result: Result, val callback: BaseFunction? = null, private val selector: UiSelector? = UiSelector()) {
|
||||
internal class Detector(
|
||||
private val compass: CharSequence? = COMPASS_PASS_ON,
|
||||
private val result: Result,
|
||||
val callback: BaseFunction? = null,
|
||||
private val selector: UiSelector? = UiSelector()
|
||||
) {
|
||||
|
||||
fun detect(): Any? = when (val type = result.type.let { if (it is List<*>) it[0] else it }.toString()) {
|
||||
in RESULT_GROUP_WIDGET -> getUiObject()
|
||||
@@ -524,13 +572,17 @@ open class UiObject constructor(info: Any?, private val allocator: Accessibility
|
||||
|
||||
private fun getUiObject() = result.byOne()?.compass(compass)
|
||||
|
||||
private fun getUiObjectCollection(transformer: (UiObject?) -> UiObject?) = UiObjectCollection.transform(result.byAll(), transformer).let { RhinoUtils.wrap(it) }
|
||||
private fun getUiObjectCollection(transformer: (UiObject?) -> UiObject?) =
|
||||
UiObjectCollection.transform(result.byAll(), transformer).let { RhinoUtils.wrap(it) }
|
||||
|
||||
private fun getUiObjectArray(transformer: (UiObject?) -> Any?) = UiObjectCollection.mapNotNull(result.byAll(), transformer).let { RhinoUtils.toArray(it) }
|
||||
private fun getUiObjectArray(transformer: (UiObject?) -> Any?) =
|
||||
UiObjectCollection.mapNotNull(result.byAll(), transformer).let { RhinoUtils.toArray(it) }
|
||||
|
||||
private fun detectWithCallback(callback: BaseFunction?, args: Array<*>): Any? = callback?.let { RhinoUtils.callFunction(it, args) }
|
||||
private fun detectWithCallback(callback: BaseFunction?, args: Array<*>): Any? =
|
||||
callback?.let { RhinoUtils.callFunction(it, args) }
|
||||
|
||||
private fun detectWithCallback(callback: BaseFunction?, arg: Any?): Any? = callback?.let { detectWithCallback(it, arrayOf(arg)) }
|
||||
private fun detectWithCallback(callback: BaseFunction?, arg: Any?): Any? =
|
||||
callback?.let { detectWithCallback(it, arrayOf(arg)) }
|
||||
|
||||
}
|
||||
|
||||
@@ -596,15 +648,45 @@ open class UiObject constructor(info: Any?, private val allocator: Accessibility
|
||||
"setLiveRegion", "setMaxTextLength", "setMovementGranularities",
|
||||
),
|
||||
arrayOf(cString) to arrayOf(
|
||||
"compass", "findAccessibilityNodeInfosByText", "findAccessibilityNodeInfosByViewId", "setClassName", "setContentDescription",
|
||||
"setError", "setHintText", "setPackageName", "setPaneTitle", "setRoleDescription", "setStateDescription", "setText",
|
||||
"setTooltipText", "setViewIdResourceName",
|
||||
"compass",
|
||||
"findAccessibilityNodeInfosByText",
|
||||
"findAccessibilityNodeInfosByViewId",
|
||||
"setClassName",
|
||||
"setContentDescription",
|
||||
"setError",
|
||||
"setHintText",
|
||||
"setPackageName",
|
||||
"setPaneTitle",
|
||||
"setRoleDescription",
|
||||
"setStateDescription",
|
||||
"setText",
|
||||
"setTooltipText",
|
||||
"setViewIdResourceName",
|
||||
),
|
||||
arrayOf(cBoolean) to arrayOf(
|
||||
"setAccessibilityFocused", "setCanOpenPopup", "setCheckable", "setChecked", "setClickable", "setEditable", "setEnabled",
|
||||
"setFocusable", "setFocused", "setHeading", "setImportantForAccessibility", "setLongClickable", "setMultiLine", "setPassword",
|
||||
"setScreenReaderFocusable", "setScrollable", "setSelected", "setShowingHintText", "setTextEntryKey", "setVisibleToUser",
|
||||
"setContentInvalid", "setContextClickable", "setDismissable",
|
||||
"setAccessibilityFocused",
|
||||
"setCanOpenPopup",
|
||||
"setCheckable",
|
||||
"setChecked",
|
||||
"setClickable",
|
||||
"setEditable",
|
||||
"setEnabled",
|
||||
"setFocusable",
|
||||
"setFocused",
|
||||
"setHeading",
|
||||
"setImportantForAccessibility",
|
||||
"setLongClickable",
|
||||
"setMultiLine",
|
||||
"setPassword",
|
||||
"setScreenReaderFocusable",
|
||||
"setScrollable",
|
||||
"setSelected",
|
||||
"setShowingHintText",
|
||||
"setTextEntryKey",
|
||||
"setVisibleToUser",
|
||||
"setContentInvalid",
|
||||
"setContextClickable",
|
||||
"setDismissable",
|
||||
),
|
||||
arrayOf(cFloatPrim) to arrayOf("setProgress"),
|
||||
arrayOf(cIntPrim, cIntPrim) to arrayOf("setTextSelection", "setSelection", "scrollTo"),
|
||||
@@ -629,7 +711,11 @@ open class UiObject constructor(info: Any?, private val allocator: Accessibility
|
||||
constructor(resultType: String, resultParams: Array<Any?>?) : super(
|
||||
when (resultParams) {
|
||||
null -> str(R.string.error_unknown_picker_result_type, resultType)
|
||||
else -> str(R.string.error_unknown_picker_result_type_with_params, resultType, "[${resultParams.joinToString { it.toString() }}]")
|
||||
else -> str(
|
||||
R.string.error_unknown_picker_result_type_with_params,
|
||||
resultType,
|
||||
"[${resultParams.joinToString { it.toString() }}]"
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package org.autojs.autojs.core.ui;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.autojs.autojs.core.ui.inflater.util.Ids;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/5/14.
|
||||
*/
|
||||
public class JsViewHelper {
|
||||
|
||||
@Nullable
|
||||
public static View findViewByStringId(View view, String id) {
|
||||
View result = view.findViewById(Ids.parse(id));
|
||||
if (result != null)
|
||||
return result;
|
||||
if (!(view instanceof ViewGroup group)) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 0; i < group.getChildCount(); i++) {
|
||||
result = findViewByStringId(group.getChildAt(i), id);
|
||||
if (result != null)
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
22
app/src/main/java/org/autojs/autojs/core/ui/JsViewHelper.kt
Normal file
22
app/src/main/java/org/autojs/autojs/core/ui/JsViewHelper.kt
Normal file
@@ -0,0 +1,22 @@
|
||||
package org.autojs.autojs.core.ui
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import org.autojs.autojs.core.ui.inflater.util.Ids
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/5/14.
|
||||
* Transformed by 抠脚本人 on Jul 10, 2023.
|
||||
*/
|
||||
object JsViewHelper {
|
||||
@JvmStatic
|
||||
fun findViewByStringId(view: View, id: String?): View? {
|
||||
view.findViewById<View>(Ids.parse(id))?.let { return it }
|
||||
if (view is ViewGroup) {
|
||||
for (i in 0 until view.childCount) {
|
||||
findViewByStringId(view.getChildAt(i), id)?.let { return it }
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package org.autojs.autojs.model.autocomplete;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2018/2/3.
|
||||
*/
|
||||
public class CodeCompletion {
|
||||
|
||||
private final String mHint;
|
||||
private final String mUrl;
|
||||
private final String mInsertText;
|
||||
private final int mInsertPos;
|
||||
|
||||
public CodeCompletion(String hint, String url, int insertPos) {
|
||||
mHint = hint;
|
||||
mUrl = url;
|
||||
mInsertPos = insertPos;
|
||||
mInsertText = null;
|
||||
}
|
||||
|
||||
public CodeCompletion(String hint, String url, String insertText) {
|
||||
mHint = hint;
|
||||
mUrl = url;
|
||||
mInsertText = insertText;
|
||||
mInsertPos = -1;
|
||||
}
|
||||
|
||||
public String getHint() {
|
||||
return mHint;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return mUrl;
|
||||
}
|
||||
|
||||
public String getInsertText() {
|
||||
if (mInsertText != null)
|
||||
return mInsertText;
|
||||
if (mInsertPos == 0) {
|
||||
return mHint;
|
||||
}
|
||||
return mHint.substring(mInsertPos);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.autojs.autojs.model.autocomplete
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2018/2/3.
|
||||
* Transformed by 抠脚本人 on Jul 11, 2023.
|
||||
*/
|
||||
class CodeCompletion {
|
||||
val hint: String
|
||||
val url: String?
|
||||
private val mInsertText: String?
|
||||
private val mInsertPos: Int
|
||||
|
||||
constructor(hint: String, url: String?, insertPos: Int) {
|
||||
this.hint = hint
|
||||
this.url = url
|
||||
mInsertPos = insertPos
|
||||
mInsertText = null
|
||||
}
|
||||
|
||||
constructor(hint: String, url: String, insertText: String?) {
|
||||
this.hint = hint
|
||||
this.url = url
|
||||
mInsertText = insertText
|
||||
mInsertPos = -1
|
||||
}
|
||||
|
||||
val insertText: String
|
||||
get() {
|
||||
if (mInsertText != null) return mInsertText
|
||||
return if (mInsertPos == 0) {
|
||||
hint
|
||||
} else hint.substring(mInsertPos)
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package org.autojs.autojs.model.autocomplete;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/9/27.
|
||||
*/
|
||||
public class CodeCompletions {
|
||||
|
||||
|
||||
private final int mFrom;
|
||||
private final List<CodeCompletion> mCompletions;
|
||||
|
||||
public CodeCompletions(int cursor, List<CodeCompletion> completions) {
|
||||
mFrom = cursor;
|
||||
mCompletions = completions;
|
||||
}
|
||||
|
||||
public static CodeCompletions just(List<String> hints) {
|
||||
List<CodeCompletion> completions = new ArrayList<>(hints.size());
|
||||
for (String hint : hints) {
|
||||
completions.add(new CodeCompletion(hint, null, 0));
|
||||
}
|
||||
return new CodeCompletions(-1, completions);
|
||||
}
|
||||
|
||||
public int getFrom() {
|
||||
return mFrom;
|
||||
}
|
||||
|
||||
|
||||
public int size() {
|
||||
return mCompletions.size();
|
||||
}
|
||||
|
||||
public String getHint(int position) {
|
||||
return mCompletions.get(position).getHint();
|
||||
}
|
||||
|
||||
public CodeCompletion get(int pos) {
|
||||
return mCompletions.get(pos);
|
||||
}
|
||||
|
||||
public String getUrl(int pos) {
|
||||
return mCompletions.get(pos).getUrl();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.autojs.autojs.model.autocomplete
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/9/27.
|
||||
* Transformed by 抠脚本人 on Jul 11, 2023.
|
||||
*/
|
||||
class CodeCompletions(val from: Int, private val mCompletions: List<CodeCompletion>) {
|
||||
fun size(): Int {
|
||||
return mCompletions.size
|
||||
}
|
||||
|
||||
fun getHint(position: Int): String {
|
||||
return mCompletions[position].hint
|
||||
}
|
||||
|
||||
operator fun get(pos: Int): CodeCompletion {
|
||||
return mCompletions[pos]
|
||||
}
|
||||
|
||||
fun getUrl(pos: Int): String? {
|
||||
return mCompletions[pos].url
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun just(hints: List<String?>): CodeCompletions {
|
||||
val completions: MutableList<CodeCompletion> = ArrayList(hints.size)
|
||||
for (hint in hints) {
|
||||
completions.add(CodeCompletion(hint!!, null, 0))
|
||||
}
|
||||
return CodeCompletions(-1, completions)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,10 @@ import java.util.Arrays;
|
||||
public class Symbols {
|
||||
|
||||
private static final CodeCompletions sSymbols = CodeCompletions.just(Arrays.asList(
|
||||
",", ".", "=", ";", "\"", "'", "-", "_",
|
||||
",", ".", "=", ";", "\"", "'", "/", "-", "_",
|
||||
"(", ")", "[", "]", "{", "}", "<", ">",
|
||||
"+", "*", "?", "$", "#", "@", "`",
|
||||
"/", "\\", "&", "|", "!", "%", "×", "÷",
|
||||
"+", "*", "?", ":", "$", "#", "@", "`",
|
||||
"\\", "&", "|", "!", "%", "×", "÷",
|
||||
"∈", "∩", "∪", "∉", "⊙", "∅", "¥", "€",
|
||||
"°", "℃", "∵", "∴", "±", "≠", "≈",
|
||||
"α", "β", "γ", "λ", "μ", "π", "σ", "ω",
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.autojs.autojs.AutoJs;
|
||||
import org.autojs.autojs.annotation.ScriptVariable;
|
||||
import org.autojs.autojs.concurrent.VolatileDispose;
|
||||
import org.autojs.autojs.core.accessibility.AccessibilityBridge;
|
||||
import org.autojs.autojs.core.accessibility.AccessibilityService;
|
||||
import org.autojs.autojs.core.accessibility.SimpleActionAutomator;
|
||||
import org.autojs.autojs.core.accessibility.UiSelector;
|
||||
import org.autojs.autojs.core.activity.ActivityInfoProvider;
|
||||
@@ -528,6 +529,9 @@ public class ScriptRuntime {
|
||||
}
|
||||
});
|
||||
|
||||
// 清空无障碍事件
|
||||
ignoresException(AccessibilityService::clearAccessibilityEventCallback);
|
||||
|
||||
ignoresException(RootUtils::resetRuntimeOverriddenRootModeState);
|
||||
ignoresException(ImageWrapper::recycleAll);
|
||||
|
||||
|
||||
@@ -285,7 +285,16 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar
|
||||
.setFunctionsView(mFunctionsKeyboard)
|
||||
.setEditView(editor!!.codeEditText)
|
||||
.build()
|
||||
//todo:不清楚作用,暂时注释掉
|
||||
// @Hint by SuperMonster003 on Jul 12, 2023.
|
||||
// ! 此处的点击事件回调注册是为了使功能键盘智能提示的属性可以实现其接口对应的功能:
|
||||
// ! 点击: 自动补全, 并根据情况添加括号或句点符号等.
|
||||
// ! 长按: 以浮动窗口形式展示 [方法/属性/模块] 对应的文档内容 (如果存在的话).
|
||||
mFunctionsKeyboard!!.setClickCallback(this)
|
||||
mShowFunctionsButton!!.setOnLongClickListener {
|
||||
editor!!.beautifyCode()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpInputMethodEnhancedBar() {
|
||||
@@ -321,7 +330,7 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar
|
||||
mAutoCompletion!!.onCursorChange(line, cursor)
|
||||
}
|
||||
|
||||
fun setTheme(theme: Theme?) {
|
||||
private fun setTheme(theme: Theme?) {
|
||||
theme?.let {
|
||||
mEditorTheme = it
|
||||
editor!!.setTheme(it)
|
||||
@@ -575,11 +584,19 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar
|
||||
|
||||
override fun onHintClick(completions: CodeCompletions, pos: Int) {
|
||||
val completion = completions[pos]
|
||||
editor!!.insert(completion.insertText)
|
||||
//todo:增加行注释
|
||||
if (completion.insertText=="/") {
|
||||
editor!!.commentLine()
|
||||
} else editor!!.insert(completion.insertText)
|
||||
}
|
||||
|
||||
override fun onHintLongClick(completions: CodeCompletions, pos: Int) {
|
||||
val completion = completions[pos]
|
||||
//todo:增加块注释
|
||||
if (completion.insertText=="/") {
|
||||
editor!!.commentBlock()
|
||||
return
|
||||
}
|
||||
if (completion.url == null) return
|
||||
showManual(completion.url, completion.hint)
|
||||
}
|
||||
|
||||
@@ -403,11 +403,14 @@ public class CodeEditor extends HVScrollView {
|
||||
|
||||
public void beautifyCode() {
|
||||
setProgress(true);
|
||||
int pos = mCodeEditText.getSelectionStart();
|
||||
mJsBeautifier.beautify(mCodeEditText.getText().toString(), new JsBeautifier.Callback() {
|
||||
@Override
|
||||
public void onSuccess(String beautifiedCode) {
|
||||
setProgress(false);
|
||||
mCodeEditText.setText(beautifiedCode);
|
||||
// 格式化后恢复光标位置
|
||||
mCodeEditText.setSelection(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -418,6 +421,43 @@ public class CodeEditor extends HVScrollView {
|
||||
});
|
||||
}
|
||||
|
||||
public void commentLine() {
|
||||
//如果没有选中,则添加文本/,否则选中的行前加//
|
||||
String selectionText = getSelectionRaw();
|
||||
if (selectionText.equals("")) {
|
||||
insert("/");
|
||||
} else {
|
||||
String[] lines = selectionText.split("\\n");
|
||||
StringBuilder commentedText = new StringBuilder();
|
||||
//处理取消注释
|
||||
if (lines[0].startsWith("//")) {
|
||||
for (String line : lines) {
|
||||
commentedText.append(line.substring(2)).append("\n");
|
||||
}
|
||||
} else {
|
||||
for (String line : lines) {
|
||||
commentedText.append("//").append(line).append("\n");
|
||||
}
|
||||
}
|
||||
mReplacement = commentedText.toString().replaceAll("\\n$", "");
|
||||
replaceSelection();
|
||||
}
|
||||
}
|
||||
|
||||
public void commentBlock() {
|
||||
String selectionText = getSelectionRaw();
|
||||
if (!selectionText.isEmpty()) {
|
||||
String regex = "/\\*([^*]|\\*+[^*/])*\\*/";
|
||||
if (selectionText.matches(regex)) {
|
||||
// 取消块注释
|
||||
mReplacement = selectionText.substring(2, selectionText.length() - 2);
|
||||
} else {
|
||||
// 增加块注释
|
||||
mReplacement = "/*" + selectionText + "*/";
|
||||
}
|
||||
replaceSelection();
|
||||
}
|
||||
}
|
||||
|
||||
public void insert(String insertText) {
|
||||
int selection = Math.max(mCodeEditText.getSelectionStart(), 0);
|
||||
@@ -437,13 +477,17 @@ public class CodeEditor extends HVScrollView {
|
||||
return mCodeEditText.getText().toString();
|
||||
}
|
||||
|
||||
public Observable<String> getSelection() {
|
||||
public String getSelectionRaw() {
|
||||
int s = mCodeEditText.getSelectionStart();
|
||||
int e = mCodeEditText.getSelectionEnd();
|
||||
if (s == e) {
|
||||
return Observable.just("");
|
||||
return "";
|
||||
}
|
||||
return Observable.just(mCodeEditText.getText().toString().substring(s, e));
|
||||
return mCodeEditText.getText().toString().substring(s, e);
|
||||
}
|
||||
|
||||
public Observable<String> getSelection() {
|
||||
return Observable.just(getSelectionRaw());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -13,9 +13,11 @@ import org.autojs.autojs.ui.floating.layoutinspector.LayoutBoundsFloatyWindow
|
||||
import org.autojs.autojs.ui.floating.layoutinspector.LayoutHierarchyFloatyWindow
|
||||
import org.autojs.autojs.ui.floating.layoutinspector.NodeInfoView
|
||||
import org.autojs.autojs.ui.widget.BubblePopupMenu
|
||||
import org.autojs.autojs.util.ClipboardUtils
|
||||
import org.autojs.autojs.util.ViewUtils
|
||||
import org.autojs.autojs6.R
|
||||
|
||||
abstract class LayoutFloatyWindow(private val rootNode: NodeInfo?, private val context: Context, private val isServiceRelied: Boolean) : FullScreenFloatyWindow() {
|
||||
abstract class LayoutFloatyWindow(private val rootNode: NodeInfo?, private val context: Context, private val isServiceRelied: Boolean) : FullScreenFloatyWindow(){
|
||||
|
||||
private lateinit var mServiceContext: Context
|
||||
private lateinit var mActions: LinkedHashMap<Int, Runnable>
|
||||
@@ -27,6 +29,12 @@ abstract class LayoutFloatyWindow(private val rootNode: NodeInfo?, private val c
|
||||
private val mNodeInfoDialog by lazy {
|
||||
AppLevelThemeDialogBuilder(mServiceContext)
|
||||
.customView(mNodeInfoView, false)
|
||||
.positiveText("生成")
|
||||
.onPositive { _, _ ->
|
||||
ViewUtils.showToast(context, "TODO")
|
||||
val selector = mNodeInfoView.getCheckedDate().joinToString(".")
|
||||
if (selector.isNotEmpty()) ClipboardUtils.setClip(context, selector)
|
||||
}
|
||||
.build()
|
||||
.also { it.window!!.setType(FloatyWindowManger.getWindowType()) }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.autojs.autojs.ui.floating.layoutinspector
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
@@ -97,7 +98,7 @@ open class LayoutBoundsView : View {
|
||||
draw(canvas, child)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
if (mRootNode != null) {
|
||||
setSelectedNode(findNodeAt(mRootNode!!, event.rawX.toInt(), event.rawY.toInt()))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.autojs.autojs.ui.floating.layoutinspector;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
@@ -18,12 +19,7 @@ import org.autojs.autojs.ui.widget.LevelBeamView;
|
||||
import org.autojs.autojs.util.ViewUtils;
|
||||
import org.autojs.autojs6.R;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
import java.util.*;
|
||||
|
||||
import pl.openrnd.multilevellistview.ItemInfo;
|
||||
import pl.openrnd.multilevellistview.MultiLevelListAdapter;
|
||||
@@ -40,7 +36,7 @@ public class LayoutHierarchyView extends MultiLevelListView {
|
||||
public interface OnItemLongClickListener {
|
||||
void onItemLongClick(View view, NodeInfo nodeInfo);
|
||||
}
|
||||
|
||||
private final Map<NodeInfo,ViewHolder> nodeMap = new LinkedHashMap<>();
|
||||
private Adapter mAdapter;
|
||||
private OnItemLongClickListener mOnItemLongClickListener;
|
||||
private final AdapterView.OnItemLongClickListener mOnItemLongClickListenerProxy = new AdapterView.OnItemLongClickListener() {
|
||||
@@ -111,17 +107,41 @@ public class LayoutHierarchyView extends MultiLevelListView {
|
||||
}
|
||||
|
||||
private void setClickedItem(View view, NodeInfo item) {
|
||||
mClickedNodeInfo = item;
|
||||
if (mClickedView == null) {
|
||||
mOriginalBackground = view.getBackground();
|
||||
} else {
|
||||
mClickedView.setBackground(mOriginalBackground);
|
||||
drawListItem(mClickedNodeInfo, false);
|
||||
}
|
||||
view.setBackgroundColor(mClickedColor);
|
||||
drawListItem(item, true);
|
||||
mClickedNodeInfo = item;
|
||||
mClickedView = view;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private void drawListItem(NodeInfo info, Boolean draw) {
|
||||
ArrayList<ViewHolder> list = new ArrayList<>();
|
||||
NodeInfo currentInfo = info;
|
||||
while (true) {
|
||||
currentInfo = currentInfo.getParent();
|
||||
if (currentInfo == null) break;
|
||||
ViewHolder vh = nodeMap.get(currentInfo);
|
||||
list.add(vh);
|
||||
}
|
||||
//todo:选用能适应深色模式的字体颜色
|
||||
//fixme:列表滑动时listview数据错乱
|
||||
if (draw) {
|
||||
for (ViewHolder vh : list) {
|
||||
vh.nameView.setTextColor(Color.RED); // 设置字体颜色为红色
|
||||
}
|
||||
} else {
|
||||
for (ViewHolder vh : list) {
|
||||
vh.nameView.setTextColor(Color.BLACK); // 设置字体颜色为红色
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initPaint() {
|
||||
mPaint = new Paint();
|
||||
mPaint.setColor(Color.DKGRAY);
|
||||
@@ -186,7 +206,7 @@ public class LayoutHierarchyView extends MultiLevelListView {
|
||||
return found;
|
||||
}
|
||||
|
||||
private class ViewHolder {
|
||||
private static class ViewHolder {
|
||||
TextView nameView;
|
||||
TextView infoView;
|
||||
ImageView arrowView;
|
||||
@@ -220,6 +240,7 @@ public class LayoutHierarchyView extends MultiLevelListView {
|
||||
return mInitiallyExpandedNodes.contains((NodeInfo) object);
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
@Override
|
||||
public View getViewForObject(Object object, View convertView, ItemInfo itemInfo) {
|
||||
NodeInfo nodeInfo = (NodeInfo) object;
|
||||
@@ -231,8 +252,9 @@ public class LayoutHierarchyView extends MultiLevelListView {
|
||||
} else {
|
||||
viewHolder = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
viewHolder.nameView.setText(simplifyClassName(nodeInfo.getClassName()));
|
||||
nodeMap.put(nodeInfo,viewHolder);
|
||||
//对于id,desc,text,clickable,longClickable不为空的显示额外信息
|
||||
viewHolder.nameView.setText(extraInfo(nodeInfo));
|
||||
viewHolder.nodeInfo = nodeInfo;
|
||||
if (viewHolder.infoView.getVisibility() == VISIBLE)
|
||||
viewHolder.infoView.setText(getItemInfoDsc(itemInfo));
|
||||
@@ -266,6 +288,31 @@ public class LayoutHierarchyView extends MultiLevelListView {
|
||||
return s;
|
||||
}
|
||||
|
||||
private String extraInfo(NodeInfo nodeInfo) {
|
||||
String extra = simplifyClassName(nodeInfo.getClassName());
|
||||
ArrayList<String> info = new ArrayList<>();
|
||||
if (nodeInfo.getId() != null) {
|
||||
info.add("id=" + nodeInfo.getId());
|
||||
}
|
||||
if (!nodeInfo.getText().equals("")) {
|
||||
info.add("text=" + nodeInfo.getText());
|
||||
}
|
||||
if (nodeInfo.getDesc() != null) {
|
||||
info.add("desc=" + nodeInfo.getDesc());
|
||||
}
|
||||
if (nodeInfo.getClickable()) {
|
||||
info.add("clickable");
|
||||
}
|
||||
if (nodeInfo.getLongClickable()) {
|
||||
info.add("longClickable");
|
||||
}
|
||||
//字符串拼接
|
||||
String others = String.join(", ", info);
|
||||
if (!others.isEmpty()) {
|
||||
return extra + " [" + others + "]";
|
||||
}
|
||||
return extra;
|
||||
}
|
||||
|
||||
private String getItemInfoDsc(ItemInfo itemInfo) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package org.autojs.autojs.ui.floating.layoutinspector
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CheckBox
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@@ -20,7 +22,7 @@ import java.lang.reflect.Field
|
||||
* Modified by SuperMonster003 as of Dec 1, 2021.
|
||||
*/
|
||||
class NodeInfoView : RecyclerView {
|
||||
|
||||
//todo:调整数据结构,对话框关闭后根据已勾选的属性,生成选择器
|
||||
private val data = Array(FIELDS.size + 1) { Array(2) { "" } }
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
@@ -41,6 +43,7 @@ class NodeInfoView : RecyclerView {
|
||||
)
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun setNodeInfo(nodeInfo: NodeInfo) {
|
||||
for (i in FIELDS.indices) {
|
||||
try {
|
||||
@@ -71,6 +74,14 @@ class NodeInfoView : RecyclerView {
|
||||
}
|
||||
}
|
||||
|
||||
fun getCheckedDate(): Array<String> {
|
||||
//todo:数据增加checked属性,区分已选中项目
|
||||
val checkedArr = data.filter { it[0] == "id" || it[0] == "text" }
|
||||
return Array(checkedArr.size) {
|
||||
dataToFx(checkedArr[it])
|
||||
}
|
||||
}
|
||||
|
||||
private inner class Adapter : RecyclerView.Adapter<ViewHolder>() {
|
||||
|
||||
val mViewTypeHeader = 0
|
||||
@@ -84,6 +95,7 @@ class NodeInfoView : RecyclerView {
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.apply {
|
||||
data[position].let {
|
||||
//attrChecked.isChecked = false
|
||||
attrName.text = it[0]
|
||||
attrValue.text = it[1]
|
||||
}
|
||||
@@ -97,7 +109,7 @@ class NodeInfoView : RecyclerView {
|
||||
}
|
||||
|
||||
internal inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
//val attrChecked: CheckBox = itemView.findViewById(R.id.generate)
|
||||
val attrName: TextView = itemView.findViewById(R.id.name)
|
||||
val attrValue: TextView = itemView.findViewById(R.id.value)
|
||||
|
||||
@@ -130,7 +142,7 @@ class NodeInfoView : RecyclerView {
|
||||
|
||||
private val FIELD_NAMES = arrayOf(
|
||||
// Common
|
||||
"packageName", "simpleId", "fullId", "idHex",
|
||||
"packageName", "id", "fullId", "idHex",
|
||||
"desc", "text",
|
||||
"bounds", "className",
|
||||
"clickable", "longClickable", "scrollable",
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package org.autojs.autojs.ui.floating.layoutinspector;
|
||||
|
||||
import org.autojs.autojs.core.accessibility.NodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/10.
|
||||
*/
|
||||
public interface OnNodeInfoSelectListener {
|
||||
|
||||
void onNodeSelect(NodeInfo info);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.autojs.autojs.ui.floating.layoutinspector
|
||||
|
||||
import org.autojs.autojs.core.accessibility.NodeInfo
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/10.
|
||||
*/
|
||||
fun interface OnNodeInfoSelectListener {
|
||||
fun onNodeSelect(info: NodeInfo)
|
||||
}
|
||||
@@ -63,15 +63,16 @@
|
||||
android:layout_alignParentBottom="true" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/functions"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="35dp"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:padding="6dp"
|
||||
android:src="@drawable/ic_ali_fx"
|
||||
app:tint="#222329" />
|
||||
android:id="@+id/functions"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="35dp"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/fun"
|
||||
android:padding="6dp"
|
||||
android:src="@drawable/ic_ali_fx"
|
||||
app:tint="#222329" />
|
||||
|
||||
<org.autojs.autojs.ui.edit.completion.CodeCompletionBar
|
||||
android:id="@+id/code_completion_bar"
|
||||
|
||||
@@ -10,6 +10,11 @@
|
||||
android:orientation="horizontal"
|
||||
android:padding="8dp">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/generate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:ignore="TouchTargetSizeCheck" />
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
android:layout_width="0dp"
|
||||
|
||||
@@ -880,6 +880,7 @@
|
||||
<string name="error_colon_must_follow_a_valid_ip_address">Colon must follow a valid IP address</string>
|
||||
<string name="error_repeated_dot_symbol">Repeated dot symbol</string>
|
||||
<string name="error_repeated_colon_symbol">Repeated colon symbol</string>
|
||||
<string name="fun">函数</string>
|
||||
|
||||
<plurals name="text_items_total_sum">
|
||||
<item quantity="one">Total: %d item</item>
|
||||
|
||||
Reference in New Issue
Block a user