Skip to content

Commit

Permalink
feat: position
Browse files Browse the repository at this point in the history
  • Loading branch information
lisonge committed Feb 17, 2024
1 parent c711ccf commit a726e2b
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 38 deletions.
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -216,4 +216,6 @@ dependencies {

implementation(libs.coil.compose)
implementation(libs.coil.gif)

implementation(libs.exp4j)
}
48 changes: 26 additions & 22 deletions app/src/main/kotlin/li/songe/gkd/data/GkdAction.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import com.blankj.utilcode.util.ScreenUtils
import kotlinx.serialization.Serializable


typealias ActionFc = (context: AccessibilityService, node: AccessibilityNodeInfo) -> ActionResult
typealias ActionFc = (context: AccessibilityService, node: AccessibilityNodeInfo, position: RawSubscription.Position?) -> ActionResult


@Serializable
data class GkdAction(
val selector: String,
val quickFind: Boolean = false,
val action: String? = null,
val position: RawSubscription.Position? = null
)

@Serializable
Expand All @@ -26,19 +27,21 @@ data class ActionResult(
val result: Boolean,
)

val clickNode: ActionFc = { _, node ->
val clickNode: ActionFc = { _, node, _ ->
ActionResult(
action = "clickNode", result = node.performAction(AccessibilityNodeInfo.ACTION_CLICK)
)
}

val clickCenter: ActionFc = { context, node ->
val react = Rect()
node.getBoundsInScreen(react)
val x = (react.right + react.left) / 2f
val y = (react.bottom + react.top) / 2f
val clickCenter: ActionFc = { context, node, position ->
val rect = Rect()
node.getBoundsInScreen(rect)
val p = position?.calc(rect)
val x = p?.first ?: ((rect.right + rect.left) / 2f)
val y = p?.second ?: ((rect.bottom + rect.top) / 2f)
ActionResult(
action = "clickCenter",
// TODO 在分屏/小窗模式下会点击到应用界面外部导致误触其它应用
result = if (0 <= x && 0 <= y && x <= ScreenUtils.getScreenWidth() && y <= ScreenUtils.getScreenHeight()) {
val gestureDescription = GestureDescription.Builder()
val path = Path()
Expand All @@ -56,31 +59,32 @@ val clickCenter: ActionFc = { context, node ->
)
}

val click: ActionFc = { context, node ->
val click: ActionFc = { context, node, position ->
if (node.isClickable) {
val result = clickNode(context, node)
val result = clickNode(context, node, position)
if (result.result) {
result
} else {
clickCenter(context, node)
clickCenter(context, node, position)
}
} else {
clickCenter(context, node)
clickCenter(context, node, position)
}
}

val longClickNode: ActionFc = { _, node ->
val longClickNode: ActionFc = { _, node, _ ->
ActionResult(
action = "longClickNode",
result = node.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK)
)
}

val longClickCenter: ActionFc = { context, node ->
val react = Rect()
node.getBoundsInScreen(react)
val x = (react.right + react.left) / 2f
val y = (react.bottom + react.top) / 2f
val longClickCenter: ActionFc = { context, node, position ->
val rect = Rect()
node.getBoundsInScreen(rect)
val p = position?.calc(rect)
val x = p?.first ?: ((rect.right + rect.left) / 2f)
val y = p?.second ?: ((rect.bottom + rect.top) / 2f)
// 内部的 DEFAULT_LONG_PRESS_TIMEOUT 常量是 400
// 而 ViewConfiguration.getLongPressTimeout() 返回 300, 这将导致触发普通的 click 事件
ActionResult(
Expand All @@ -104,20 +108,20 @@ val longClickCenter: ActionFc = { context, node ->
}


val longClick: ActionFc = { context, node ->
val longClick: ActionFc = { context, node, position ->
if (node.isLongClickable) {
val result = longClickNode(context, node)
val result = longClickNode(context, node, position)
if (result.result) {
result
} else {
longClickCenter(context, node)
longClickCenter(context, node, position)
}
} else {
longClickCenter(context, node)
longClickCenter(context, node, position)
}
}

val backFc: ActionFc = { context, _ ->
val backFc: ActionFc = { context, _, _ ->
ActionResult(
action = "back",
result = context.performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK)
Expand Down
153 changes: 139 additions & 14 deletions app/src/main/kotlin/li/songe/gkd/data/RawSubscription.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package li.songe.gkd.data

import android.graphics.Rect
import androidx.compose.runtime.Immutable
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonArray
Expand All @@ -11,11 +12,14 @@ import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.long
import li.songe.gkd.service.allowPropertyNames
import li.songe.gkd.util.json
import li.songe.gkd.util.json5ToJson
import li.songe.selector.Selector
import net.objecthunter.exp4j.Expression
import net.objecthunter.exp4j.ExpressionBuilder

@Immutable
@Serializable
Expand Down Expand Up @@ -90,6 +94,61 @@ data class RawSubscription(
@Serializable
data class RawCategory(val key: Int, val name: String, val enable: Boolean?)

@Immutable
@Serializable
data class Position(
val left: String?,
val top: String?,
val right: String?,
val bottom: String?
) {
private val leftExp by lazy { getExpression(left) }
private val topExp by lazy { getExpression(top) }
private val rightExp by lazy { getExpression(right) }
private val bottomExp by lazy { getExpression(bottom) }

val isValid by lazy {
((leftExp != null && (topExp != null || bottomExp != null)) || (rightExp != null && (topExp != null || bottomExp != null)))
}

/**
* return (x, y)
*/
fun calc(rect: Rect): Pair<Float, Float>? {
if (!isValid) return null
arrayOf(
leftExp,
topExp,
rightExp,
bottomExp
).forEach { exp ->
if (exp != null) {
setVariables(exp, rect)
}
}
if (leftExp != null) {
if (topExp != null) {
return (rect.left + leftExp!!.evaluate()
.toFloat()) to (rect.top + topExp!!.evaluate().toFloat())
}
if (bottomExp != null) {
return (rect.left + leftExp!!.evaluate()
.toFloat()) to (rect.bottom - bottomExp!!.evaluate().toFloat())
}
} else if (rightExp != null) {
if (topExp != null) {
return (rect.right - rightExp!!.evaluate()
.toFloat()) to (rect.top + topExp!!.evaluate().toFloat())
}
if (bottomExp != null) {
return (rect.right - rightExp!!.evaluate()
.toFloat()) to (rect.bottom - bottomExp!!.evaluate().toFloat())
}
}
return null
}
}


interface RawCommonProps {
val actionCd: Long?
Expand All @@ -111,6 +170,7 @@ data class RawSubscription(
val key: Int?
val preKeys: List<Int>?
val action: String?
val position: Position?
val matches: List<String>?
val excludeMatches: List<String>?
}
Expand Down Expand Up @@ -208,6 +268,9 @@ data class RawSubscription(
unknownPropertyNames.forEach { n ->
return@lazy "非法属性名:${n}"
}
if (rules.any { r -> r.position?.isValid == false }) {
return@lazy "非法位置"
}
null
}

Expand Down Expand Up @@ -239,6 +302,7 @@ data class RawSubscription(
override val key: Int?,
override val preKeys: List<Int>?,
override val action: String?,
override val position: Position?,
override val matches: List<String>,
override val excludeMatches: List<String>?,
override val matchAnyApp: Boolean?,
Expand Down Expand Up @@ -318,6 +382,7 @@ data class RawSubscription(
override val key: Int?,
override val preKeys: List<Int>?,
override val action: String?,
override val position: Position?,
override val matches: List<String>?,
override val excludeMatches: List<String>?,

Expand Down Expand Up @@ -345,11 +410,69 @@ data class RawSubscription(

companion object {

private val expVars = arrayOf(
"left",
"top",
"right",
"bottom",
"width",
"height"
)

private fun setVariables(exp: Expression, rect: Rect) {
exp.setVariable("left", rect.left.toDouble())
exp.setVariable("top", rect.top.toDouble())
exp.setVariable("right", rect.right.toDouble())
exp.setVariable("bottom", rect.bottom.toDouble())
exp.setVariable("width", rect.width().toDouble())
exp.setVariable("height", rect.height().toDouble())
}

private fun getExpression(value: String?): Expression? {
return if (value != null) {
try {
ExpressionBuilder(value).variables(*expVars).build().apply {
expVars.forEach { v ->
// 预填充作 validate
setVariable(v, 0.0)
}
}.let { e ->
if (e.validate().isValid) {
e
} else {
null
}
}
} catch (e: Exception) {
e.printStackTrace()
null
}
} else {
null
}
}

private fun getPosition(jsonObject: JsonObject? = null): Position? {
return when (val element = jsonObject?.get("position")) {
JsonNull, null -> null
is JsonObject -> {
Position(
left = element["left"]?.jsonPrimitive?.content,
bottom = element["bottom"]?.jsonPrimitive?.content,
top = element["top"]?.jsonPrimitive?.content,
right = element["right"]?.jsonPrimitive?.content,
)
}

else -> null
}
}

private fun getStringIArray(
json: JsonObject? = null,
jsonObject: JsonObject? = null,
name: String
): List<String>? {
return when (val element = json?.get(name)) {
return when (val element = jsonObject?.get(name)) {
JsonNull, null -> null
is JsonObject -> error("Element ${this::class} can not be object")
is JsonArray -> element.map {
Expand All @@ -363,8 +486,8 @@ data class RawSubscription(
}
}

private fun getIntIArray(json: JsonObject? = null, name: String): List<Int>? {
return when (val element = json?.get(name)) {
private fun getIntIArray(jsonObject: JsonObject? = null, name: String): List<Int>? {
return when (val element = jsonObject?.get(name)) {
JsonNull, null -> null
is JsonArray -> element.map {
when (it) {
Expand All @@ -378,8 +501,8 @@ data class RawSubscription(
}
}

private fun getLongIArray(json: JsonObject? = null, name: String): List<Long>? {
return when (val element = json?.get(name)) {
private fun getLongIArray(jsonObject: JsonObject? = null, name: String): List<Long>? {
return when (val element = jsonObject?.get(name)) {
JsonNull, null -> null
is JsonArray -> element.map {
when (it) {
Expand All @@ -393,8 +516,8 @@ data class RawSubscription(
}
}

private fun getString(json: JsonObject? = null, key: String): String? =
when (val p = json?.get(key)) {
private fun getString(jsonObject: JsonObject? = null, key: String): String? =
when (val p = jsonObject?.get(key)) {
JsonNull, null -> null
is JsonPrimitive -> {
if (p.isString) {
Expand All @@ -407,8 +530,8 @@ data class RawSubscription(
else -> error("Element $p is not a string")
}

private fun getLong(json: JsonObject? = null, key: String): Long? =
when (val p = json?.get(key)) {
private fun getLong(jsonObject: JsonObject? = null, key: String): Long? =
when (val p = jsonObject?.get(key)) {
JsonNull, null -> null
is JsonPrimitive -> {
p.long
Expand All @@ -417,8 +540,8 @@ data class RawSubscription(
else -> error("Element $p is not a long")
}

private fun getInt(json: JsonObject? = null, key: String): Int? =
when (val p = json?.get(key)) {
private fun getInt(jsonObject: JsonObject? = null, key: String): Int? =
when (val p = jsonObject?.get(key)) {
JsonNull, null -> null
is JsonPrimitive -> {
p.int
Expand All @@ -427,8 +550,8 @@ data class RawSubscription(
else -> error("Element $p is not a int")
}

private fun getBoolean(json: JsonObject? = null, key: String): Boolean? =
when (val p = json?.get(key)) {
private fun getBoolean(jsonObject: JsonObject? = null, key: String): Boolean? =
when (val p = jsonObject?.get(key)) {
JsonNull, null -> null
is JsonPrimitive -> {
p.boolean
Expand Down Expand Up @@ -468,6 +591,7 @@ data class RawSubscription(
excludeVersionCodes = getLongIArray(jsonObject, "excludeVersionCodes"),
versionNames = getStringIArray(jsonObject, "versionNames"),
excludeVersionNames = getStringIArray(jsonObject, "excludeVersionNames"),
position = getPosition(jsonObject),
)
}

Expand Down Expand Up @@ -568,6 +692,7 @@ data class RawSubscription(
excludeMatches = getStringIArray(jsonObject, "excludeMatches"),
matches = getStringIArray(jsonObject, "matches") ?: error("miss matches"),
order = getInt(jsonObject, "order"),
position = getPosition(jsonObject),
)
}

Expand Down
Loading

0 comments on commit a726e2b

Please sign in to comment.