mirror of
https://mirror.skon.top/github.com/gkd-kit/gkd
synced 2026-04-20 21:00:12 +08:00
feat: swipe action
This commit is contained in:
@@ -442,8 +442,9 @@ class A11yRuleEngine(val service: A11yCommonImpl) {
|
||||
a, selector, MatchOption(fastQuery = gkdAction.fastQuery)
|
||||
) ?: throw RpcError("没有查询到节点")
|
||||
return withContext(Dispatchers.IO) {
|
||||
ActionPerformer.getAction(gkdAction.action ?: ActionPerformer.None.action)
|
||||
.perform(targetNode, gkdAction.position)
|
||||
ActionPerformer
|
||||
.getAction(gkdAction.action ?: ActionPerformer.None.action)
|
||||
.perform(targetNode, gkdAction)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,27 +16,28 @@ data class GkdAction(
|
||||
val selector: String,
|
||||
val fastQuery: Boolean = false,
|
||||
val action: String? = null,
|
||||
val position: RawSubscription.Position? = null,
|
||||
)
|
||||
override val position: RawSubscription.Position? = null,
|
||||
override val swipeArg: RawSubscription.SwipeArg? = null,
|
||||
) : RawSubscription.LocationProps
|
||||
|
||||
@Serializable
|
||||
data class ActionResult(
|
||||
val action: String,
|
||||
val result: Boolean,
|
||||
val shizuku: Boolean = false,
|
||||
val shell: Boolean = false,
|
||||
val position: Pair<Float, Float>? = null,
|
||||
)
|
||||
|
||||
sealed class ActionPerformer(val action: String) {
|
||||
abstract fun perform(
|
||||
node: AccessibilityNodeInfo,
|
||||
position: RawSubscription.Position?,
|
||||
locationProps: RawSubscription.LocationProps,
|
||||
): ActionResult
|
||||
|
||||
data object ClickNode : ActionPerformer("clickNode") {
|
||||
override fun perform(
|
||||
node: AccessibilityNodeInfo,
|
||||
position: RawSubscription.Position?,
|
||||
locationProps: RawSubscription.LocationProps,
|
||||
): ActionResult {
|
||||
return ActionResult(
|
||||
action = action,
|
||||
@@ -48,20 +49,24 @@ sealed class ActionPerformer(val action: String) {
|
||||
data object ClickCenter : ActionPerformer("clickCenter") {
|
||||
override fun perform(
|
||||
node: AccessibilityNodeInfo,
|
||||
position: RawSubscription.Position?,
|
||||
locationProps: RawSubscription.LocationProps,
|
||||
): ActionResult {
|
||||
val rect = node.casted.boundsInScreen
|
||||
val p = position?.calc(rect)
|
||||
val p = locationProps.position?.calc(rect)
|
||||
val x = p?.first ?: ((rect.right + rect.left) / 2f)
|
||||
val y = p?.second ?: ((rect.bottom + rect.top) / 2f)
|
||||
if (!ScreenUtils.inScreen(x, y)) {
|
||||
return ActionResult(
|
||||
action = action,
|
||||
result = false,
|
||||
position = x to y,
|
||||
)
|
||||
}
|
||||
return ActionResult(
|
||||
action = action,
|
||||
result = if (0 <= x && 0 <= y && x <= ScreenUtils.getScreenWidth() && y <= ScreenUtils.getScreenHeight()) {
|
||||
if (shizukuContextFlow.value.tap(x, y)) {
|
||||
return ActionResult(
|
||||
action = action, result = true, shizuku = true, position = x to y
|
||||
)
|
||||
}
|
||||
result = if (shizukuContextFlow.value.tap(x, y)) {
|
||||
true
|
||||
} else {
|
||||
val gestureDescription = GestureDescription.Builder()
|
||||
val path = Path()
|
||||
path.moveTo(x, y)
|
||||
@@ -73,8 +78,6 @@ sealed class ActionPerformer(val action: String) {
|
||||
A11yService.instance?.dispatchGesture(
|
||||
gestureDescription.build(), null, null
|
||||
) != null
|
||||
} else {
|
||||
false
|
||||
},
|
||||
position = x to y
|
||||
)
|
||||
@@ -84,65 +87,72 @@ sealed class ActionPerformer(val action: String) {
|
||||
data object Click : ActionPerformer("click") {
|
||||
override fun perform(
|
||||
node: AccessibilityNodeInfo,
|
||||
position: RawSubscription.Position?,
|
||||
locationProps: RawSubscription.LocationProps,
|
||||
): ActionResult {
|
||||
if (node.isClickable) {
|
||||
val result = ClickNode.perform(node, position)
|
||||
val result = ClickNode.perform(node, locationProps)
|
||||
if (result.result) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return ClickCenter.perform(node, position)
|
||||
return ClickCenter.perform(node, locationProps)
|
||||
}
|
||||
}
|
||||
|
||||
data object LongClickNode : ActionPerformer("longClickNode") {
|
||||
override fun perform(
|
||||
node: AccessibilityNodeInfo,
|
||||
position: RawSubscription.Position?,
|
||||
locationProps: RawSubscription.LocationProps,
|
||||
): ActionResult {
|
||||
return ActionResult(
|
||||
action = action,
|
||||
result = node.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK)
|
||||
result = node.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK).apply {
|
||||
if (this) {
|
||||
Thread.sleep(LongClickCenter.LONG_DURATION)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data object LongClickCenter : ActionPerformer("longClickCenter") {
|
||||
const val LONG_DURATION = 500L
|
||||
override fun perform(
|
||||
node: AccessibilityNodeInfo,
|
||||
position: RawSubscription.Position?,
|
||||
locationProps: RawSubscription.LocationProps,
|
||||
): ActionResult {
|
||||
val rect = node.casted.boundsInScreen
|
||||
val p = position?.calc(rect)
|
||||
val p = locationProps.position?.calc(rect)
|
||||
val x = p?.first ?: ((rect.right + rect.left) / 2f)
|
||||
val y = p?.second ?: ((rect.bottom + rect.top) / 2f)
|
||||
// 某些系统的 ViewConfiguration.getLongPressTimeout() 返回 300 , 这将导致触发普通的 click 事件
|
||||
val longClickDuration = 500L
|
||||
if (!ScreenUtils.inScreen(x, y)) {
|
||||
return ActionResult(
|
||||
action = action,
|
||||
result = false,
|
||||
position = x to y,
|
||||
)
|
||||
}
|
||||
return ActionResult(
|
||||
action = action,
|
||||
result = if (0 <= x && 0 <= y && x <= ScreenUtils.getScreenWidth() && y <= ScreenUtils.getScreenHeight()) {
|
||||
if (shizukuContextFlow.value.tap(
|
||||
x, y, longClickDuration
|
||||
)
|
||||
) {
|
||||
return ActionResult(
|
||||
action = action, result = true, shizuku = true, position = x to y
|
||||
)
|
||||
}
|
||||
result = if (shizukuContextFlow.value.tap(x, y, LONG_DURATION)) {
|
||||
true
|
||||
} else {
|
||||
val gestureDescription = GestureDescription.Builder()
|
||||
val path = Path()
|
||||
path.moveTo(x, y)
|
||||
gestureDescription.addStroke(
|
||||
GestureDescription.StrokeDescription(
|
||||
path, 0, longClickDuration
|
||||
path, 0, LONG_DURATION
|
||||
)
|
||||
)
|
||||
A11yService.instance?.dispatchGesture(
|
||||
(A11yService.instance?.dispatchGesture(
|
||||
gestureDescription.build(), null, null
|
||||
) != null
|
||||
} else {
|
||||
false
|
||||
) != null).apply {
|
||||
if (this) {
|
||||
Thread.sleep(LONG_DURATION)
|
||||
}
|
||||
}
|
||||
},
|
||||
position = x to y
|
||||
)
|
||||
@@ -152,22 +162,22 @@ sealed class ActionPerformer(val action: String) {
|
||||
data object LongClick : ActionPerformer("longClick") {
|
||||
override fun perform(
|
||||
node: AccessibilityNodeInfo,
|
||||
position: RawSubscription.Position?,
|
||||
locationProps: RawSubscription.LocationProps,
|
||||
): ActionResult {
|
||||
if (node.isLongClickable) {
|
||||
val result = LongClickNode.perform(node, position)
|
||||
val result = LongClickNode.perform(node, locationProps)
|
||||
if (result.result) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return LongClickCenter.perform(node, position)
|
||||
return LongClickCenter.perform(node, locationProps)
|
||||
}
|
||||
}
|
||||
|
||||
data object Back : ActionPerformer("back") {
|
||||
override fun perform(
|
||||
node: AccessibilityNodeInfo,
|
||||
position: RawSubscription.Position?,
|
||||
locationProps: RawSubscription.LocationProps,
|
||||
): ActionResult {
|
||||
return ActionResult(
|
||||
action = action,
|
||||
@@ -179,18 +189,89 @@ sealed class ActionPerformer(val action: String) {
|
||||
data object None : ActionPerformer("none") {
|
||||
override fun perform(
|
||||
node: AccessibilityNodeInfo,
|
||||
position: RawSubscription.Position?,
|
||||
locationProps: RawSubscription.LocationProps,
|
||||
): ActionResult {
|
||||
return ActionResult(
|
||||
action = action, result = true
|
||||
action = action,
|
||||
result = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data object Swipe : ActionPerformer("swipe") {
|
||||
override fun perform(
|
||||
node: AccessibilityNodeInfo,
|
||||
locationProps: RawSubscription.LocationProps,
|
||||
): ActionResult {
|
||||
val rect = node.casted.boundsInScreen
|
||||
val swipeArg = locationProps.swipeArg ?: return None.perform(node, locationProps)
|
||||
val startP = swipeArg.start.calc(rect)
|
||||
val endP = swipeArg.end?.calc(rect) ?: startP
|
||||
if (startP == null || endP == null) {
|
||||
return None.perform(node, locationProps)
|
||||
}
|
||||
val startX = startP.first
|
||||
val startY = startP.second
|
||||
val endX = endP.first
|
||||
val endY = endP.second
|
||||
if (!(ScreenUtils.inScreen(startX, startY) && ScreenUtils.inScreen(endX, endY))) {
|
||||
return ActionResult(
|
||||
action = action,
|
||||
result = false,
|
||||
position = endX to endY,
|
||||
)
|
||||
}
|
||||
return if (shizukuContextFlow.value.swipe(
|
||||
startX,
|
||||
startY,
|
||||
endX,
|
||||
endY,
|
||||
swipeArg.duration
|
||||
)
|
||||
) {
|
||||
ActionResult(
|
||||
action = action,
|
||||
result = true,
|
||||
shell = true,
|
||||
position = endX to endY,
|
||||
)
|
||||
} else {
|
||||
val gestureDescription = GestureDescription.Builder()
|
||||
val path = Path()
|
||||
path.moveTo(startX, startY)
|
||||
path.lineTo(endX, endY)
|
||||
gestureDescription.addStroke(
|
||||
GestureDescription.StrokeDescription(
|
||||
path, 0, swipeArg.duration
|
||||
)
|
||||
)
|
||||
ActionResult(
|
||||
action = action,
|
||||
result = (A11yService.instance?.dispatchGesture(
|
||||
gestureDescription.build(), null, null
|
||||
) != null).apply {
|
||||
if (this) {
|
||||
Thread.sleep(swipeArg.duration)
|
||||
}
|
||||
},
|
||||
position = endX to endY,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val allSubObjects by lazy {
|
||||
arrayOf(
|
||||
ClickNode, ClickCenter, Click, LongClickNode, LongClickCenter, LongClick, Back, None
|
||||
ClickNode,
|
||||
ClickCenter,
|
||||
Click,
|
||||
LongClickNode,
|
||||
LongClickCenter,
|
||||
LongClick,
|
||||
Back,
|
||||
None,
|
||||
Swipe,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,11 @@ import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlinx.serialization.json.long
|
||||
import li.songe.gkd.a11y.A11yRuleEngine
|
||||
import li.songe.gkd.a11y.typeInfo
|
||||
import li.songe.gkd.util.LOCAL_SUBS_IDS
|
||||
import li.songe.gkd.util.LogUtils
|
||||
import li.songe.gkd.util.ScreenUtils
|
||||
import li.songe.gkd.util.appInfoMapFlow
|
||||
import li.songe.gkd.util.distinctByIfAny
|
||||
import li.songe.gkd.util.filterIfNotAll
|
||||
@@ -60,16 +62,6 @@ data class RawSubscription(
|
||||
|
||||
val hasRule get() = globalGroups.isNotEmpty() || apps.any { it.groups.isNotEmpty() }
|
||||
|
||||
val usedApps by lazy {
|
||||
apps.run {
|
||||
if (any { it.groups.isEmpty() }) {
|
||||
filterNot { it.groups.isEmpty() }
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getSafeCategory(key: Int): RawCategory {
|
||||
return categories.find { it.key == key } ?: RawCategory(
|
||||
key = key,
|
||||
@@ -112,8 +104,6 @@ data class RawSubscription(
|
||||
return categoryAppsMap[categoryKey] ?: emptyList()
|
||||
}
|
||||
|
||||
fun getCategoryGroups(categoryKey: Int) = categoryGroupsMap[categoryKey] ?: emptyList()
|
||||
|
||||
fun getCategory(groupName: String): RawCategory? {
|
||||
return categories.find { c -> groupName.startsWith(c.name) }
|
||||
}
|
||||
@@ -260,15 +250,37 @@ data class RawSubscription(
|
||||
|
||||
@Serializable
|
||||
data class Position(
|
||||
val left: String?, val top: String?, val right: String?, val bottom: String?
|
||||
val left: String?,
|
||||
val top: String?,
|
||||
val right: String?,
|
||||
val bottom: String?,
|
||||
val x: String?,
|
||||
val y: 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) }
|
||||
private val xExp by lazy { getExpression(x) }
|
||||
private val yExp by lazy { getExpression(y) }
|
||||
|
||||
val isValid by lazy {
|
||||
((leftExp != null && (topExp != null || bottomExp != null)) || (rightExp != null && (topExp != null || bottomExp != null)))
|
||||
arrayOf(
|
||||
leftExp to topExp,
|
||||
leftExp to bottomExp,
|
||||
rightExp to topExp,
|
||||
rightExp to bottomExp,
|
||||
xExp to yExp,
|
||||
).any {
|
||||
it.first != null && it.second != null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val isUseAppRect by lazy {
|
||||
listOfNotNull(leftExp, topExp, rightExp, bottomExp, xExp, yExp).any { exp ->
|
||||
exp.variableNames.any { v -> v == "appWidth" || v == "appHeight" }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,11 +288,23 @@ data class RawSubscription(
|
||||
*/
|
||||
fun calc(rect: Rect): Pair<Float, Float>? {
|
||||
if (!isValid) return null
|
||||
val appRect = if (isUseAppRect) {
|
||||
Rect().apply {
|
||||
A11yRuleEngine.service?.windowNodeInfo?.getBoundsInScreen(this)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
arrayOf(
|
||||
leftExp, topExp, rightExp, bottomExp
|
||||
leftExp,
|
||||
topExp,
|
||||
rightExp,
|
||||
bottomExp,
|
||||
xExp,
|
||||
yExp,
|
||||
).forEach { exp ->
|
||||
if (exp != null) {
|
||||
setVariables(exp, rect)
|
||||
setVariables(exp, rect, appRect)
|
||||
}
|
||||
}
|
||||
try {
|
||||
@@ -302,6 +326,10 @@ data class RawSubscription(
|
||||
return (rect.right - rightExp!!.evaluate()
|
||||
.toFloat()) to (rect.bottom - bottomExp!!.evaluate().toFloat())
|
||||
}
|
||||
} else if (xExp != null) {
|
||||
if (yExp != null) {
|
||||
return xExp!!.evaluate().toFloat() to yExp!!.evaluate().toFloat()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// 可能存在 1/0 导致错误
|
||||
@@ -334,12 +362,26 @@ data class RawSubscription(
|
||||
val priorityActionMaximum: Int?
|
||||
}
|
||||
|
||||
sealed interface RawRuleProps : RawCommonProps {
|
||||
@Serializable
|
||||
data class SwipeArg(
|
||||
val start: Position,
|
||||
val end: Position?,
|
||||
val duration: Long,
|
||||
)
|
||||
|
||||
interface LocationProps {
|
||||
// click
|
||||
val position: Position?
|
||||
|
||||
// swipe
|
||||
val swipeArg: SwipeArg?
|
||||
}
|
||||
|
||||
sealed interface RawRuleProps : RawCommonProps, LocationProps {
|
||||
val name: String?
|
||||
val key: Int?
|
||||
val preKeys: List<Int>?
|
||||
val action: String?
|
||||
val position: Position?
|
||||
val matches: List<String>?
|
||||
val anyMatches: List<String>?
|
||||
val excludeMatches: List<String>?
|
||||
@@ -499,6 +541,7 @@ data class RawSubscription(
|
||||
override val preKeys: List<Int>?,
|
||||
override val action: String?,
|
||||
override val position: Position?,
|
||||
override val swipeArg: SwipeArg?,
|
||||
override val matches: List<String>?,
|
||||
override val excludeMatches: List<String>?,
|
||||
override val excludeAllMatches: List<String>?,
|
||||
@@ -559,6 +602,7 @@ data class RawSubscription(
|
||||
override val preKeys: List<Int>?,
|
||||
override val action: String?,
|
||||
override val position: Position?,
|
||||
override val swipeArg: SwipeArg?,
|
||||
override val matches: List<String>?,
|
||||
override val excludeMatches: List<String>?,
|
||||
override val excludeAllMatches: List<String>?,
|
||||
@@ -623,7 +667,7 @@ data class RawSubscription(
|
||||
"random"
|
||||
)
|
||||
|
||||
private fun setVariables(exp: Expression, rect: Rect) {
|
||||
private fun setVariables(exp: Expression, rect: Rect, appRect: Rect?) {
|
||||
exp.setVariable("left", rect.left.toDouble())
|
||||
exp.setVariable("top", rect.top.toDouble())
|
||||
exp.setVariable("right", rect.right.toDouble())
|
||||
@@ -631,6 +675,12 @@ data class RawSubscription(
|
||||
exp.setVariable("width", rect.width().toDouble())
|
||||
exp.setVariable("height", rect.height().toDouble())
|
||||
exp.setVariable("random", Math.random())
|
||||
exp.setVariable("screenWidth", ScreenUtils.getScreenWidth().toDouble())
|
||||
exp.setVariable("screenHeight", ScreenUtils.getScreenHeight().toDouble())
|
||||
if (appRect != null) {
|
||||
exp.setVariable("appWidth", appRect.width().toDouble())
|
||||
exp.setVariable("appHeight", appRect.height().toDouble())
|
||||
}
|
||||
}
|
||||
|
||||
private fun getExpression(value: String?): Expression? {
|
||||
@@ -657,22 +707,37 @@ data class RawSubscription(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPosition(jsonObject: JsonObject?): 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,
|
||||
)
|
||||
}
|
||||
private fun getPosition(jsonObject: JsonObject?, useSelf: Boolean = false): Position? {
|
||||
return when (val element = if (useSelf) jsonObject else jsonObject?.get("position")) {
|
||||
is JsonObject -> Position(
|
||||
left = element["left"]?.jsonPrimitive?.content,
|
||||
bottom = element["bottom"]?.jsonPrimitive?.content,
|
||||
top = element["top"]?.jsonPrimitive?.content,
|
||||
right = element["right"]?.jsonPrimitive?.content,
|
||||
x = element["x"]?.jsonPrimitive?.content,
|
||||
y = element["y"]?.jsonPrimitive?.content,
|
||||
)
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSwipeArg(
|
||||
jsonObject: JsonObject?
|
||||
): SwipeArg? = when (val element = jsonObject?.get("swipeArg")) {
|
||||
is JsonObject -> {
|
||||
SwipeArg(
|
||||
start = getPosition(element["start"]?.jsonObject, true)
|
||||
?: error("swipe start position is required"),
|
||||
end = getPosition(element["end"]?.jsonObject, true),
|
||||
duration = element["duration"]?.jsonPrimitive?.long
|
||||
?: error("swipe duration is required"),
|
||||
)
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun getStringIArray(jsonObject: JsonObject?, name: String): List<String>? {
|
||||
return when (val element = jsonObject?.get(name)) {
|
||||
JsonNull, null -> null
|
||||
@@ -841,6 +906,7 @@ data class RawSubscription(
|
||||
versionCode = getCompatVersionCode(jsonObject),
|
||||
versionName = getCompatVersionName(jsonObject),
|
||||
position = getPosition(jsonObject),
|
||||
swipeArg = getSwipeArg(jsonObject),
|
||||
forcedTime = getLong(jsonObject, "forcedTime"),
|
||||
priorityTime = getLong(jsonObject, "priorityTime"),
|
||||
priorityActionMaximum = getInt(jsonObject, "priorityActionMaximum"),
|
||||
@@ -958,6 +1024,7 @@ data class RawSubscription(
|
||||
order = getInt(jsonObject, "order"),
|
||||
forcedTime = getLong(jsonObject, "forcedTime"),
|
||||
position = getPosition(jsonObject),
|
||||
swipeArg = getSwipeArg(jsonObject),
|
||||
priorityTime = getLong(jsonObject, "priorityTime"),
|
||||
priorityActionMaximum = getInt(jsonObject, "priorityActionMaximum"),
|
||||
)
|
||||
|
||||
@@ -159,10 +159,12 @@ sealed class ResolvedRule(
|
||||
|
||||
private val performer = ActionPerformer.getAction(rule.action ?: rule.position?.let {
|
||||
ActionPerformer.ClickCenter.action
|
||||
} ?: rule.swipeArg?.let {
|
||||
ActionPerformer.Swipe.action
|
||||
})
|
||||
|
||||
fun performAction(node: AccessibilityNodeInfo): ActionResult {
|
||||
return performer.perform(node, rule.position)
|
||||
return performer.perform(node, rule)
|
||||
}
|
||||
|
||||
val matchDelayJob = atomic<Job?>(null)
|
||||
|
||||
@@ -37,6 +37,11 @@ class SafeInputManager(private val value: IInputManager) {
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun swipe(x1: Float, y1: Float, x2: Float, y2: Float, duration: Long) {
|
||||
command.runSwipe(x1, y1, x2, y2, duration)
|
||||
}
|
||||
|
||||
fun key(keyCode: Int) = command.runKeyEvent(keyCode)
|
||||
|
||||
}
|
||||
@@ -144,6 +144,16 @@ class ShizukuContext(
|
||||
return serviceWrapper?.tap(x, y, duration) ?: (inputManager?.tap(x, y, duration) != null)
|
||||
}
|
||||
|
||||
fun swipe(x1: Float, y1: Float, x2: Float, y2: Float, duration: Long): Boolean {
|
||||
return serviceWrapper?.swipe(x1, y1, x2, y2, duration) ?: (inputManager?.swipe(
|
||||
x1,
|
||||
y1,
|
||||
x2,
|
||||
y2,
|
||||
duration
|
||||
) != null)
|
||||
}
|
||||
|
||||
fun topCpn(): ComponentName? {
|
||||
return (activityTaskManager?.getTasks()
|
||||
?: activityManager?.getTasks())?.firstOrNull()?.topActivity
|
||||
|
||||
@@ -165,6 +165,11 @@ data class UserServiceWrapper(
|
||||
return execCommandForResult(command).ok
|
||||
}
|
||||
|
||||
fun swipe(x1: Float, y1: Float, x2: Float, y2: Float, duration: Long): Boolean {
|
||||
val command = "input swipe $x1 $y1 $x2 $y2 $duration"
|
||||
return execCommandForResult(command).ok
|
||||
}
|
||||
|
||||
fun screencapFile(filePath: String): Boolean {
|
||||
val tempPath = "/data/local/tmp/screencap_${System.currentTimeMillis()}.png"
|
||||
val command = "screencap -p $tempPath"
|
||||
|
||||
@@ -22,4 +22,8 @@ object ScreenUtils {
|
||||
}
|
||||
|
||||
fun isScreenLock(): Boolean = app.keyguardManager.inKeyguardRestrictedInputMode()
|
||||
|
||||
fun inScreen(x: Float, y: Float): Boolean {
|
||||
return 0 <= x && 0 <= y && x <= getScreenWidth() && y <= getScreenHeight()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user