mirror of
https://github.com/gkd-kit/gkd.git
synced 2024-11-16 03:32:38 +08:00
perf: check type
This commit is contained in:
parent
4758448739
commit
ad394c5a84
|
@ -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()
|
||||
|
|
|
@ -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,17 +17,42 @@ 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) {
|
||||
// 在某些机型由于未知原因创建失败
|
||||
// 在此保证每次重新打开APP都能重新检测创建
|
||||
initFolder()
|
||||
|
||||
// 每次打开页面更新记录桌面 appId
|
||||
updateLauncherAppId()
|
||||
|
||||
val localSubsItem = SubsItem(
|
||||
id = -2, order = -2, mtime = System.currentTimeMillis()
|
||||
)
|
||||
if (!DbSet.subsItemDao.query().first().any { s -> s.id == localSubsItem.id }) {
|
||||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModelScope.launchTry(Dispatchers.IO) {
|
||||
// 每次进入删除缓存
|
||||
|
@ -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) {
|
||||
|
|
|
@ -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<Long>?,
|
||||
) : 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",
|
||||
|
|
|
@ -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<AccessibilityNodeInfo> = {
|
|||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <T : Any> match(node: T, transform: MultiplatformTransform<T>): T? {
|
||||
return selector.match(node, transform.transform)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T : Any> matchTrack(node: T, transform: MultiplatformTransform<T>): Array<T>? {
|
||||
return selector.matchTracks(node, transform.transform)?.toTypedArray<Any?>() as Array<T>?
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -6,12 +6,12 @@ data class BinaryExpression(
|
|||
val name: String,
|
||||
val operator: CompareOperator,
|
||||
val value: PrimitiveValue
|
||||
) :
|
||||
Expression() {
|
||||
) : Expression() {
|
||||
override fun <T> match(node: T, transform: Transform<T>) =
|
||||
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}"
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -3,7 +3,7 @@ package li.songe.selector.data
|
|||
import li.songe.selector.Transform
|
||||
|
||||
sealed class Expression {
|
||||
abstract fun <T> match(node: T, transform: Transform<T>): Boolean
|
||||
internal abstract fun <T> match(node: T, transform: Transform<T>): Boolean
|
||||
|
||||
abstract val binaryExpressions: List<BinaryExpression>
|
||||
abstract val binaryExpressions: Array<BinaryExpression>
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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, type) {
|
||||
companion object {
|
||||
const val type = "string"
|
||||
}
|
||||
|
||||
data class StringValue(override val value: String) : PrimitiveValue(value) {
|
||||
override fun toString(): String {
|
||||
val wrapChar = '"'
|
||||
val sb = StringBuilder(value.length + 2)
|
||||
|
|
|
@ -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 ""
|
||||
|
|
Loading…
Reference in New Issue
Block a user