mirror of
https://github.com/gkd-kit/gkd.git
synced 2024-11-16 11:42:22 +08:00
Merge branch 'main' into pr/tiann/402
This commit is contained in:
commit
e5d96dcfc0
|
@ -1,43 +1,25 @@
|
|||
package li.songe.gkd.data
|
||||
|
||||
import li.songe.gkd.service.TopActivity
|
||||
import li.songe.selector.Selector
|
||||
|
||||
class AppRule(
|
||||
matches: List<Selector>,
|
||||
excludeMatches: List<Selector>,
|
||||
actionDelay: Long,
|
||||
quickFind: Boolean,
|
||||
matchDelay: Long,
|
||||
matchTime: Long?,
|
||||
resetMatch: String?,
|
||||
key: Int?,
|
||||
preKeys: Set<Int>,
|
||||
index: Int,
|
||||
rule: RawSubscription.RawAppRule,
|
||||
subsItem: SubsItem,
|
||||
group: RawSubscription.RawAppGroup,
|
||||
rawSubs: RawSubscription,
|
||||
val appId: String,
|
||||
val activityIds: List<String>,
|
||||
val excludeActivityIds: List<String>,
|
||||
val app: RawSubscription.RawApp,
|
||||
) : ResolvedRule(
|
||||
matches = matches,
|
||||
excludeMatches = excludeMatches,
|
||||
actionDelay = actionDelay,
|
||||
quickFind = quickFind,
|
||||
matchDelay = matchDelay,
|
||||
matchTime = matchTime,
|
||||
resetMatch = resetMatch,
|
||||
key = key,
|
||||
preKeys = preKeys,
|
||||
index = index,
|
||||
rule = rule,
|
||||
group = group,
|
||||
subsItem = subsItem,
|
||||
rawSubs = rawSubs,
|
||||
) {
|
||||
val appId = app.id
|
||||
val activityIds = getFixActivityIds(app.id, rule.activityIds ?: group.activityIds)
|
||||
val excludeActivityIds =
|
||||
getFixActivityIds(app.id, rule.excludeActivityIds ?: group.excludeActivityIds)
|
||||
|
||||
override val type = "app"
|
||||
override fun matchActivity(topActivity: TopActivity?): Boolean {
|
||||
topActivity ?: return false
|
||||
topActivity.activityId ?: return true
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package li.songe.gkd.data
|
||||
|
||||
import li.songe.gkd.service.TopActivity
|
||||
import li.songe.selector.Selector
|
||||
|
||||
data class GlobalApp(
|
||||
val id: String,
|
||||
|
@ -11,38 +10,31 @@ data class GlobalApp(
|
|||
)
|
||||
|
||||
class GlobalRule(
|
||||
matches: List<Selector>,
|
||||
excludeMatches: List<Selector>,
|
||||
actionDelay: Long,
|
||||
quickFind: Boolean,
|
||||
matchDelay: Long,
|
||||
matchTime: Long?,
|
||||
resetMatch: String?,
|
||||
key: Int?,
|
||||
preKeys: Set<Int>,
|
||||
index: Int,
|
||||
subsItem: SubsItem,
|
||||
rule: RawSubscription.RawGlobalRule,
|
||||
group: RawSubscription.RawGlobalGroup,
|
||||
rawSubs: RawSubscription,
|
||||
val apps: Map<String, GlobalApp>,
|
||||
val matchAnyApp: Boolean,
|
||||
) : ResolvedRule(
|
||||
matches = matches,
|
||||
excludeMatches = excludeMatches,
|
||||
actionDelay = actionDelay,
|
||||
quickFind = quickFind,
|
||||
matchDelay = matchDelay,
|
||||
matchTime = matchTime,
|
||||
resetMatch = resetMatch,
|
||||
key = key,
|
||||
preKeys = preKeys,
|
||||
index = index,
|
||||
rule = rule,
|
||||
group = group,
|
||||
subsItem = subsItem,
|
||||
rawSubs = rawSubs,
|
||||
) {
|
||||
|
||||
val matchAnyApp = rule.matchAnyApp ?: group.matchAnyApp ?: true
|
||||
val apps = mutableMapOf<String, GlobalApp>().apply {
|
||||
(rule.apps ?: group.apps ?: emptyList()).forEach { a ->
|
||||
this[a.id] = GlobalApp(
|
||||
id = a.id,
|
||||
enable = a.enable ?: true,
|
||||
activityIds = getFixActivityIds(a.id, a.activityIds),
|
||||
excludeActivityIds = getFixActivityIds(a.id, a.excludeActivityIds)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override val type = "global"
|
||||
|
||||
private val excludeAppIds = apps.filter { e -> !e.value.enable }.keys
|
||||
override fun matchActivity(topActivity: TopActivity?): Boolean {
|
||||
topActivity ?: return false
|
||||
|
|
|
@ -25,9 +25,9 @@ data class RawSubscription(
|
|||
val updateUrl: String? = null,
|
||||
val supportUri: String? = null,
|
||||
val checkUpdateUrl: String? = null,
|
||||
val apps: List<RawApp> = emptyList(),
|
||||
val categories: List<RawCategory> = emptyList(),
|
||||
val globalGroups: List<RawGlobalGroup> = emptyList(),
|
||||
val categories: List<RawCategory> = emptyList(),
|
||||
val apps: List<RawApp> = emptyList(),
|
||||
) {
|
||||
|
||||
@IgnoredOnParcel
|
||||
|
@ -57,8 +57,27 @@ data class RawSubscription(
|
|||
}
|
||||
|
||||
@IgnoredOnParcel
|
||||
val allGroupSize by lazy {
|
||||
globalGroups.size + appGroups.size
|
||||
val numText by lazy {
|
||||
val appsSize = apps.size
|
||||
val appGroupsSize = appGroups.size
|
||||
val globalGroupSize = globalGroups.size
|
||||
if (appGroupsSize + globalGroupSize > 0) {
|
||||
if (globalGroupSize > 0) {
|
||||
"${globalGroupSize}全局" + if (appGroupsSize > 0) {
|
||||
"/"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
} else {
|
||||
""
|
||||
} + if (appGroupsSize > 0) {
|
||||
"${appsSize}应用/${appGroupsSize}规则组"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
} else {
|
||||
"暂无规则"
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
|
@ -118,9 +137,9 @@ data class RawSubscription(
|
|||
data class RawGlobalApp(
|
||||
val id: String,
|
||||
val enable: Boolean?,
|
||||
val activityIds: List<String>?,
|
||||
val excludeActivityIds: List<String>?,
|
||||
)
|
||||
override val activityIds: List<String>?,
|
||||
override val excludeActivityIds: List<String>?,
|
||||
) : RawAppRuleProps
|
||||
|
||||
|
||||
@Serializable
|
||||
|
|
|
@ -9,26 +9,63 @@ import li.songe.gkd.service.querySelector
|
|||
import li.songe.selector.Selector
|
||||
|
||||
sealed class ResolvedRule(
|
||||
val matches: List<Selector>,
|
||||
val excludeMatches: List<Selector>,
|
||||
|
||||
val actionDelay: Long,
|
||||
val quickFind: Boolean,
|
||||
|
||||
val matchDelay: Long,
|
||||
val matchTime: Long?,
|
||||
val resetMatch: String?,
|
||||
|
||||
val key: Int?,
|
||||
val preKeys: Set<Int>,
|
||||
|
||||
val index: Int,
|
||||
val rule: RawSubscription.RawRuleProps,
|
||||
val group: RawSubscription.RawGroupProps,
|
||||
val rawSubs: RawSubscription,
|
||||
val subsItem: SubsItem,
|
||||
) {
|
||||
var preAppRules: Set<ResolvedRule> = emptySet()
|
||||
val key = rule.key
|
||||
val index = group.rules.indexOf(rule)
|
||||
val preKeys = (rule.preKeys ?: emptyList()).toSet()
|
||||
val resetMatch = rule.resetMatch ?: group.resetMatch
|
||||
val matches = rule.matches.map { s -> Selector.parse(s) }
|
||||
val excludeMatches = (rule.excludeMatches ?: emptyList()).map { s -> Selector.parse(s) }
|
||||
val matchDelay = rule.matchDelay ?: group.matchDelay ?: 0L
|
||||
val actionDelay = rule.actionDelay ?: group.actionDelay ?: 0L
|
||||
val matchTime = rule.matchTime ?: group.matchTime
|
||||
val quickFind = rule.quickFind ?: group.quickFind ?: false
|
||||
|
||||
val actionCdKey = rule.actionCdKey ?: group.actionCdKey
|
||||
val actionCd = rule.actionCd ?: if (actionCdKey != null) {
|
||||
group.rules.find { r -> r.key == actionCdKey }?.actionCd
|
||||
} else {
|
||||
null
|
||||
} ?: group.actionCd ?: 1000L
|
||||
|
||||
val actionMaximumKey = rule.actionMaximumKey ?: group.actionMaximumKey
|
||||
val actionMaximum = rule.actionMaximum ?: if (actionMaximumKey != null) {
|
||||
group.rules.find { r -> r.key == actionMaximumKey }?.actionMaximum
|
||||
} else {
|
||||
null
|
||||
} ?: group.actionMaximum
|
||||
|
||||
var groupRules: List<ResolvedRule> = emptyList()
|
||||
set(value) {
|
||||
field = value
|
||||
// 共享次数
|
||||
if (actionMaximumKey != null) {
|
||||
val otherRule = field.find { r -> r.key == actionMaximumKey }
|
||||
if (otherRule != null) {
|
||||
actionCount = otherRule.actionCount
|
||||
}
|
||||
}
|
||||
// 共享 cd
|
||||
if (actionCdKey != null) {
|
||||
val otherRule = field.find { r -> r.key == actionCdKey }
|
||||
if (otherRule != null) {
|
||||
actionTriggerTime = otherRule.actionTriggerTime
|
||||
}
|
||||
}
|
||||
preRules = field.filter { otherRule ->
|
||||
(otherRule.key != null) && preKeys.contains(
|
||||
otherRule.key
|
||||
)
|
||||
}.toSet()
|
||||
}
|
||||
|
||||
var preRules = emptySet<ResolvedRule>()
|
||||
val hasNext = group.rules.any { r -> r.preKeys?.any { k -> k == rule.key } == true }
|
||||
|
||||
var actionDelayTriggerTime = 0L
|
||||
var actionDelayJob: Job? = null
|
||||
fun checkDelay(): Boolean {
|
||||
|
@ -39,12 +76,6 @@ sealed class ResolvedRule(
|
|||
return false
|
||||
}
|
||||
|
||||
val actionCd = (if (rule.actionCdKey != null) {
|
||||
group.rules.find { r -> r.key == rule.actionCdKey }?.actionCd ?: group.actionCd
|
||||
} else {
|
||||
null
|
||||
} ?: rule.actionCd ?: group.actionCd ?: 1000L)
|
||||
|
||||
var actionTriggerTime = Value(0L)
|
||||
fun trigger() {
|
||||
actionTriggerTime.value = System.currentTimeMillis()
|
||||
|
@ -55,13 +86,6 @@ sealed class ResolvedRule(
|
|||
lastTriggerRule = this
|
||||
}
|
||||
|
||||
val actionMaximum = ((if (rule.actionMaximumKey != null) {
|
||||
group.rules.find { r -> r.key == rule.actionMaximumKey }?.actionMaximum
|
||||
?: group.actionMaximum
|
||||
} else {
|
||||
null
|
||||
}) ?: rule.actionMaximum ?: group.actionMaximum)
|
||||
|
||||
var actionCount = Value(0)
|
||||
|
||||
var matchChangedTime = 0L
|
||||
|
@ -90,42 +114,69 @@ sealed class ResolvedRule(
|
|||
|
||||
var matchDelayJob: Job? = null
|
||||
|
||||
val statusCode: Int
|
||||
val status: RuleStatus
|
||||
get() {
|
||||
if (actionMaximum != null) {
|
||||
if (actionCount.value >= actionMaximum) {
|
||||
return 1 // 达到最大执行次数
|
||||
return RuleStatus.Status1 // 达到最大执行次数
|
||||
}
|
||||
}
|
||||
if (preAppRules.isNotEmpty()) { // 需要提前点击某个规则
|
||||
lastTriggerRule ?: return 2
|
||||
return if (preAppRules.any { it === lastTriggerRule }) {
|
||||
0
|
||||
if (preRules.isNotEmpty()) { // 需要提前点击某个规则
|
||||
return if (preRules.any { it === lastTriggerRule }) {
|
||||
RuleStatus.StatusOk
|
||||
} else {
|
||||
3 // 上一个点击的规则不在当前需要点击的列表
|
||||
RuleStatus.Status2
|
||||
}
|
||||
}
|
||||
val t = System.currentTimeMillis()
|
||||
if (matchDelay > 0 && t - matchChangedTime < matchDelay) {
|
||||
return 4 // 处于匹配延迟中
|
||||
return RuleStatus.Status3 // 处于匹配延迟中
|
||||
}
|
||||
if (matchTime != null && t - matchChangedTime > matchLimitTime) {
|
||||
return 5 // 超出匹配时间
|
||||
return RuleStatus.Status4 // 超出匹配时间
|
||||
}
|
||||
if (actionTriggerTime.value + actionCd > t) {
|
||||
return 6 // 处于冷却时间
|
||||
return RuleStatus.Status5 // 处于冷却时间
|
||||
}
|
||||
if (actionDelayTriggerTime > 0) {
|
||||
if (actionDelayTriggerTime + actionDelay > t) {
|
||||
return 7 // 处于点击延迟中
|
||||
return RuleStatus.Status6 // 处于点击延迟中
|
||||
}
|
||||
}
|
||||
return 0
|
||||
return RuleStatus.StatusOk
|
||||
}
|
||||
|
||||
fun statusText(): String {
|
||||
return "id:${subsItem.id}, v:${rawSubs.version}, type:${type}, gKey=${group.key}, gName:${group.name}, index:${index}, key:${key}, status:${status.name}"
|
||||
}
|
||||
|
||||
abstract val type: String
|
||||
abstract fun matchActivity(topActivity: TopActivity?): Boolean
|
||||
|
||||
}
|
||||
|
||||
sealed class RuleStatus(val name: String) {
|
||||
data object StatusOk : RuleStatus("ok")
|
||||
data object Status1 : RuleStatus("达到最大执行次数")
|
||||
data object Status2 : RuleStatus("需要提前点击某个规则")
|
||||
data object Status3 : RuleStatus("处于匹配延迟")
|
||||
data object Status4 : RuleStatus("超出匹配时间")
|
||||
data object Status5 : RuleStatus("处于冷却时间")
|
||||
data object Status6 : RuleStatus("处于点击延迟")
|
||||
}
|
||||
|
||||
fun getFixActivityIds(
|
||||
appId: String,
|
||||
activityIds: List<String>?,
|
||||
): List<String> {
|
||||
activityIds ?: return emptyList()
|
||||
return activityIds.map { activityId ->
|
||||
if (activityId.startsWith('.')) { // .a.b.c -> com.x.y.x.a.b.c
|
||||
appId + activityId
|
||||
} else {
|
||||
activityId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -6,13 +6,15 @@ import io.ktor.http.HttpStatusCode
|
|||
import io.ktor.server.application.createApplicationPlugin
|
||||
import io.ktor.server.application.hooks.CallFailed
|
||||
import io.ktor.server.request.uri
|
||||
import io.ktor.server.response.header
|
||||
import io.ktor.server.response.respond
|
||||
import li.songe.gkd.data.RpcError
|
||||
|
||||
val KtorErrorPlugin = createApplicationPlugin(name = "KtorErrorPlugin") {
|
||||
onCall { call ->
|
||||
Log.d("Ktor", "onCall: ${call.request.uri}")
|
||||
// TODO 在局域网会被扫描工具批量请求多个路径
|
||||
if (call.request.uri == "/" || call.request.uri.startsWith("/api/")) {
|
||||
Log.d("Ktor", "onCall: ${call.request.uri}")
|
||||
}
|
||||
}
|
||||
on(CallFailed) { call, cause ->
|
||||
when (cause) {
|
||||
|
|
|
@ -120,10 +120,15 @@ private val getAttr: (AccessibilityNodeInfo, String) -> Any? = { node, name ->
|
|||
when (name) {
|
||||
"id" -> node.viewIdResourceName
|
||||
"vid" -> node.viewIdResourceName?.let { id ->
|
||||
id.subSequence(
|
||||
(node.packageName?.length ?: 0) + ":id/".length,
|
||||
id.length
|
||||
)
|
||||
val appId = node.packageName
|
||||
if (appId != null && id.startsWith(appId) && id.startsWith(":id/", appId.length)) {
|
||||
id.subSequence(
|
||||
appId.length + ":id/".length,
|
||||
id.length
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
"name" -> node.className
|
||||
|
|
|
@ -102,22 +102,20 @@ var lastTriggerRule: ResolvedRule? = null
|
|||
var lastTriggerTime = 0L
|
||||
var appChangeTime = 0L
|
||||
|
||||
fun insertClickLog(appRule: ResolvedRule) {
|
||||
appRule.trigger()
|
||||
toastClickTip()
|
||||
fun insertClickLog(rule: ResolvedRule) {
|
||||
appScope.launchTry(Dispatchers.IO) {
|
||||
val clickLog = ClickLog(
|
||||
appId = topActivityFlow.value.appId,
|
||||
activityId = topActivityFlow.value.activityId,
|
||||
subsId = appRule.subsItem.id,
|
||||
subsVersion = appRule.rawSubs.version,
|
||||
groupKey = appRule.group.key,
|
||||
groupType = when (appRule) {
|
||||
subsId = rule.subsItem.id,
|
||||
subsVersion = rule.rawSubs.version,
|
||||
groupKey = rule.group.key,
|
||||
groupType = when (rule) {
|
||||
is AppRule -> SubsConfig.AppGroupType
|
||||
is GlobalRule -> SubsConfig.GlobalGroupType
|
||||
},
|
||||
ruleIndex = appRule.index,
|
||||
ruleKey = appRule.key,
|
||||
ruleIndex = rule.index,
|
||||
ruleKey = rule.key,
|
||||
)
|
||||
DbSet.clickLogDao.insert(clickLog)
|
||||
increaseClickCount()
|
||||
|
|
|
@ -23,6 +23,7 @@ import io.ktor.client.statement.bodyAsText
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
@ -37,11 +38,14 @@ import li.songe.gkd.data.AttrInfo
|
|||
import li.songe.gkd.data.GkdAction
|
||||
import li.songe.gkd.data.RawSubscription
|
||||
import li.songe.gkd.data.RpcError
|
||||
import li.songe.gkd.data.RuleStatus
|
||||
import li.songe.gkd.data.SubsVersion
|
||||
import li.songe.gkd.data.getActionFc
|
||||
import li.songe.gkd.db.DbSet
|
||||
import li.songe.gkd.debug.SnapshotExt
|
||||
import li.songe.gkd.shizuku.shizukuIsSafeOK
|
||||
import li.songe.gkd.shizuku.useSafeGetTasksFc
|
||||
import li.songe.gkd.shizuku.useShizukuAliveState
|
||||
import li.songe.gkd.util.VOLUME_CHANGED_ACTION
|
||||
import li.songe.gkd.util.client
|
||||
import li.songe.gkd.util.launchTry
|
||||
|
@ -49,6 +53,7 @@ import li.songe.gkd.util.map
|
|||
import li.songe.gkd.util.storeFlow
|
||||
import li.songe.gkd.util.subsIdToRawFlow
|
||||
import li.songe.gkd.util.subsItemsFlow
|
||||
import li.songe.gkd.util.updateStorage
|
||||
import li.songe.gkd.util.updateSubscription
|
||||
import li.songe.selector.Selector
|
||||
import kotlin.coroutines.resume
|
||||
|
@ -66,7 +71,25 @@ class GkdAbService : CompositionAbService({
|
|||
service = null
|
||||
}
|
||||
|
||||
val safeGetTasksFc = useSafeGetTasksFc(scope)
|
||||
val shizukuAliveFlow = useShizukuAliveState()
|
||||
val shizukuGrantFlow = MutableStateFlow(false)
|
||||
var lastCheckShizukuTime = 0L
|
||||
onAccessibilityEvent { // 借助无障碍轮询校验 shizuku 权限
|
||||
if (storeFlow.value.enableService && it.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {// 筛选降低判断频率
|
||||
val t = System.currentTimeMillis()
|
||||
if (t - lastCheckShizukuTime > 5000L) {
|
||||
lastCheckShizukuTime = t
|
||||
scope.launchTry(Dispatchers.IO) {
|
||||
shizukuGrantFlow.value = if (shizukuAliveFlow.value) {
|
||||
shizukuIsSafeOK()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val safeGetTasksFc = useSafeGetTasksFc(scope, shizukuGrantFlow, shizukuAliveFlow)
|
||||
|
||||
// 当锁屏/上拉通知栏时, safeActiveWindow 没有 activityId, 但是此时 shizuku 获取到是前台 app 的 appId 和 activityId
|
||||
fun getShizukuTopActivity(): TopActivity? {
|
||||
|
@ -94,62 +117,65 @@ class GkdAbService : CompositionAbService({
|
|||
|
||||
var lastTriggerShizukuTime = 0L
|
||||
var lastContentEventTime = 0L
|
||||
val singleThread = Dispatchers.IO.limitedParallelism(1)
|
||||
val queryThread = Dispatchers.IO.limitedParallelism(1)
|
||||
val eventThread = Dispatchers.IO.limitedParallelism(1)
|
||||
onDestroy {
|
||||
singleThread.cancel()
|
||||
queryThread.cancel()
|
||||
}
|
||||
val loopCheckTask = MutableStateFlow(0)
|
||||
var queryTaskJob: Job? = null
|
||||
fun newQueryTask(eventNode: AccessibilityNodeInfo? = null) {
|
||||
if (!storeFlow.value.enableService) return
|
||||
val ctx = if (System.currentTimeMillis() - appChangeTime < 5000L) {
|
||||
Dispatchers.IO
|
||||
} else {
|
||||
singleThread
|
||||
queryThread
|
||||
}
|
||||
scope.launchTry(ctx) {
|
||||
queryTaskJob = scope.launchTry(ctx) {
|
||||
val activityRule = getCurrentRules()
|
||||
for (rule in (activityRule.currentRules)) {
|
||||
val statusCode = rule.statusCode
|
||||
if (statusCode == 4 && rule.matchDelayJob == null) {
|
||||
rule.matchDelayJob = scope.launch {
|
||||
val statusCode = rule.status
|
||||
if (statusCode == RuleStatus.Status3 && rule.matchDelayJob == null) {
|
||||
rule.matchDelayJob = scope.launch(queryThread) {
|
||||
delay(rule.matchDelay)
|
||||
rule.matchDelayJob = null
|
||||
newQueryTask()
|
||||
}
|
||||
}
|
||||
if (statusCode != 0) continue
|
||||
if (statusCode != RuleStatus.StatusOk) continue
|
||||
val nodeVal = (eventNode ?: safeActiveWindow) ?: continue
|
||||
val target = rule.query(nodeVal) ?: continue
|
||||
if (activityRule !== getCurrentRules()) break
|
||||
if (rule.checkDelay() && rule.actionDelayJob == null) {
|
||||
rule.actionDelayJob = scope.launch {
|
||||
rule.actionDelayJob = scope.launch(queryThread) {
|
||||
delay(rule.actionDelay)
|
||||
rule.actionDelayJob = null
|
||||
newQueryTask()
|
||||
}
|
||||
continue
|
||||
}
|
||||
scope.launch(singleThread) {
|
||||
if (rule.statusCode != 0) return@launch
|
||||
scope.launch(queryThread) {
|
||||
if (rule.status != RuleStatus.StatusOk) return@launch
|
||||
val actionResult = rule.performAction(context, target)
|
||||
if (actionResult.result) {
|
||||
rule.trigger()
|
||||
if (rule.hasNext) {
|
||||
scope.launch(queryThread) {
|
||||
delay(500L)
|
||||
if (queryTaskJob?.isActive != true) {
|
||||
newQueryTask()
|
||||
}
|
||||
}
|
||||
}
|
||||
toastClickTip()
|
||||
insertClickLog(rule)
|
||||
LogUtils.d(
|
||||
*rule.matches.toTypedArray(),
|
||||
rule.statusText(),
|
||||
AttrInfo.info2data(nodeVal, 0, 0),
|
||||
actionResult
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (activityRule.currentRules.any { r -> r.statusCode != 5 }) {
|
||||
loopCheckTask.value++
|
||||
}
|
||||
}
|
||||
}
|
||||
scope.launch(singleThread) {
|
||||
loopCheckTask.debounce(5000).collect {
|
||||
newQueryTask()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,7 +195,7 @@ class GkdAbService : CompositionAbService({
|
|||
lastContentEventTime = fixedEvent.time
|
||||
}
|
||||
|
||||
// AccessibilityEvent 的 clear 方法会在后续时间被系统调用导致内部数据丢失
|
||||
// AccessibilityEvent 的 clear 方法会在后续时间被 某些系统 调用导致内部数据丢失
|
||||
// 因此不要在协程/子线程内传递引用, 此处使用 data class 保存数据
|
||||
val evAppId = fixedEvent.appId
|
||||
val evActivityId = fixedEvent.className
|
||||
|
@ -225,51 +251,47 @@ class GkdAbService : CompositionAbService({
|
|||
return@launch
|
||||
}
|
||||
|
||||
if (!storeFlow.value.enableService) return@launch
|
||||
|
||||
newQueryTask(eventNode)
|
||||
}
|
||||
}
|
||||
|
||||
fun checkSubsUpdate() {
|
||||
scope.launchTry(Dispatchers.IO) { // 自动从网络更新订阅文件
|
||||
LogUtils.d("开始自动检测更新")
|
||||
subsItemsFlow.value.forEach { subsItem ->
|
||||
if (subsItem.updateUrl == null) return@forEach
|
||||
try {
|
||||
val oldSubsRaw = subsIdToRawFlow.value[subsItem.id]
|
||||
if (oldSubsRaw?.checkUpdateUrl != null) {
|
||||
try {
|
||||
val subsVersion =
|
||||
client.get(oldSubsRaw.checkUpdateUrl).body<SubsVersion>()
|
||||
LogUtils.d("快速检测更新成功", subsVersion)
|
||||
if (subsVersion.id == oldSubsRaw.id && subsVersion.version <= oldSubsRaw.version) {
|
||||
return@forEach
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
LogUtils.d("快速检测更新失败", subsItem, e)
|
||||
fun checkSubsUpdate() = scope.launchTry(Dispatchers.IO) { // 自动从网络更新订阅文件
|
||||
LogUtils.d("开始自动检测更新")
|
||||
subsItemsFlow.value.forEach { subsItem ->
|
||||
if (subsItem.updateUrl == null) return@forEach
|
||||
try {
|
||||
val oldSubsRaw = subsIdToRawFlow.value[subsItem.id]
|
||||
if (oldSubsRaw?.checkUpdateUrl != null) {
|
||||
try {
|
||||
val subsVersion =
|
||||
client.get(oldSubsRaw.checkUpdateUrl).body<SubsVersion>()
|
||||
LogUtils.d("快速检测更新成功", subsVersion)
|
||||
if (subsVersion.id == oldSubsRaw.id && subsVersion.version <= oldSubsRaw.version) {
|
||||
return@forEach
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
LogUtils.d("快速检测更新失败", subsItem, e)
|
||||
}
|
||||
val newSubsRaw = RawSubscription.parse(
|
||||
client.get(subsItem.updateUrl).bodyAsText()
|
||||
)
|
||||
if (newSubsRaw.id != subsItem.id) {
|
||||
return@forEach
|
||||
}
|
||||
if (oldSubsRaw != null && newSubsRaw.version <= oldSubsRaw.version) {
|
||||
return@forEach
|
||||
}
|
||||
updateSubscription(newSubsRaw)
|
||||
val newItem = subsItem.copy(
|
||||
updateUrl = newSubsRaw.updateUrl ?: subsItem.updateUrl,
|
||||
mtime = System.currentTimeMillis()
|
||||
)
|
||||
DbSet.subsItemDao.update(newItem)
|
||||
LogUtils.d("更新磁盘订阅文件:${newSubsRaw.name}")
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
LogUtils.d("检测更新失败", e)
|
||||
}
|
||||
val newSubsRaw = RawSubscription.parse(
|
||||
client.get(subsItem.updateUrl).bodyAsText()
|
||||
)
|
||||
if (newSubsRaw.id != subsItem.id) {
|
||||
return@forEach
|
||||
}
|
||||
if (oldSubsRaw != null && newSubsRaw.version <= oldSubsRaw.version) {
|
||||
return@forEach
|
||||
}
|
||||
updateSubscription(newSubsRaw)
|
||||
val newItem = subsItem.copy(
|
||||
updateUrl = newSubsRaw.updateUrl ?: subsItem.updateUrl,
|
||||
mtime = System.currentTimeMillis()
|
||||
)
|
||||
DbSet.subsItemDao.update(newItem)
|
||||
LogUtils.d("更新订阅文件:${newSubsRaw.name}")
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
LogUtils.d("检测更新失败", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -291,9 +313,7 @@ class GkdAbService : CompositionAbService({
|
|||
activityRuleFlow.debounce(300).collect {
|
||||
if (storeFlow.value.enableService) {
|
||||
LogUtils.d(it.topActivity, *it.currentRules.map { r ->
|
||||
"id:${r.subsItem.id}, v:${r.rawSubs.version}, gKey=${r.group.key}, gName:${r.group.name}, rIndex:${r.index}, rKey:${r.key}, rCode:${
|
||||
r.statusCode
|
||||
}"
|
||||
r.statusText()
|
||||
}.toTypedArray())
|
||||
} else {
|
||||
LogUtils.d(
|
||||
|
@ -314,7 +334,7 @@ class GkdAbService : CompositionAbService({
|
|||
}
|
||||
}
|
||||
if (it) {
|
||||
aliveView = View(context)
|
||||
val tempView = View(context)
|
||||
val lp = WindowManager.LayoutParams().apply {
|
||||
type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY
|
||||
format = PixelFormat.TRANSLUCENT
|
||||
|
@ -325,10 +345,16 @@ class GkdAbService : CompositionAbService({
|
|||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
try {
|
||||
wm.addView(aliveView, lp)
|
||||
// 在某些机型创建失败, 原因未知
|
||||
wm.addView(tempView, lp)
|
||||
aliveView = tempView
|
||||
} catch (e: Exception) {
|
||||
LogUtils.d(e)
|
||||
ToastUtils.showShort("创建无障碍悬浮窗失败!")
|
||||
LogUtils.d("创建无障碍悬浮窗失败", e)
|
||||
ToastUtils.showShort("创建无障碍悬浮窗失败")
|
||||
updateStorage(
|
||||
storeFlow,
|
||||
storeFlow.value.copy(enableAbFloatWindow = false)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -344,7 +370,7 @@ class GkdAbService : CompositionAbService({
|
|||
}
|
||||
|
||||
|
||||
fun createReceiver(): BroadcastReceiver {
|
||||
fun createVolumeReceiver(): BroadcastReceiver {
|
||||
return object : BroadcastReceiver() {
|
||||
var lastTriggerTime = -1L
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
|
@ -369,7 +395,7 @@ class GkdAbService : CompositionAbService({
|
|||
context.unregisterReceiver(captureVolumeReceiver)
|
||||
}
|
||||
captureVolumeReceiver = if (it) {
|
||||
createReceiver().apply {
|
||||
createVolumeReceiver().apply {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
context.registerReceiver(
|
||||
this, IntentFilter(VOLUME_CHANGED_ACTION), Context.RECEIVER_EXPORTED
|
||||
|
@ -393,11 +419,9 @@ class GkdAbService : CompositionAbService({
|
|||
if (!storeFlow.value.captureScreenshot) return@onAccessibilityEvent
|
||||
val appId = e.packageName ?: return@onAccessibilityEvent
|
||||
val appCls = e.className ?: return@onAccessibilityEvent
|
||||
if (appId.contentEquals("com.miui.screenshot") &&
|
||||
e.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED &&
|
||||
!e.isFullScreen &&
|
||||
appCls.contentEquals("android.widget.RelativeLayout") &&
|
||||
e.text.firstOrNull()?.contentEquals("截屏缩略图") == true // [截屏缩略图, 截长屏, 发送]
|
||||
if (appId.contentEquals("com.miui.screenshot") && e.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && !e.isFullScreen && appCls.contentEquals(
|
||||
"android.widget.RelativeLayout"
|
||||
) && e.text.firstOrNull()?.contentEquals("截屏缩略图") == true // [截屏缩略图, 截长屏, 发送]
|
||||
) {
|
||||
LogUtils.d("captureScreenshot", e)
|
||||
scope.launchTry(Dispatchers.IO) {
|
||||
|
@ -406,7 +430,6 @@ class GkdAbService : CompositionAbService({
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
isRunning.value = true
|
||||
onDestroy {
|
||||
isRunning.value = false
|
||||
|
|
|
@ -33,15 +33,11 @@ class ManageService : CompositionService({
|
|||
) { allRules, clickCount, enableService, abRunning ->
|
||||
if (!abRunning) return@combine "无障碍未授权"
|
||||
if (!enableService) return@combine "服务已暂停"
|
||||
(if (allRules.allGroupSize > 0) {
|
||||
if (allRules.appSize > 0) {
|
||||
"${allRules.appSize}应用/${allRules.allGroupSize}规则组"
|
||||
} else {
|
||||
"${allRules.allGroupSize}规则组"
|
||||
}
|
||||
allRules.numText + if (clickCount > 0) {
|
||||
"/${clickCount}点击"
|
||||
} else {
|
||||
"暂无规则"
|
||||
}) + if (clickCount > 0) "/${clickCount}点击" else ""
|
||||
""
|
||||
}
|
||||
}.stateIn(scope, SharingStarted.Eagerly, "").collect { text ->
|
||||
createNotif(
|
||||
context, defaultChannel.id, abNotif.copy(
|
||||
|
|
|
@ -7,8 +7,6 @@ import android.view.Display
|
|||
import com.blankj.utilcode.util.LogUtils
|
||||
import com.blankj.utilcode.util.ToastUtils
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
@ -16,7 +14,6 @@ import kotlinx.coroutines.flow.combine
|
|||
import kotlinx.coroutines.flow.stateIn
|
||||
import li.songe.gkd.composition.CanOnDestroy
|
||||
import li.songe.gkd.data.DeviceInfo
|
||||
import li.songe.gkd.util.launchWhile
|
||||
import li.songe.gkd.util.map
|
||||
import li.songe.gkd.util.storeFlow
|
||||
import rikka.shizuku.Shizuku
|
||||
|
@ -95,13 +92,11 @@ fun CanOnDestroy.useShizukuAliveState(): StateFlow<Boolean> {
|
|||
return shizukuAliveFlow
|
||||
}
|
||||
|
||||
fun CanOnDestroy.useSafeGetTasksFc(scope: CoroutineScope): () -> List<ActivityManager.RunningTaskInfo>? {
|
||||
val shizukuAliveFlow = useShizukuAliveState()
|
||||
val shizukuGrantFlow = MutableStateFlow(false)
|
||||
scope.launchWhile(Dispatchers.IO) {
|
||||
shizukuGrantFlow.value = if (shizukuAliveFlow.value) shizukuIsSafeOK() else false
|
||||
delay(3000)
|
||||
}
|
||||
fun useSafeGetTasksFc(
|
||||
scope: CoroutineScope,
|
||||
shizukuGrantFlow: StateFlow<Boolean>,
|
||||
shizukuAliveFlow: StateFlow<Boolean>
|
||||
): () -> List<ActivityManager.RunningTaskInfo>? {
|
||||
val shizukuCanUsedFlow = combine(
|
||||
shizukuAliveFlow,
|
||||
shizukuGrantFlow,
|
||||
|
|
|
@ -38,15 +38,11 @@ class ControlVm @Inject constructor() : ViewModel() {
|
|||
}.stateIn(viewModelScope, SharingStarted.Eagerly, null)
|
||||
|
||||
val subsStatusFlow = combine(allRulesFlow, clickCountFlow) { allRules, clickCount ->
|
||||
(if (allRules.allGroupSize > 0) {
|
||||
if (allRules.appSize > 0) {
|
||||
"${allRules.appSize}应用/${allRules.allGroupSize}规则组"
|
||||
} else {
|
||||
"${allRules.allGroupSize}规则组"
|
||||
}
|
||||
allRules.numText + if (clickCount > 0) {
|
||||
"/${clickCount}点击"
|
||||
} else {
|
||||
"暂无规则"
|
||||
}) + if (clickCount > 0) "/${clickCount}点击" else ""
|
||||
""
|
||||
}
|
||||
}.stateIn(viewModelScope, SharingStarted.Eagerly, "")
|
||||
|
||||
}
|
|
@ -153,10 +153,10 @@ fun DebugPage() {
|
|||
val httpServerRunning by HttpService.isRunning.collectAsState()
|
||||
TextSwitch(
|
||||
name = "HTTP服务",
|
||||
desc = "开启HTTP服务, 以便在同一局域网下传递数据" + if (httpServerRunning) "\n${
|
||||
desc = if (httpServerRunning) "浏览器打开下面任意链接即可自动连接\n${
|
||||
Ext.getIpAddressInLocalNetwork()
|
||||
.map { host -> "http://${host}:${store.httpServerPort}" }.joinToString(",")
|
||||
}" else "",
|
||||
.map { host -> "http://${host}:${store.httpServerPort}" }.joinToString("\n")
|
||||
}" else "开启HTTP服务在同一局域网下连接调试工具",
|
||||
checked = httpServerRunning
|
||||
) {
|
||||
if (!checkOrRequestNotifPermission(context)) {
|
||||
|
|
|
@ -113,7 +113,7 @@ fun SettingsPage() {
|
|||
Divider()
|
||||
|
||||
TextSwitch(name = "前台悬浮窗",
|
||||
desc = "添加前台透明悬浮窗,关闭可能导致不点击/点击缓慢",
|
||||
desc = "添加透明悬浮窗,关闭可能导致不点击/点击缓慢",
|
||||
checked = store.enableAbFloatWindow,
|
||||
onCheckedChange = {
|
||||
updateStorage(
|
||||
|
|
|
@ -76,19 +76,9 @@ fun SubsItemCard(
|
|||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
}
|
||||
val apps = rawSubscription.apps
|
||||
val groupsSize = rawSubscription.allGroupSize
|
||||
val ruleNumText = if (groupsSize > 0) {
|
||||
if (apps.isNotEmpty()) {
|
||||
"${apps.size}应用/${groupsSize}规则组"
|
||||
} else {
|
||||
"${groupsSize}规则组"
|
||||
}
|
||||
} else {
|
||||
"暂无规则"
|
||||
}
|
||||
|
||||
Text(
|
||||
text = ruleNumText,
|
||||
text = rawSubscription.numText,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package li.songe.gkd.ui.theme
|
||||
|
||||
import android.os.Build
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
|
@ -8,10 +9,12 @@ import androidx.compose.material3.dynamicDarkColorScheme
|
|||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import li.songe.gkd.util.map
|
||||
import li.songe.gkd.util.storeFlow
|
||||
|
||||
|
@ -23,17 +26,23 @@ fun AppTheme(
|
|||
content: @Composable () -> Unit,
|
||||
) {
|
||||
// https://developer.android.com/jetpack/compose/designsystems/material3?hl=zh-cn
|
||||
val context = LocalContext.current as ComponentActivity
|
||||
val scope = rememberCoroutineScope()
|
||||
val enableDarkTheme by storeFlow.map(scope) { s -> s.enableDarkTheme }.collectAsState()
|
||||
val systemInDarkTheme = isSystemInDarkTheme()
|
||||
val darkTheme = enableDarkTheme ?: systemInDarkTheme
|
||||
val dynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
||||
val colorScheme = when {
|
||||
dynamicColor && darkTheme -> dynamicDarkColorScheme(LocalContext.current)
|
||||
dynamicColor && !darkTheme -> dynamicLightColorScheme(LocalContext.current)
|
||||
dynamicColor && darkTheme -> dynamicDarkColorScheme(context)
|
||||
dynamicColor && !darkTheme -> dynamicLightColorScheme(context)
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
}
|
||||
LaunchedEffect(darkTheme) {
|
||||
WindowInsetsControllerCompat(context.window, context.window.decorView).apply {
|
||||
isAppearanceLightStatusBars = !darkTheme
|
||||
}
|
||||
}
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme, content = content
|
||||
)
|
||||
|
|
|
@ -5,22 +5,10 @@ import com.blankj.utilcode.util.ToastUtils
|
|||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
|
||||
fun CoroutineScope.launchWhile(
|
||||
context: CoroutineContext = EmptyCoroutineContext,
|
||||
start: CoroutineStart = CoroutineStart.DEFAULT,
|
||||
block: suspend CoroutineScope.() -> Unit,
|
||||
) = launch(context, start) {
|
||||
while (isActive) {
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
fun CoroutineScope.launchTry(
|
||||
context: CoroutineContext = EmptyCoroutineContext,
|
||||
start: CoroutineStart = CoroutineStart.DEFAULT,
|
||||
|
|
|
@ -13,12 +13,10 @@ import kotlinx.serialization.encodeToString
|
|||
import li.songe.gkd.appScope
|
||||
import li.songe.gkd.data.AppRule
|
||||
import li.songe.gkd.data.CategoryConfig
|
||||
import li.songe.gkd.data.GlobalApp
|
||||
import li.songe.gkd.data.GlobalRule
|
||||
import li.songe.gkd.data.RawSubscription
|
||||
import li.songe.gkd.data.SubsConfig
|
||||
import li.songe.gkd.db.DbSet
|
||||
import li.songe.selector.Selector
|
||||
|
||||
val subsItemsFlow by lazy {
|
||||
DbSet.subsItemDao.query().stateIn(appScope, SharingStarted.Eagerly, emptyList())
|
||||
|
@ -77,28 +75,32 @@ fun getGroupRawEnable(
|
|||
} ?: rawGroup.enable ?: true
|
||||
}
|
||||
|
||||
private fun getFixActivityIds(
|
||||
appId: String,
|
||||
activityIds: List<String>?,
|
||||
): List<String> {
|
||||
activityIds ?: return emptyList()
|
||||
return activityIds.map { activityId ->
|
||||
if (activityId.startsWith('.')) { // .a.b.c -> com.x.y.x.a.b.c
|
||||
appId + activityId
|
||||
} else {
|
||||
activityId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class AllRules(
|
||||
val globalRules: List<GlobalRule> = emptyList(),
|
||||
val globalGroups: List<RawSubscription.RawGlobalGroup> = emptyList(),
|
||||
val appIdToRules: Map<String, List<AppRule>> = emptyMap(),
|
||||
val appIdToGroups: Map<String, List<RawSubscription.RawAppGroup>> = emptyMap(),
|
||||
) {
|
||||
val appSize = appIdToRules.keys.size
|
||||
val allGroupSize = globalGroups.size + appIdToGroups.values.sumOf { s -> s.size }
|
||||
private val appSize = appIdToRules.keys.size
|
||||
private val appGroupSize = appIdToGroups.values.sumOf { s -> s.size }
|
||||
|
||||
val numText = if (globalGroups.size + appGroupSize > 0) {
|
||||
if (globalGroups.isNotEmpty()) {
|
||||
"${globalGroups.size}全局" + if (appGroupSize > 0) {
|
||||
"/"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
} else {
|
||||
""
|
||||
} + if (appGroupSize > 0) {
|
||||
"${appSize}应用/${appGroupSize}规则组"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
} else {
|
||||
"暂无规则"
|
||||
}
|
||||
}
|
||||
|
||||
val allRulesFlow by lazy {
|
||||
|
@ -124,76 +126,16 @@ val allRulesFlow by lazy {
|
|||
?: g.enable ?: true)
|
||||
}.forEach { groupRaw ->
|
||||
globalGroups.add(groupRaw)
|
||||
val subRules = groupRaw.rules.mapIndexed { ruleIndex, ruleRaw ->
|
||||
val apps = mutableMapOf<String, GlobalApp>()
|
||||
(ruleRaw.apps ?: groupRaw.apps ?: emptyList()).forEach { a ->
|
||||
apps[a.id] = GlobalApp(
|
||||
id = a.id,
|
||||
enable = a.enable ?: true,
|
||||
activityIds = a.activityIds ?: emptyList(),
|
||||
excludeActivityIds = a.excludeActivityIds ?: emptyList()
|
||||
)
|
||||
}
|
||||
|
||||
val matchAnyApp = ruleRaw.matchAnyApp ?: groupRaw.matchAnyApp ?: false
|
||||
|
||||
val quickFind =
|
||||
ruleRaw.quickFind ?: groupRaw.quickFind ?: false
|
||||
val matchDelay =
|
||||
ruleRaw.matchDelay ?: groupRaw.matchDelay ?: 0
|
||||
val matchTime = ruleRaw.matchTime ?: groupRaw.matchTime
|
||||
val resetMatch =
|
||||
ruleRaw.resetMatch ?: groupRaw.resetMatch
|
||||
val actionDelay =
|
||||
ruleRaw.actionDelay ?: groupRaw.actionDelay ?: 0
|
||||
|
||||
val subRules = groupRaw.rules.map { ruleRaw ->
|
||||
GlobalRule(
|
||||
quickFind = quickFind,
|
||||
actionDelay = actionDelay,
|
||||
index = ruleIndex,
|
||||
matches = ruleRaw.matches.map { Selector.parse(it) },
|
||||
excludeMatches = (ruleRaw.excludeMatches ?: emptyList()).map {
|
||||
Selector.parse(
|
||||
it
|
||||
)
|
||||
},
|
||||
matchDelay = matchDelay,
|
||||
matchTime = matchTime,
|
||||
key = ruleRaw.key,
|
||||
preKeys = (ruleRaw.preKeys ?: emptyList()).toSet(),
|
||||
rule = ruleRaw,
|
||||
group = groupRaw,
|
||||
subsItem = subsItem,
|
||||
resetMatch = resetMatch,
|
||||
matchAnyApp = matchAnyApp,
|
||||
apps = apps,
|
||||
rawSubs = rawSubs,
|
||||
subsItem = subsItem,
|
||||
)
|
||||
}
|
||||
subRules.forEach { ruleConfig ->
|
||||
// 保留原始对象引用, 方便判断 lastTriggerRule 时直接使用 ===
|
||||
ruleConfig.preAppRules = subRules.filter { otherRule ->
|
||||
(otherRule.key != null) && ruleConfig.preKeys.contains(
|
||||
otherRule.key
|
||||
)
|
||||
}.toSet()
|
||||
// 共用次数
|
||||
val maxKey =
|
||||
ruleConfig.rule.actionMaximumKey ?: ruleConfig.group.actionMaximumKey
|
||||
if (maxKey != null) {
|
||||
val otherRule = subRules.find { r -> r.key == maxKey }
|
||||
if (otherRule != null) {
|
||||
ruleConfig.actionCount = otherRule.actionCount
|
||||
}
|
||||
}
|
||||
// 共用 cd
|
||||
val cdKey = ruleConfig.rule.actionCdKey ?: ruleConfig.group.actionCdKey
|
||||
if (cdKey != null) {
|
||||
val otherRule = subRules.find { r -> r.key == cdKey }
|
||||
if (otherRule != null) {
|
||||
ruleConfig.actionTriggerTime = otherRule.actionTriggerTime
|
||||
}
|
||||
}
|
||||
subRules.forEach { r ->
|
||||
r.groupRules = subRules
|
||||
}
|
||||
globalRules.addAll(subRules)
|
||||
}
|
||||
|
@ -216,79 +158,17 @@ val allRulesFlow by lazy {
|
|||
)
|
||||
}.forEach { groupRaw ->
|
||||
subAppGroups.add(groupRaw)
|
||||
val subRules = groupRaw.rules.mapIndexed { ruleIndex, ruleRaw ->
|
||||
val activityIds =
|
||||
getFixActivityIds(
|
||||
appRaw.id,
|
||||
ruleRaw.activityIds ?: groupRaw.activityIds
|
||||
)
|
||||
|
||||
val excludeActivityIds = getFixActivityIds(
|
||||
appRaw.id,
|
||||
ruleRaw.excludeActivityIds ?: groupRaw.excludeActivityIds,
|
||||
)
|
||||
|
||||
val quickFind =
|
||||
ruleRaw.quickFind ?: groupRaw.quickFind ?: false
|
||||
|
||||
val matchDelay =
|
||||
ruleRaw.matchDelay ?: groupRaw.matchDelay ?: 0
|
||||
val matchTime = ruleRaw.matchTime ?: groupRaw.matchTime
|
||||
val resetMatch =
|
||||
ruleRaw.resetMatch ?: groupRaw.resetMatch
|
||||
val actionDelay =
|
||||
ruleRaw.actionDelay ?: groupRaw.actionDelay ?: 0
|
||||
|
||||
val subRules = groupRaw.rules.map { ruleRaw ->
|
||||
AppRule(
|
||||
quickFind = quickFind,
|
||||
actionDelay = actionDelay,
|
||||
index = ruleIndex,
|
||||
matches = ruleRaw.matches.map { Selector.parse(it) },
|
||||
excludeMatches = (ruleRaw.excludeMatches ?: emptyList()).map {
|
||||
Selector.parse(
|
||||
it
|
||||
)
|
||||
},
|
||||
matchDelay = matchDelay,
|
||||
matchTime = matchTime,
|
||||
appId = appRaw.id,
|
||||
activityIds = activityIds,
|
||||
excludeActivityIds = excludeActivityIds,
|
||||
key = ruleRaw.key,
|
||||
preKeys = (ruleRaw.preKeys ?: emptyList()).toSet(),
|
||||
rule = ruleRaw,
|
||||
group = groupRaw,
|
||||
app = appRaw,
|
||||
subsItem = subsItem,
|
||||
resetMatch = resetMatch,
|
||||
rawSubs = rawSubs,
|
||||
subsItem = subsItem,
|
||||
)
|
||||
|
||||
}
|
||||
subRules.forEach { ruleConfig ->
|
||||
// 保留原始对象引用, 方便判断 lastTriggerRule 时直接使用 ===
|
||||
ruleConfig.preAppRules = subRules.filter { otherRule ->
|
||||
(otherRule.key != null) && ruleConfig.preKeys.contains(
|
||||
otherRule.key
|
||||
)
|
||||
}.toSet()
|
||||
// 共用次数
|
||||
val maxKey =
|
||||
ruleConfig.rule.actionMaximumKey ?: ruleConfig.group.actionMaximumKey
|
||||
if (maxKey != null) {
|
||||
val otherRule = subRules.find { r -> r.key == maxKey }
|
||||
if (otherRule != null) {
|
||||
ruleConfig.actionCount = otherRule.actionCount
|
||||
}
|
||||
}
|
||||
// 共用 cd
|
||||
val cdKey = ruleConfig.rule.actionCdKey ?: ruleConfig.group.actionCdKey
|
||||
if (cdKey != null) {
|
||||
val otherRule = subRules.find { r -> r.key == cdKey }
|
||||
if (otherRule != null) {
|
||||
ruleConfig.actionTriggerTime = otherRule.actionTriggerTime
|
||||
}
|
||||
}
|
||||
subRules.forEach { r ->
|
||||
r.groupRules = subRules
|
||||
}
|
||||
if (subRules.isNotEmpty()) {
|
||||
val rules = appRules[appRaw.id] ?: mutableListOf()
|
||||
|
|
Loading…
Reference in New Issue
Block a user