From ad394c5a84e8f5816cb8b913ebb37274bf0a6cd5 Mon Sep 17 00:00:00 2001 From: lisonge Date: Mon, 26 Feb 2024 23:04:44 +0800 Subject: [PATCH] perf: check type --- .../main/kotlin/li/songe/gkd/MainActivity.kt | 5 +- .../main/kotlin/li/songe/gkd/MainViewModel.kt | 49 ++++++++--- .../li/songe/gkd/data/RawSubscription.kt | 85 +++++++------------ .../main/kotlin/li/songe/gkd/service/AbExt.kt | 53 ++++++------ .../kotlin/li/songe/gkd/util/SubsState.kt | 8 -- .../songe/selector/MultiplatformSelector.kt | 7 +- .../kotlin/li/songe/selector/Selector.kt | 18 +--- .../songe/selector/data/BinaryExpression.kt | 6 +- .../li/songe/selector/data/CompareOperator.kt | 2 +- .../li/songe/selector/data/Expression.kt | 4 +- .../songe/selector/data/LogicalExpression.kt | 3 +- .../li/songe/selector/data/PrimitiveValue.kt | 22 +++-- .../li/songe/selector/data/PropertySegment.kt | 3 +- 13 files changed, 129 insertions(+), 136 deletions(-) diff --git a/app/src/main/kotlin/li/songe/gkd/MainActivity.kt b/app/src/main/kotlin/li/songe/gkd/MainActivity.kt index c84e3a7..b85b979 100644 --- a/app/src/main/kotlin/li/songe/gkd/MainActivity.kt +++ b/app/src/main/kotlin/li/songe/gkd/MainActivity.kt @@ -16,7 +16,6 @@ import kotlinx.coroutines.launch import li.songe.gkd.composition.CompositionActivity import li.songe.gkd.composition.CompositionExt.useLifeCycleLog import li.songe.gkd.service.ManageService -import li.songe.gkd.service.updateLauncherAppId import li.songe.gkd.ui.NavGraphs import li.songe.gkd.ui.component.ConfirmDialog import li.songe.gkd.ui.theme.AppTheme @@ -47,10 +46,8 @@ class MainActivity : CompositionActivity({ } } - // 每次打开页面更新记录桌面 appId - updateLauncherAppId() - ManageService.autoStart(this) + mainVm setContent { val navController = rememberNavController() diff --git a/app/src/main/kotlin/li/songe/gkd/MainViewModel.kt b/app/src/main/kotlin/li/songe/gkd/MainViewModel.kt index 5499c17..e564fba 100644 --- a/app/src/main/kotlin/li/songe/gkd/MainViewModel.kt +++ b/app/src/main/kotlin/li/songe/gkd/MainViewModel.kt @@ -3,10 +3,12 @@ package li.songe.gkd import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.launch +import li.songe.gkd.data.RawSubscription import li.songe.gkd.data.SubsItem import li.songe.gkd.db.DbSet +import li.songe.gkd.service.updateLauncherAppId import li.songe.gkd.util.authActionFlow import li.songe.gkd.util.checkUpdate import li.songe.gkd.util.initFolder @@ -15,15 +17,40 @@ import li.songe.gkd.util.logZipDir import li.songe.gkd.util.newVersionApkDir import li.songe.gkd.util.snapshotZipDir import li.songe.gkd.util.storeFlow +import li.songe.gkd.util.subsIdToRawFlow +import li.songe.gkd.util.subsItemsFlow +import li.songe.gkd.util.updateSubscription class MainViewModel : ViewModel() { init { - appScope.launchTry(Dispatchers.IO) { - val localSubsItem = SubsItem( - id = -2, order = -2, mtime = System.currentTimeMillis() - ) - if (!DbSet.subsItemDao.query().first().any { s -> s.id == localSubsItem.id }) { - DbSet.subsItemDao.insert(localSubsItem) + // 在某些机型由于未知原因创建失败 + // 在此保证每次重新打开APP都能重新检测创建 + initFolder() + + // 每次打开页面更新记录桌面 appId + updateLauncherAppId() + + val localSubsItem = SubsItem( + id = -2, order = -2, mtime = System.currentTimeMillis() + ) + viewModelScope.launchTry(Dispatchers.IO) { + subsItemsFlow.debounce(1000).collect { subsItems -> + if (!subsItems.any { s -> s.id == localSubsItem.id }) { + DbSet.subsItemDao.insert(localSubsItem) + } + } + } + viewModelScope.launchTry(Dispatchers.IO) { + subsIdToRawFlow.debounce(1000).collect { subsIdToRaw -> + if (!subsIdToRaw.containsKey(localSubsItem.id)) { + updateSubscription( + RawSubscription( + id = localSubsItem.id, + name = "本地订阅", + version = 0 + ) + ) + } } } @@ -40,14 +67,8 @@ class MainViewModel : ViewModel() { } } - viewModelScope.launchTry(Dispatchers.IO) { - // 在某些机型由于未知原因创建失败 - // 在此保证每次重新打开APP都能重新检测创建 - initFolder() - } - if (storeFlow.value.autoCheckAppUpdate) { - appScope.launch { + viewModelScope.launch { try { checkUpdate() } catch (e: Exception) { diff --git a/app/src/main/kotlin/li/songe/gkd/data/RawSubscription.kt b/app/src/main/kotlin/li/songe/gkd/data/RawSubscription.kt index 8a316dc..9c0d274 100644 --- a/app/src/main/kotlin/li/songe/gkd/data/RawSubscription.kt +++ b/app/src/main/kotlin/li/songe/gkd/data/RawSubscription.kt @@ -254,34 +254,7 @@ data class RawSubscription( (apps ?: emptyList()).associate { a -> a.id to (a.enable ?: true) } } - val allSelectorStrings by lazy { - rules.map { r -> r.matches + (r.excludeMatches ?: emptyList()) }.flatten() - - } - - val allSelector by lazy { - allSelectorStrings.map { s -> Selector.parseOrNull(s) } - } - - val unknownPropertyNames by lazy { - allSelector.filterNotNull().flatMap { r -> r.propertyNames.toList() }.distinct() - .filterNot { n -> allowPropertyNames.contains(n) } - } - - val errorDesc by lazy { - allSelector.forEachIndexed { i, s -> - if (s == null) { - return@lazy "非法选择器:${allSelectorStrings[i]}" - } - } - unknownPropertyNames.forEach { n -> - return@lazy "非法属性名:${n}" - } - if (rules.any { r -> r.position?.isValid == false }) { - return@lazy "非法位置" - } - null - } + val errorDesc by lazy { getErrorDesc() } val valid by lazy { errorDesc == null } @@ -349,31 +322,7 @@ data class RawSubscription( override val excludeVersionCodes: List?, ) : RawGroupProps, RawAppRuleProps { - val allSelectorStrings by lazy { - rules.map { r -> (r.matches ?: emptyList()) + (r.excludeMatches ?: emptyList()) } - .flatten() - } - - val allSelector by lazy { - allSelectorStrings.map { s -> Selector.parseOrNull(s) } - } - - val unknownPropertyNames by lazy { - allSelector.filterNotNull().flatMap { r -> r.propertyNames.toList() }.distinct() - .filterNot { n -> allowPropertyNames.contains(n) } - } - - val errorDesc by lazy { - allSelector.forEachIndexed { i, s -> - if (s == null) { - return@lazy "非法选择器:${allSelectorStrings[i]}" - } - } - unknownPropertyNames.forEach { n -> - return@lazy "非法属性名:${n}" - } - null - } + val errorDesc by lazy { getErrorDesc() } val valid by lazy { errorDesc == null } @@ -419,6 +368,36 @@ data class RawSubscription( companion object { + private fun RawGroupProps.getErrorDesc(): String? { + val allSelectorStrings = + rules.map { r -> (r.matches ?: emptyList()) + (r.excludeMatches ?: emptyList()) } + .flatten() + + val allSelector = allSelectorStrings.map { s -> Selector.parseOrNull(s) } + + allSelector.forEachIndexed { i, s -> + if (s == null) { + return "非法选择器:${allSelectorStrings[i]}" + } + } + allSelector.forEach { s -> + s?.nameToTypeList?.forEach { (name, type) -> + if (!allowPropertyNames.contains(name)) { + return "非法属性名:${name}" + } + if (type != "null" && allowPropertyNames[name] != type) { + return "非法类型:${name}=$type" + } + } + } + rules.forEach { r -> + if (r.position?.isValid == false) { + return "非法位置:${r.position}" + } + } + return null + } + private val expVars = arrayOf( "left", "top", diff --git a/app/src/main/kotlin/li/songe/gkd/service/AbExt.kt b/app/src/main/kotlin/li/songe/gkd/service/AbExt.kt index 613ed95..d70e014 100644 --- a/app/src/main/kotlin/li/songe/gkd/service/AbExt.kt +++ b/app/src/main/kotlin/li/songe/gkd/service/AbExt.kt @@ -5,6 +5,7 @@ import android.graphics.Rect import android.view.accessibility.AccessibilityNodeInfo import li.songe.selector.Selector import li.songe.selector.Transform +import li.songe.selector.data.PrimitiveValue val AccessibilityService.safeActiveWindow: AccessibilityNodeInfo? get() = try { @@ -128,35 +129,37 @@ val getChildren: (AccessibilityNodeInfo) -> Sequence = { } } -val allowPropertyNames = setOf( - "id", - "vid", +val allowPropertyNames by lazy { + mapOf( + "id" to PrimitiveValue.StringValue.type, + "vid" to PrimitiveValue.StringValue.type, - "name", - "text", - "text.length", - "desc", - "desc.length", + "name" to PrimitiveValue.StringValue.type, + "text" to PrimitiveValue.StringValue.type, + "text.length" to PrimitiveValue.IntValue.type, + "desc" to PrimitiveValue.StringValue.type, + "desc.length" to PrimitiveValue.IntValue.type, - "clickable", - "focusable", - "checkable", - "checked", - "editable", - "longClickable", - "visibleToUser", + "clickable" to PrimitiveValue.BooleanValue.type, + "focusable" to PrimitiveValue.BooleanValue.type, + "checkable" to PrimitiveValue.BooleanValue.type, + "checked" to PrimitiveValue.BooleanValue.type, + "editable" to PrimitiveValue.BooleanValue.type, + "longClickable" to PrimitiveValue.BooleanValue.type, + "visibleToUser" to PrimitiveValue.BooleanValue.type, - "left", - "top", - "right", - "bottom", - "width", - "height", + "left" to PrimitiveValue.IntValue.type, + "top" to PrimitiveValue.IntValue.type, + "right" to PrimitiveValue.IntValue.type, + "bottom" to PrimitiveValue.IntValue.type, + "width" to PrimitiveValue.IntValue.type, + "height" to PrimitiveValue.IntValue.type, - "index", - "depth", - "childCount" -) + "index" to PrimitiveValue.IntValue.type, + "depth" to PrimitiveValue.IntValue.type, + "childCount" to PrimitiveValue.IntValue.type, + ) +} private val getAttr: (AccessibilityNodeInfo, String) -> Any? = { node, name -> when (name) { diff --git a/app/src/main/kotlin/li/songe/gkd/util/SubsState.kt b/app/src/main/kotlin/li/songe/gkd/util/SubsState.kt index 82247a6..7118189 100644 --- a/app/src/main/kotlin/li/songe/gkd/util/SubsState.kt +++ b/app/src/main/kotlin/li/songe/gkd/util/SubsState.kt @@ -251,14 +251,6 @@ fun initSubsState() { subscriptions.forEach { s -> newMap[s.id] = s } - if (newMap[-2] == null) { - newMap[-2] = RawSubscription( - id = -2, - name = "本地订阅", - version = 0, - author = "gkd", - ) - } subsIdToRawFlow.value = newMap.toImmutableMap() } } diff --git a/selector/src/commonMain/kotlin/li/songe/selector/MultiplatformSelector.kt b/selector/src/commonMain/kotlin/li/songe/selector/MultiplatformSelector.kt index cf84e33..ad5623d 100644 --- a/selector/src/commonMain/kotlin/li/songe/selector/MultiplatformSelector.kt +++ b/selector/src/commonMain/kotlin/li/songe/selector/MultiplatformSelector.kt @@ -3,7 +3,7 @@ package li.songe.selector import kotlin.js.JsExport @JsExport -@Suppress("UNUSED") +@Suppress("UNUSED", "UNCHECKED_CAST") class MultiplatformSelector private constructor( internal val selector: Selector, ) { @@ -17,15 +17,12 @@ class MultiplatformSelector private constructor( val qfTextValue = selector.qfTextValue val canQf = selector.canQf val isMatchRoot = selector.isMatchRoot - fun checkType(getType: (String) -> String): Boolean { - return selector.checkType(getType) - } + val nameToTypeList = selector.nameToTypeList fun match(node: T, transform: MultiplatformTransform): T? { return selector.match(node, transform.transform) } - @Suppress("UNCHECKED_CAST") fun matchTrack(node: T, transform: MultiplatformTransform): Array? { return selector.matchTracks(node, transform.transform)?.toTypedArray() as Array? } diff --git a/selector/src/commonMain/kotlin/li/songe/selector/Selector.kt b/selector/src/commonMain/kotlin/li/songe/selector/Selector.kt index a95c75d..b6379e8 100644 --- a/selector/src/commonMain/kotlin/li/songe/selector/Selector.kt +++ b/selector/src/commonMain/kotlin/li/songe/selector/Selector.kt @@ -99,21 +99,11 @@ class Selector internal constructor(private val propertyWrapper: PropertyWrapper binaryExpressions.map { e -> e.name }.distinct().toTypedArray() } - fun checkType(getType: (String) -> String): Boolean { - binaryExpressions.forEach { e -> - if (e.value.value != null) { - if (!(when (getType(e.name)) { - "boolean" -> e.value is PrimitiveValue.BooleanValue - "int" -> e.value is PrimitiveValue.IntValue - "string" -> e.value is PrimitiveValue.StringValue - else -> false - }) - ) return false - } - } - return true - } + // [name,type][] + val nameToTypeList = run { + binaryExpressions.map { e -> arrayOf(e.name, e.value.type) }.distinct().toTypedArray() + } val canCacheIndex = connectKeys.contains(ConnectOperator.BeforeBrother.key) || connectKeys.contains( diff --git a/selector/src/commonMain/kotlin/li/songe/selector/data/BinaryExpression.kt b/selector/src/commonMain/kotlin/li/songe/selector/data/BinaryExpression.kt index 9a969ce..b1c2858 100644 --- a/selector/src/commonMain/kotlin/li/songe/selector/data/BinaryExpression.kt +++ b/selector/src/commonMain/kotlin/li/songe/selector/data/BinaryExpression.kt @@ -6,12 +6,12 @@ data class BinaryExpression( val name: String, val operator: CompareOperator, val value: PrimitiveValue -) : - Expression() { +) : Expression() { override fun match(node: T, transform: Transform) = operator.compare(transform.getAttr(node, name), value.value) - override val binaryExpressions = listOf(this) + override val binaryExpressions + get() = arrayOf(this) override fun toString() = "${name}${operator.key}${value}" } diff --git a/selector/src/commonMain/kotlin/li/songe/selector/data/CompareOperator.kt b/selector/src/commonMain/kotlin/li/songe/selector/data/CompareOperator.kt index 4d917af..41f0be3 100644 --- a/selector/src/commonMain/kotlin/li/songe/selector/data/CompareOperator.kt +++ b/selector/src/commonMain/kotlin/li/songe/selector/data/CompareOperator.kt @@ -11,7 +11,7 @@ sealed class CompareOperator(val key: String) { Equal, NotEqual, Start, NotStart, Include, NotInclude, End, NotEnd, Less, LessEqual, More, MoreEqual - ).sortedBy { -it.key.length } + ).sortedBy { -it.key.length }.toTypedArray() } // example diff --git a/selector/src/commonMain/kotlin/li/songe/selector/data/Expression.kt b/selector/src/commonMain/kotlin/li/songe/selector/data/Expression.kt index 2562eb6..56b5e49 100644 --- a/selector/src/commonMain/kotlin/li/songe/selector/data/Expression.kt +++ b/selector/src/commonMain/kotlin/li/songe/selector/data/Expression.kt @@ -3,7 +3,7 @@ package li.songe.selector.data import li.songe.selector.Transform sealed class Expression { - abstract fun match(node: T, transform: Transform): Boolean + internal abstract fun match(node: T, transform: Transform): Boolean - abstract val binaryExpressions: List + abstract val binaryExpressions: Array } diff --git a/selector/src/commonMain/kotlin/li/songe/selector/data/LogicalExpression.kt b/selector/src/commonMain/kotlin/li/songe/selector/data/LogicalExpression.kt index e2cd3fd..b95fe60 100644 --- a/selector/src/commonMain/kotlin/li/songe/selector/data/LogicalExpression.kt +++ b/selector/src/commonMain/kotlin/li/songe/selector/data/LogicalExpression.kt @@ -11,7 +11,8 @@ data class LogicalExpression( return operator.compare(node, transform, left, right) } - override val binaryExpressions = left.binaryExpressions + right.binaryExpressions + override val binaryExpressions + get() = left.binaryExpressions + right.binaryExpressions override fun toString(): String { val leftStr = if (left is LogicalExpression && left.operator != operator) { diff --git a/selector/src/commonMain/kotlin/li/songe/selector/data/PrimitiveValue.kt b/selector/src/commonMain/kotlin/li/songe/selector/data/PrimitiveValue.kt index f0c3b74..4f3cc2a 100644 --- a/selector/src/commonMain/kotlin/li/songe/selector/data/PrimitiveValue.kt +++ b/selector/src/commonMain/kotlin/li/songe/selector/data/PrimitiveValue.kt @@ -1,19 +1,31 @@ package li.songe.selector.data -sealed class PrimitiveValue(open val value: Any?) { - data object NullValue : PrimitiveValue(null) { +sealed class PrimitiveValue(open val value: Any?, open val type: String) { + data object NullValue : PrimitiveValue(null, "null") { override fun toString() = "null" } - data class BooleanValue(override val value: Boolean) : PrimitiveValue(value) { + data class BooleanValue(override val value: Boolean) : PrimitiveValue(value, type) { override fun toString() = value.toString() + + companion object { + const val type = "boolean" + } } - data class IntValue(override val value: Int) : PrimitiveValue(value) { + data class IntValue(override val value: Int) : PrimitiveValue(value, type) { override fun toString() = value.toString() + + companion object { + const val type = "int" + } } - data class StringValue(override val value: String) : PrimitiveValue(value) { + data class StringValue(override val value: String) : PrimitiveValue(value, type) { + companion object { + const val type = "string" + } + override fun toString(): String { val wrapChar = '"' val sb = StringBuilder(value.length + 2) diff --git a/selector/src/commonMain/kotlin/li/songe/selector/data/PropertySegment.kt b/selector/src/commonMain/kotlin/li/songe/selector/data/PropertySegment.kt index 87ad1e1..f8e5b62 100644 --- a/selector/src/commonMain/kotlin/li/songe/selector/data/PropertySegment.kt +++ b/selector/src/commonMain/kotlin/li/songe/selector/data/PropertySegment.kt @@ -13,7 +13,8 @@ data class PropertySegment( ) { private val matchAnyName = name.isBlank() || name == "*" - val binaryExpressions = expressions.map { e -> e.binaryExpressions }.flatten() + val binaryExpressions + get() = expressions.flatMap { it.binaryExpressions.toList() }.toTypedArray() override fun toString(): String { val matchTag = if (tracked) "@" else ""