diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 855eb02..8d08682 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,10 +4,10 @@ plugins { id("kotlin-parcelize") id("kotlin-kapt") id("org.jetbrains.kotlin.plugin.serialization") - id("com.google.devtools.ksp") version "1.7.10-1.0.6" + id("com.google.devtools.ksp") version "1.7.20-1.0.7" } -val composeVersion = "1.3.0-beta01" +val composeVersion = "1.3.0" android { compileSdk = 33 buildToolsVersion = "33.0.0" @@ -87,7 +87,9 @@ android { compose = true } composeOptions { - kotlinCompilerExtensionVersion = composeVersion +// compose 编译器的版本, 需要注意它与 compose 的版本不一致 +// https://mvnrepository.com/artifact/androidx.compose.compiler/compiler + kotlinCompilerExtensionVersion = "1.3.2" } packagingOptions { resources { @@ -103,7 +105,7 @@ dependencies { // normal implementation("androidx.appcompat:appcompat:1.5.1") - implementation("com.google.android.material:material:1.6.1") + implementation("com.google.android.material:material:1.7.0") // ktx implementation("androidx.core:core-ktx:1.9.0") @@ -115,7 +117,7 @@ dependencies { implementation("androidx.compose.ui:ui-tooling-preview:$composeVersion") debugImplementation("androidx.compose.ui:ui-tooling:$composeVersion") androidTestImplementation("androidx.compose.ui:ui-test-junit4:$composeVersion") - implementation("androidx.activity:activity-compose:1.6.0") + implementation("androidx.activity:activity-compose:1.6.1") // test testImplementation("junit:junit:4.13.2") @@ -151,10 +153,6 @@ dependencies { // https://github.com/Tencent/MMKV/blob/master/README_CN.md implementation("com.tencent:mmkv:1.2.13") -// https://juejin.cn/post/6969841959082917901 - implementation("com.squareup.moshi:moshi-kotlin:1.13.0") - kapt("com.squareup.moshi:moshi-kotlin-codegen:1.13.0") - // ktor implementation("io.ktor:ktor-server-core:2.1.0") implementation("io.ktor:ktor-server-netty:2.1.0") @@ -186,6 +184,12 @@ dependencies { ksp(project(":room_processor")) - implementation(project(mapOf("path" to ":node_selector"))) + implementation(project(mapOf("path" to ":selector"))) + +// https://github.com/falkreon/Jankson + implementation("blue.endless:jankson:1.2.1") + +// https://github.com/Kotlin/kotlinx.collections.immutable + implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5") } \ No newline at end of file diff --git a/app/schemas/li.songe.gkd.db.AppDatabase/1.json b/app/schemas/li.songe.gkd.db.AppDatabase/1.json index b918eb5..3dc0674 100644 --- a/app/schemas/li.songe.gkd.db.AppDatabase/1.json +++ b/app/schemas/li.songe.gkd.db.AppDatabase/1.json @@ -2,11 +2,11 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "f3082a1bb735b6f7f163e448585542ce", + "identityHash": "8ca34ac65f8956691667eb1cbca0bfb3", "entities": [ { "tableName": "subs_item", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `ctime` INTEGER NOT NULL, `mtime` INTEGER NOT NULL, `url` TEXT NOT NULL, `file_path` TEXT NOT NULL, `comment` TEXT NOT NULL, `description` TEXT NOT NULL, `enable` INTEGER NOT NULL)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `ctime` INTEGER NOT NULL, `mtime` INTEGER NOT NULL, `update_url` TEXT NOT NULL, `file_path` TEXT NOT NULL, `comment` TEXT NOT NULL, `enable` INTEGER NOT NULL)", "fields": [ { "fieldPath": "id", @@ -27,8 +27,8 @@ "notNull": true }, { - "fieldPath": "url", - "columnName": "url", + "fieldPath": "updateUrl", + "columnName": "update_url", "affinity": "TEXT", "notNull": true }, @@ -44,12 +44,6 @@ "affinity": "TEXT", "notNull": true }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, { "fieldPath": "enable", "columnName": "enable", @@ -65,20 +59,20 @@ }, "indices": [ { - "name": "index_subs_item_url", + "name": "index_subs_item_update_url", "unique": true, "columnNames": [ - "url" + "update_url" ], "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_subs_item_url` ON `${TABLE_NAME}` (`url`)" + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_subs_item_update_url` ON `${TABLE_NAME}` (`update_url`)" } ], "foreignKeys": [] }, { "tableName": "subs_config", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `ctime` INTEGER NOT NULL, `mtime` INTEGER NOT NULL, `type` INTEGER NOT NULL, `enable` INTEGER NOT NULL, `subs_item_id` INTEGER NOT NULL, `package_name` TEXT NOT NULL, `group_key` INTEGER NOT NULL, `rule_key` INTEGER NOT NULL)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `ctime` INTEGER NOT NULL, `mtime` INTEGER NOT NULL, `type` INTEGER NOT NULL, `enable` INTEGER NOT NULL, `subs_item_id` INTEGER NOT NULL, `app_id` TEXT NOT NULL, `group_key` INTEGER NOT NULL, `rule_key` INTEGER NOT NULL)", "fields": [ { "fieldPath": "id", @@ -117,8 +111,8 @@ "notNull": true }, { - "fieldPath": "packageName", - "columnName": "package_name", + "fieldPath": "appId", + "columnName": "app_id", "affinity": "TEXT", "notNull": true }, @@ -148,7 +142,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f3082a1bb735b6f7f163e448585542ce')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8ca34ac65f8956691667eb1cbca0bfb3')" ] } } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 012086d..a591711 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -51,7 +51,7 @@ @@ -67,9 +67,9 @@ android:exported="false" android:foregroundServiceType="mediaProjection" /> - + >>() fun load(subscription: Subscription) { subscription.appList.forEach { - if (!data.containsKey(it.packageName)) { - data[it.packageName] = mutableMapOf() + if (!data.containsKey(it.id)) { + data[it.id] = mutableMapOf() } - val m2 = data[it.packageName]!! + val m2 = data[it.id]!! it.groupList.forEach { group -> group.ruleList.forEach loop@{ rule -> - val name = (rule.className ?: group.className) + val name = (rule.activityId ?: group.activityId) if (!m2.containsKey(name)) { m2[name] = mutableListOf() } val list = m2[name]!! - list.add(GkdSelector.gkdSelectorParser(rule.selector)) + list.add(GkdSelector.gkdSelectorParser(rule.match)) } } } diff --git a/app/src/main/java/li/songe/gkd/data/Subscription.kt b/app/src/main/java/li/songe/gkd/data/Subscription.kt index 5315c1e..48dd59d 100644 --- a/app/src/main/java/li/songe/gkd/data/Subscription.kt +++ b/app/src/main/java/li/songe/gkd/data/Subscription.kt @@ -8,84 +8,55 @@ import li.songe.gkd.util.Singleton @Serializable -//@JsonClass(generateAdapter = true) data class Subscription( - @SerialName("appList") - //@Json(name = "appList") - val appList: List, - - @SerialName("author") - //@Json(name = "author") - val author: String?=null, - - @SerialName("description") - //@Json(name = "description") - val description: String = "", - - @SerialName("version") - //@Json(name = "version") - val version: Int, - - @SerialName("url") - //@Json(name = "url") - var url: String = "" + @SerialName("name") val name: String? = null, + @SerialName("version") val version: Int, + @SerialName("author") val author: String? = null, + @SerialName("updateUrl") val updateUrl: String? = null, + @SerialName("appList") val appList: List, ) { - companion object { fun parse(source: String) = Singleton.json.decodeFromString(source) + fun parse5(source: String): Subscription { + return Singleton.json.decodeFromString( + Singleton.json5.load(source).toJson() + ) + } fun stringify(source: Subscription) = Singleton.json.encodeToString(source) } @Serializable - //@JsonClass(generateAdapter = true) data class App( - @SerialName("groupList") - //@Json(name = "groupList") - val groupList: List, - - @SerialName("packageName") - //@Json(name = "packageName") - val packageName: String + @SerialName("id") val id: String, + @SerialName("groupList") val groupList: List, ) @Serializable - //@JsonClass(generateAdapter = true) data class Group( - @SerialName("className") - //@Json(name = "className") - val className: String, - - @SerialName("description") - //@Json(name = "description") - val description: String?=null, - - @SerialName("key") - //@Json(name = "key") - val key: Int?=null, - - @SerialName("ruleList") - //@Json(name = "ruleList") - val ruleList: List + @SerialName("key") val key: Int? = null, + @SerialName("name") val name: String? = null, + @SerialName("activityId") val activityId: String, + @SerialName("cd") val cd: Int? = null, + @SerialName("ruleList") val ruleList: List, ) @Serializable - //@JsonClass(generateAdapter = true) data class Rule( - @SerialName("className") - //@Json(name = "className") - val className: String?=null, + @SerialName("key") val key: Int? = null, + @SerialName("name") val name: String? = null, + @SerialName("activityId") val activityId: String? = null, + @SerialName("cd") val cd: Int? = null, + @SerialName("prompt") val prompt: String? = null, + @SerialName("match") val match: String, + @SerialName("action") val action: Action? = null, + @SerialName("ordered") val ordered: Boolean = false, + ) - @SerialName("description") - //@Json(name = "description") - val description: String?=null, - - @SerialName("selector") - //@Json(name = "selector") - val selector: String, - - @SerialName("prompt") - //@Json(name = "prompt") - val prompt: String?=null + @Serializable + data class Action( + @SerialName("type") val type: String, + @SerialName("target") val target: String, + @SerialName("position") val position: String? = null, ) } \ No newline at end of file diff --git a/app/src/main/java/li/songe/gkd/db/AppDatabase.kt b/app/src/main/java/li/songe/gkd/db/AppDatabase.kt index d37fb89..c4d34b0 100644 --- a/app/src/main/java/li/songe/gkd/db/AppDatabase.kt +++ b/app/src/main/java/li/songe/gkd/db/AppDatabase.kt @@ -1,12 +1,12 @@ package li.songe.gkd.db -import androidx.room.AutoMigration import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase import com.blankj.utilcode.util.PathUtils import li.songe.gkd.App -import li.songe.gkd.db.table.* +import li.songe.gkd.db.table.SubsConfig +import li.songe.gkd.db.table.SubsItem import java.io.File @Database( @@ -20,9 +20,6 @@ import java.io.File ) abstract class AppDatabase : RoomDatabase() { abstract fun subsItemRoomDao(): SubsItem.RoomDao -// abstract fun subsAppItemRoomDao(): SubsAppItem.RoomDao -// abstract fun subsGroupItemRoomDao(): SubsGroupItem.RoomDao -// abstract fun subsRuleItemRoomDao(): SubsRuleItem.RoomDao abstract fun subsConfigRoomDao(): SubsConfig.RoomDao companion object { diff --git a/app/src/main/java/li/songe/gkd/db/table/SubsConfig.kt b/app/src/main/java/li/songe/gkd/db/table/SubsConfig.kt index a676479..05b5fd4 100644 --- a/app/src/main/java/li/songe/gkd/db/table/SubsConfig.kt +++ b/app/src/main/java/li/songe/gkd/db/table/SubsConfig.kt @@ -27,10 +27,10 @@ data class SubsConfig( @ColumnInfo(name = "type") var type: Int = 0, @ColumnInfo(name = "enable") var enable: Boolean = true, - @ColumnInfo(name = "subs_item_id") var subsItemId: Long = 0, - @ColumnInfo(name = "package_name") var packageName: String = "", - @ColumnInfo(name = "group_key") var groupKey: Int = 0, - @ColumnInfo(name = "rule_key") var ruleKey: Int = 0, + @ColumnInfo(name = "subs_item_id") var subsItemId: Long = -1, + @ColumnInfo(name = "app_id") var appId: String = "", + @ColumnInfo(name = "group_key") var groupKey: Int = -1, + @ColumnInfo(name = "rule_key") var ruleKey: Int = -1, ) : BaseTable, Parcelable { companion object { diff --git a/app/src/main/java/li/songe/gkd/db/table/SubsItem.kt b/app/src/main/java/li/songe/gkd/db/table/SubsItem.kt index 6c96e29..ff4ef52 100644 --- a/app/src/main/java/li/songe/gkd/db/table/SubsItem.kt +++ b/app/src/main/java/li/songe/gkd/db/table/SubsItem.kt @@ -8,7 +8,7 @@ import li.songe.gkd.db.BaseTable @Entity( tableName = "subs_item", - indices = [Index(value = ["url"], unique = true)] + indices = [Index(value = ["update_url"], unique = true)] ) @Parcelize data class SubsItem( @@ -18,23 +18,22 @@ data class SubsItem( @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") override var id: Long = 0, @ColumnInfo(name = "ctime") override var ctime: Long = System.currentTimeMillis(), @ColumnInfo(name = "mtime") override var mtime: Long = System.currentTimeMillis(), - /** - * 订阅文件下载地址,也是更新链接 - */ - @ColumnInfo(name = "url") var url: String, - /** - * 订阅文件下载后存放的路径 - */ - @ColumnInfo(name = "file_path") var filePath: String, + + @ColumnInfo(name = "enable") var enable: Boolean = true, /** * 用户自定义备注 */ @ColumnInfo(name = "comment") var comment: String = "", + /** - * 来自订阅文件的描述,不应该由用户write + * 订阅文件下载地址,也是更新链接 */ - @ColumnInfo(name = "description") var description: String = "", - @ColumnInfo(name = "enable") var enable: Boolean = true, + @ColumnInfo(name = "update_url") var updateUrl: String, + /** + * 订阅文件下载后存放的路径 + */ + @ColumnInfo(name = "file_path") var filePath: String, + ) : Parcelable, BaseTable { @Dao diff --git a/app/src/main/java/li/songe/gkd/router/RouterHost.kt b/app/src/main/java/li/songe/gkd/router/RouterHost.kt index c430149..566b572 100644 --- a/app/src/main/java/li/songe/gkd/router/RouterHost.kt +++ b/app/src/main/java/li/songe/gkd/router/RouterHost.kt @@ -15,7 +15,6 @@ import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.platform.LocalContext -import com.blankj.utilcode.util.LogUtils import kotlinx.coroutines.delay import kotlinx.coroutines.launch import li.songe.gkd.router.Router.Companion.LocalRouter diff --git a/app/src/main/java/li/songe/gkd/service/HttpServerService.kt b/app/src/main/java/li/songe/gkd/server/HttpServerService.kt similarity index 96% rename from app/src/main/java/li/songe/gkd/service/HttpServerService.kt rename to app/src/main/java/li/songe/gkd/server/HttpServerService.kt index 6f8cb02..33c3286 100644 --- a/app/src/main/java/li/songe/gkd/service/HttpServerService.kt +++ b/app/src/main/java/li/songe/gkd/server/HttpServerService.kt @@ -1,4 +1,4 @@ -package li.songe.gkd.service +package li.songe.gkd.server import android.app.Service import android.content.Context @@ -23,7 +23,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import li.songe.gkd.App -import li.songe.gkd.data.api.WindowData +import li.songe.gkd.accessibility.GkdAccessService +import li.songe.gkd.server.api.WindowData +import li.songe.gkd.service.ScreenshotService import li.songe.gkd.store.Storage import java.net.NetworkInterface diff --git a/app/src/main/java/li/songe/gkd/service/RpcError.kt b/app/src/main/java/li/songe/gkd/server/RpcError.kt similarity index 70% rename from app/src/main/java/li/songe/gkd/service/RpcError.kt rename to app/src/main/java/li/songe/gkd/server/RpcError.kt index 3914fc4..7ace216 100644 --- a/app/src/main/java/li/songe/gkd/service/RpcError.kt +++ b/app/src/main/java/li/songe/gkd/server/RpcError.kt @@ -1,8 +1,10 @@ -package li.songe.gkd.service +package li.songe.gkd.server + import kotlinx.serialization.Serializable @Serializable data class RpcError( val message: String = "unknown error", val code: Int = 0, + val __error: Boolean = true ) diff --git a/app/src/main/java/li/songe/gkd/data/api/AttrData.kt b/app/src/main/java/li/songe/gkd/server/api/AttrData.kt similarity index 73% rename from app/src/main/java/li/songe/gkd/data/api/AttrData.kt rename to app/src/main/java/li/songe/gkd/server/api/AttrData.kt index 5bd5e64..c12c297 100644 --- a/app/src/main/java/li/songe/gkd/data/api/AttrData.kt +++ b/app/src/main/java/li/songe/gkd/server/api/AttrData.kt @@ -1,33 +1,20 @@ -package li.songe.gkd.data.api +package li.songe.gkd.server.api import android.graphics.Rect import android.view.accessibility.AccessibilityNodeInfo -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass import kotlinx.serialization.Serializable @Serializable -@JsonClass(generateAdapter = true) data class AttrData( - @Json(name = "id") val id: String?, - @Json(name = "className") val className: String?, - @Json(name = "childCount") val childCount: Int, - @Json(name = "text") val text: String?, - @Json(name = "isClickable") val isClickable: Boolean, - @Json(name = "contentDescription") val contentDescription: String?, - @Json(name = "left") val left: Int, - @Json(name = "top") val top: Int, - @Json(name = "right") val right: Int, - @Json(name = "bottom") val bottom: Int, ) { companion object { diff --git a/app/src/main/java/li/songe/gkd/data/api/NodeData.kt b/app/src/main/java/li/songe/gkd/server/api/NodeData.kt similarity index 83% rename from app/src/main/java/li/songe/gkd/data/api/NodeData.kt rename to app/src/main/java/li/songe/gkd/server/api/NodeData.kt index eb05868..bc673cb 100644 --- a/app/src/main/java/li/songe/gkd/data/api/NodeData.kt +++ b/app/src/main/java/li/songe/gkd/server/api/NodeData.kt @@ -1,9 +1,7 @@ -package li.songe.gkd.data.api +package li.songe.gkd.server.api import android.view.accessibility.AccessibilityNodeInfo -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import li.songe.node_selector.forEachIndexed +import li.songe.selector.forEachIndexed import java.util.* import kotlinx.serialization.Serializable @@ -13,13 +11,9 @@ import kotlinx.serialization.Serializable */ @Serializable -@JsonClass(generateAdapter = true) data class NodeData( - @Json(name = "id") val id: Int, - @Json(name = "pid") val pid: Int, - @Json(name = "attr") val attrData: AttrData ) { companion object { diff --git a/app/src/main/java/li/songe/gkd/data/api/WindowData.kt b/app/src/main/java/li/songe/gkd/server/api/WindowData.kt similarity index 56% rename from app/src/main/java/li/songe/gkd/data/api/WindowData.kt rename to app/src/main/java/li/songe/gkd/server/api/WindowData.kt index 8deecc5..b4539fa 100644 --- a/app/src/main/java/li/songe/gkd/data/api/WindowData.kt +++ b/app/src/main/java/li/songe/gkd/server/api/WindowData.kt @@ -1,25 +1,17 @@ -package li.songe.gkd.data.api +package li.songe.gkd.server.api -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.squareup.moshi.Moshi -import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory -import li.songe.gkd.service.GkdAccessService + +import li.songe.gkd.accessibility.GkdAccessService import kotlinx.serialization.Serializable -import java.util.* @Serializable -@JsonClass(generateAdapter = true) data class WindowData( - @Json(name = "packageName") val packageName: String?=null, - @Json(name = "activityName") val activityName: String?=null, - @Json(name = "nodeDataList") val nodeDataList: List?=null, ) { companion object { - val singleton:WindowData + val singleton: WindowData get() = WindowData( packageName = GkdAccessService.currentPackageName(), activityName = GkdAccessService.currentActivityName(), @@ -32,10 +24,7 @@ data class WindowData( activityName = GkdAccessService.currentActivityName(), nodeDataList = NodeData.info2nodeList(GkdAccessService.rootInActiveWindow()) ) - val moshi: Moshi = Moshi.Builder() - .addLast(KotlinJsonAdapterFactory()) - .build() - return moshi.adapter(WindowData::class.java).toJson(window) + return "" } } } diff --git a/app/src/main/java/li/songe/gkd/service/ShizukuService.kt b/app/src/main/java/li/songe/gkd/service/ShizukuService.kt deleted file mode 100644 index 99509fa..0000000 --- a/app/src/main/java/li/songe/gkd/service/ShizukuService.kt +++ /dev/null @@ -1,15 +0,0 @@ -package li.songe.gkd.service - -import li.songe.gkd.IUserService - -class ShizukuService: IUserService.Stub() { - override fun destroy() { - } - - override fun exit() { - } - - override fun doSomething(): String { - return "" - } -} \ No newline at end of file diff --git a/app/src/main/java/li/songe/gkd/subs/SubsParser.kt b/app/src/main/java/li/songe/gkd/subs/SubsParser.kt new file mode 100644 index 0000000..cdac567 --- /dev/null +++ b/app/src/main/java/li/songe/gkd/subs/SubsParser.kt @@ -0,0 +1,12 @@ +package li.songe.gkd.subs + +import kotlinx.serialization.decodeFromString +import li.songe.gkd.subs.data.GkdSubs +import li.songe.gkd.util.Singleton.json +import li.songe.gkd.util.Singleton.json5 + +object SubsParser : (String) -> Unit { + override fun invoke(p0: String) { + val gkdSubs: GkdSubs = json.decodeFromString(json5.load(p0).toJson(false, false)) + } +} \ No newline at end of file diff --git a/app/src/main/java/li/songe/gkd/subs/data/GkdAction.kt b/app/src/main/java/li/songe/gkd/subs/data/GkdAction.kt new file mode 100644 index 0000000..9ec25a0 --- /dev/null +++ b/app/src/main/java/li/songe/gkd/subs/data/GkdAction.kt @@ -0,0 +1,14 @@ +package li.songe.gkd.subs.data + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class GkdAction( + @SerialName("type") + val type: String, + @SerialName("target") + val target: String, + @SerialName("position") + val position: String? +) diff --git a/app/src/main/java/li/songe/gkd/subs/data/GkdApp.kt b/app/src/main/java/li/songe/gkd/subs/data/GkdApp.kt new file mode 100644 index 0000000..35e63c9 --- /dev/null +++ b/app/src/main/java/li/songe/gkd/subs/data/GkdApp.kt @@ -0,0 +1,10 @@ +package li.songe.gkd.subs.data + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class GkdApp( + @SerialName("id") val id: String, + @SerialName("groupList") val groupList: List +) diff --git a/app/src/main/java/li/songe/gkd/subs/data/GkdGroup.kt b/app/src/main/java/li/songe/gkd/subs/data/GkdGroup.kt new file mode 100644 index 0000000..2fc13fb --- /dev/null +++ b/app/src/main/java/li/songe/gkd/subs/data/GkdGroup.kt @@ -0,0 +1,18 @@ +package li.songe.gkd.subs.data + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class GkdGroup( + @SerialName("key") + val key: Int, + @SerialName("ruleList") + val ruleList: List, + @SerialName("desc") + val desc: String? = null, + @SerialName("activityId") + val activityId: String? = null, + @SerialName("cd") + val cd: Int? = null +) diff --git a/app/src/main/java/li/songe/gkd/subs/data/GkdRule.kt b/app/src/main/java/li/songe/gkd/subs/data/GkdRule.kt new file mode 100644 index 0000000..56bf28f --- /dev/null +++ b/app/src/main/java/li/songe/gkd/subs/data/GkdRule.kt @@ -0,0 +1,18 @@ +package li.songe.gkd.subs.data + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class GkdRule( + @SerialName("key") + val key: Int, + @SerialName("match") + val match: String, + @SerialName("desc") + val desc: String? = null, + @SerialName("cd") + val cd: Int? = null, + @SerialName("activityId") + val activityId: String? = null, +) diff --git a/app/src/main/java/li/songe/gkd/subs/data/GkdSubs.kt b/app/src/main/java/li/songe/gkd/subs/data/GkdSubs.kt new file mode 100644 index 0000000..176383f --- /dev/null +++ b/app/src/main/java/li/songe/gkd/subs/data/GkdSubs.kt @@ -0,0 +1,18 @@ +package li.songe.gkd.subs.data + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class GkdSubs( + @SerialName("version") + val version: Int, + @SerialName("desc") + val desc: String? = null, + @SerialName("author") + val author: String? = null, + @SerialName("updateUrl") + val updateUrl: String? = null, + @SerialName("appList") + val appList: List +) diff --git a/app/src/main/java/li/songe/gkd/ui/AppItemPage.kt b/app/src/main/java/li/songe/gkd/ui/AppItemPage.kt index be2f193..f81b82a 100644 --- a/app/src/main/java/li/songe/gkd/ui/AppItemPage.kt +++ b/app/src/main/java/li/songe/gkd/ui/AppItemPage.kt @@ -71,7 +71,7 @@ object AppItemPage : Page { delay(400) val config = params.subsConfig val mutableSet = - RoomX.select { (SubsConfig::type eq SubsConfig.GroupType) and (SubsConfig::subsItemId eq config.subsItemId) and (SubsConfig::packageName eq config.packageName) } + RoomX.select { (SubsConfig::type eq SubsConfig.GroupType) and (SubsConfig::subsItemId eq config.subsItemId) and (SubsConfig::appId eq config.appId) } .toMutableSet() val list = mutableListOf() params.subsApp.groupList.forEach { group -> @@ -80,7 +80,7 @@ object AppItemPage : Page { } else { val item = mutableSet.find { s -> s.groupKey == group.key } ?: SubsConfig( subsItemId = config.subsItemId, - packageName = config.packageName, + appId = config.appId, groupKey = group.key, type = SubsConfig.GroupType ) @@ -107,7 +107,7 @@ object AppItemPage : Page { ) Spacer(modifier = Modifier.width(10.dp)) Text( - text = params.subsApp.packageName, + text = params.subsApp.id, maxLines = 1, softWrap = false, overflow = TextOverflow.Ellipsis, @@ -143,7 +143,7 @@ object AppItemPage : Page { verticalArrangement = Arrangement.SpaceBetween ) { Text( - text = group.description ?: "-", + text = group.name ?: "-", maxLines = 1, softWrap = false, overflow = TextOverflow.Ellipsis, @@ -151,10 +151,10 @@ object AppItemPage : Page { .fillMaxWidth() ) Text( - text = if (group.className.startsWith(params.subsApp.packageName)) { - group.className.substring(params.subsApp.packageName.length) + text = if (group.activityId.startsWith(params.subsApp.id)) { + group.activityId.substring(params.subsApp.id.length) } else { - group.className + group.activityId }, maxLines = 1, softWrap = false, diff --git a/app/src/main/java/li/songe/gkd/ui/DebugPage.kt b/app/src/main/java/li/songe/gkd/ui/DebugPage.kt index 234823b..f1e082c 100644 --- a/app/src/main/java/li/songe/gkd/ui/DebugPage.kt +++ b/app/src/main/java/li/songe/gkd/ui/DebugPage.kt @@ -24,8 +24,8 @@ import kotlinx.coroutines.* import li.songe.gkd.MainActivity import li.songe.gkd.router.Page import li.songe.gkd.router.Router -import li.songe.gkd.service.GkdAccessService -import li.songe.gkd.service.HttpServerService +import li.songe.gkd.accessibility.GkdAccessService +import li.songe.gkd.server.HttpServerService import li.songe.gkd.service.ScreenshotService import li.songe.gkd.ui.component.StatusBar import li.songe.gkd.ui.component.TextSwitch diff --git a/app/src/main/java/li/songe/gkd/ui/GroupItemPage.kt b/app/src/main/java/li/songe/gkd/ui/GroupItemPage.kt index 65572ce..f749bf0 100644 --- a/app/src/main/java/li/songe/gkd/ui/GroupItemPage.kt +++ b/app/src/main/java/li/songe/gkd/ui/GroupItemPage.kt @@ -10,7 +10,6 @@ import androidx.compose.material.TextButton import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow @@ -46,7 +45,7 @@ object GroupItemPage : Page { .padding(10.dp, 0.dp) ) Text( - text = params.group.className, modifier = Modifier + text = params.group.activityId, modifier = Modifier .fillMaxWidth() .padding(10.dp, 0.dp) ) @@ -71,7 +70,7 @@ object GroupItemPage : Page { verticalArrangement = Arrangement.SpaceBetween ) { Text( - text = ruleList[i].description ?: "-", + text = ruleList[i].name ?: "-", maxLines = 1, softWrap = false, overflow = TextOverflow.Ellipsis, @@ -79,7 +78,7 @@ object GroupItemPage : Page { .fillMaxWidth(), ) Text( - text = ruleList[i].selector, + text = ruleList[i].match, maxLines = 1, softWrap = false, overflow = TextOverflow.Ellipsis, @@ -107,11 +106,11 @@ object GroupItemPage : Page { setShowData(null) }, title = { - Text(text = showData.description ?: "-") + Text(text = showData.name ?: "-") }, text = { Text( - text = showData.selector, + text = showData.match, fontSize = 16.sp ) }, diff --git a/app/src/main/java/li/songe/gkd/ui/SubsItemInsertPage.kt b/app/src/main/java/li/songe/gkd/ui/SubsItemInsertPage.kt index ccd0d7e..baa8908 100644 --- a/app/src/main/java/li/songe/gkd/ui/SubsItemInsertPage.kt +++ b/app/src/main/java/li/songe/gkd/ui/SubsItemInsertPage.kt @@ -94,18 +94,20 @@ object SubsItemInsertPage : Page { Button(onClick = { scope.launch { subReqStatus = Status.Progress() - if (RoomX.select { SubsItem::url eq url }.isNotEmpty()) { + if (RoomX.select { SubsItem::updateUrl eq url }.isNotEmpty()) { subReqStatus = Status.Error("链接已经存在,不可重复添加") return@launch } - val sub = try { + var sub = try { Singleton.client.get(url).body() } catch (e: Exception) { e.printStackTrace() subReqStatus = Status.Error(e) return@launch } - sub.url = url + if (sub.updateUrl == null) { + sub = sub.copy(updateUrl = url) + } File( PathUtils.getExternalAppFilesPath() .plus("/subscription/") @@ -126,9 +128,8 @@ object SubsItemInsertPage : Page { val newSubsItem = SubsItem( enable = enable, comment = comment, - url = url, + updateUrl = url, filePath = fp.absolutePath, - description = sub.description ) RoomX.insert(newSubsItem) subReqStatus = Status.Success(Unit) diff --git a/app/src/main/java/li/songe/gkd/ui/SubsItemUpdatePage.kt b/app/src/main/java/li/songe/gkd/ui/SubsItemUpdatePage.kt index a35c860..7ad886c 100644 --- a/app/src/main/java/li/songe/gkd/ui/SubsItemUpdatePage.kt +++ b/app/src/main/java/li/songe/gkd/ui/SubsItemUpdatePage.kt @@ -32,7 +32,7 @@ object SubsItemUpdatePage : Page { val scope = rememberCoroutineScope() var comment by remember { mutableStateOf(params.comment) } - var url by remember { mutableStateOf(params.url) } + var url by remember { mutableStateOf(params.updateUrl) } var enable by remember { mutableStateOf(params.enable) } Column(modifier = Modifier.padding(10.dp, 0.dp)) { @@ -75,7 +75,7 @@ object SubsItemUpdatePage : Page { val newSubsItem = params.copy( enable = enable, comment = comment, - url = url, + updateUrl = url, mtime = System.currentTimeMillis() ) RoomX.update(newSubsItem) diff --git a/app/src/main/java/li/songe/gkd/ui/SubsPage.kt b/app/src/main/java/li/songe/gkd/ui/SubsPage.kt index 557cca9..7ec56d7 100644 --- a/app/src/main/java/li/songe/gkd/ui/SubsPage.kt +++ b/app/src/main/java/li/songe/gkd/ui/SubsPage.kt @@ -65,7 +65,7 @@ object SubsPage : Page { )!!, subsConfig = SubsConfig( subsItemId = it.toLong(), - packageName = "" + it + appId = "" + it ) ) ) @@ -126,16 +126,16 @@ object SubsPage : Page { val defaultName = "-" val newSubsAppCardDataList = (subStatus as Status.Success).value.appList.map { app -> mutableSet.firstOrNull { v -> - v.packageName == app.packageName + v.appId == app.id }.apply { if (this != null) { mutableSet.remove(this) } - } ?: SubsConfig(subsItemId = subsItem.id, packageName = app.packageName, type = SubsConfig.AppType) + } ?: SubsConfig(subsItemId = subsItem.id, appId = app.id, type = SubsConfig.AppType) }.map { subsConfig -> async(IO) { val info: ApplicationInfo = try { - packageManager.getApplicationInfo(subsConfig.packageName, 0) + packageManager.getApplicationInfo(subsConfig.appId, 0) } catch (e: Exception) { return@async SubsAppCardData( defaultName, @@ -183,7 +183,7 @@ object SubsPage : Page { modifier = textModifier ) Text( - text = "描述: ${sub?.description}", + text = "描述: ${sub?.name}", modifier = textModifier ) } @@ -222,6 +222,7 @@ object SubsPage : Page { .wrapContentSize() ) { SubsAppCard(loading = true, args = placeholderList[i]) + Text(text = "") } } } diff --git a/app/src/main/java/li/songe/gkd/ui/component/SubsAppCard.kt b/app/src/main/java/li/songe/gkd/ui/component/SubsAppCard.kt index fb3c46d..6e9cc60 100644 --- a/app/src/main/java/li/songe/gkd/ui/component/SubsAppCard.kt +++ b/app/src/main/java/li/songe/gkd/ui/component/SubsAppCard.kt @@ -60,7 +60,7 @@ fun SubsAppCard( .placeholder(loading, highlight = PlaceholderHighlight.fade()) ) Text( - text = args.subsConfig.packageName, maxLines = 1, + text = args.subsConfig.appId, maxLines = 1, softWrap = false, overflow = TextOverflow.Ellipsis, modifier = Modifier diff --git a/app/src/main/java/li/songe/gkd/ui/component/SubsCard.kt b/app/src/main/java/li/songe/gkd/ui/component/SubsCard.kt new file mode 100644 index 0000000..5ff7a3d --- /dev/null +++ b/app/src/main/java/li/songe/gkd/ui/component/SubsCard.kt @@ -0,0 +1,63 @@ +package li.songe.gkd.ui.component + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import li.songe.gkd.R +import li.songe.gkd.subs.data.GkdApp +import li.songe.gkd.subs.data.GkdSubs + +@Composable +fun SubsCard(gkdSubs: GkdSubs) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(4.dp) + ) { + Column(modifier = Modifier.weight(1f)) { + Text( + text = gkdSubs.desc ?: "简单描述", maxLines = 1, + softWrap = false, + overflow = TextOverflow.Ellipsis + ) + Text(text = gkdSubs.appList.size.toString()) + } + Spacer(modifier = Modifier.width(5.dp)) +// Box() { +// Text(text = "几秒前更新", fontSize = 5.sp) + Image( + painter = painterResource(R.drawable.ic_menu), + contentDescription = "", + modifier = Modifier + .clickable { + + } + .size(20.dp) + ) +// } + + } +} + +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Composable +fun PreviewSubsCard() { + Surface(modifier = Modifier.width(200.dp)) { + SubsCard( + GkdSubs( + version = 1, + appList = listOf(GkdApp(id = "com.tencent.mm", groupList = listOf())) + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/li/songe/gkd/ui/component/SubsItemCard.kt b/app/src/main/java/li/songe/gkd/ui/component/SubsItemCard.kt index 7fcb5fe..cc2970a 100644 --- a/app/src/main/java/li/songe/gkd/ui/component/SubsItemCard.kt +++ b/app/src/main/java/li/songe/gkd/ui/component/SubsItemCard.kt @@ -6,11 +6,9 @@ import androidx.compose.foundation.layout.* import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview @@ -34,13 +32,13 @@ fun SubsItemCard( ) { Column(modifier = Modifier.weight(1f)) { Text( - text = if (data.comment.isNotEmpty()) "${data.comment}:${data.description}" else data.description, + text = data.comment, maxLines = 1, softWrap = false, overflow = TextOverflow.Ellipsis ) Text( - text = data.url, + text = data.updateUrl, maxLines = 1, softWrap = false, overflow = TextOverflow.Ellipsis @@ -89,7 +87,7 @@ fun PreviewSubscriptionItemCard() { SubsItemCard( SubsItem( filePath = "filepath", - url = "https://raw.githubusercontents.com/lisonge/gkd-subscription/main/src/ad-startup.gkd.json", + updateUrl = "https://raw.githubusercontents.com/lisonge/gkd-subscription/main/src/ad-startup.gkd.json", comment = "备注注注注注注注注注注注注注注注注注注注注注注注注注注注注注注注注注注注注注注注注注注注注注注" ) ) diff --git a/app/src/main/java/li/songe/gkd/ui/component/Text114514.kt b/app/src/main/java/li/songe/gkd/ui/component/Text114514.kt new file mode 100644 index 0000000..5995bd3 --- /dev/null +++ b/app/src/main/java/li/songe/gkd/ui/component/Text114514.kt @@ -0,0 +1,34 @@ +package li.songe.gkd.ui.component + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.width +import androidx.compose.material.LocalTextStyle +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +fun Text114514() { + CompositionLocalProvider( + LocalTextStyle provides TextStyle(fontSize = 80.sp) + ) { + Row { + Text("ping") + Text("pong") + } + } +} + +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Composable +fun Text114514Preview() { + Surface(modifier = Modifier.width(200.dp)) { + Text114514() + } +} \ No newline at end of file diff --git a/app/src/main/java/li/songe/gkd/ui/home/BottomNavItem.kt b/app/src/main/java/li/songe/gkd/ui/home/BottomNavItem.kt index a736074..2de4955 100644 --- a/app/src/main/java/li/songe/gkd/ui/home/BottomNavItem.kt +++ b/app/src/main/java/li/songe/gkd/ui/home/BottomNavItem.kt @@ -11,16 +11,6 @@ data class BottomNavItem( ) val BottomNavItems = listOf( - BottomNavItem( - label = "统计", - icon = R.drawable.ic_chart_bar, - route = "statistics" - ), - BottomNavItem( - label = "本地", - icon = R.drawable.ic_database_set, - route = "native" - ), BottomNavItem( label = "订阅", icon = R.drawable.ic_link, diff --git a/app/src/main/java/li/songe/gkd/ui/home/HomePage.kt b/app/src/main/java/li/songe/gkd/ui/home/HomePage.kt index 1201b97..9575921 100644 --- a/app/src/main/java/li/songe/gkd/ui/home/HomePage.kt +++ b/app/src/main/java/li/songe/gkd/ui/home/HomePage.kt @@ -1,13 +1,10 @@ package li.songe.gkd.ui.home -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.material.Scaffold import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha -import androidx.compose.ui.graphics.Color import androidx.compose.ui.zIndex import li.songe.gkd.router.Page import li.songe.gkd.router.Router @@ -26,7 +23,7 @@ object HomePage : Page { params: Unit, router: Router ) -> Unit = @Composable { _, _ -> - var tabInt by remember { mutableStateOf(2) } + var tabInt by remember { mutableStateOf(0) } Column(modifier = Modifier.fillMaxSize()) { StatusBar() Scaffold( @@ -39,7 +36,7 @@ object HomePage : Page { .alpha(if (tabInt == 0) 1f else 0f) .zIndex(if (tabInt == 0) 1f else 0f) .noRippleClickable { }) { - StatisticsPage() + SubscriptionManagePage() } Box( modifier = Modifier @@ -47,22 +44,6 @@ object HomePage : Page { .alpha(if (tabInt == 1) 1f else 0f) .zIndex(if (tabInt == 1) 1f else 0f) .noRippleClickable { }) { - NativePage() - } - Box( - modifier = Modifier - .fillMaxSize() - .alpha(if (tabInt == 2) 1f else 0f) - .zIndex(if (tabInt == 2) 1f else 0f) - .noRippleClickable { }) { - SubscriptionManagePage() - } - Box( - modifier = Modifier - .fillMaxSize() - .alpha(if (tabInt == 3) 1f else 0f) - .zIndex(if (tabInt == 3) 1f else 0f) - .noRippleClickable { }) { SettingsPage() } } diff --git a/app/src/main/java/li/songe/gkd/ui/home/page/SettingsPage.kt b/app/src/main/java/li/songe/gkd/ui/home/page/SettingsPage.kt index 80088f4..68a75e5 100644 --- a/app/src/main/java/li/songe/gkd/ui/home/page/SettingsPage.kt +++ b/app/src/main/java/li/songe/gkd/ui/home/page/SettingsPage.kt @@ -14,10 +14,8 @@ import com.blankj.utilcode.util.LogUtils import li.songe.gkd.MainActivity import li.songe.gkd.R import li.songe.gkd.router.Router.Companion.LocalRouter -import li.songe.gkd.service.TestService import li.songe.gkd.store.Storage import li.songe.gkd.ui.DebugPage -import li.songe.gkd.ui.component.StatusBar import li.songe.gkd.ui.component.TextSwitch import li.songe.gkd.util.LocaleString.Companion.localeString diff --git a/app/src/main/java/li/songe/gkd/util/Singleton.kt b/app/src/main/java/li/songe/gkd/util/Singleton.kt index b0a5791..cd44f6d 100644 --- a/app/src/main/java/li/songe/gkd/util/Singleton.kt +++ b/app/src/main/java/li/songe/gkd/util/Singleton.kt @@ -1,20 +1,17 @@ package li.songe.gkd.util -import com.squareup.moshi.Moshi -import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory +import blue.endless.jankson.Jankson import io.ktor.client.* import io.ktor.client.engine.cio.* import io.ktor.client.plugins.contentnegotiation.* import io.ktor.http.* import io.ktor.serialization.kotlinx.json.* import kotlinx.serialization.json.Json +import java.lang.reflect.InvocationHandler +import java.lang.reflect.Proxy object Singleton { - val moshi: Moshi by lazy { - Moshi.Builder() - .addLast(KotlinJsonAdapterFactory()) - .build() - } + val json by lazy { Json { prettyPrint = true @@ -22,6 +19,7 @@ object Singleton { ignoreUnknownKeys = true } } + val json5: Jankson by lazy { Jankson.builder().build() } val client by lazy { HttpClient(CIO) { install(ContentNegotiation) { @@ -29,4 +27,17 @@ object Singleton { } } } + +// inline fun produce(data: T, block: (data: T) -> Unit): T { +// val proxyData = Proxy.newProxyInstance( +// T::class.java.classLoader, +// arrayOf(), +// InvocationHandler { proxy, method, args -> +// +// }) as T +// block(proxyData) +// return proxyData +// } + + } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_menu.xml b/app/src/main/res/drawable/ic_menu.xml new file mode 100644 index 0000000..eec2e63 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c1b5c7d..af6ccf2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,6 +1,6 @@ gkd - 搞快点 - 基于规则匹配的无障碍速点服务 + 搞快点 + 基于规则匹配的无障碍速点服务 \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 033e52e..94920e4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,9 +18,10 @@ buildscript { } dependencies { - classpath("com.android.tools.build:gradle:7.3.0") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.10") - classpath("org.jetbrains.kotlin:kotlin-serialization:1.7.10") + classpath("com.android.tools.build:gradle:7.3.1") +// 当前 android 项目 kotlin 的版本 + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20") + classpath("org.jetbrains.kotlin:kotlin-serialization:1.7.20") classpath("dev.rikka.tools.refine:gradle-plugin:3.0.3") } } diff --git a/node_selector/src/main/java/li/songe/node_selector/GkdSelector.kt b/node_selector/src/main/java/li/songe/node_selector/GkdSelector.kt deleted file mode 100644 index ee79579..0000000 --- a/node_selector/src/main/java/li/songe/node_selector/GkdSelector.kt +++ /dev/null @@ -1,85 +0,0 @@ -package li.songe.node_selector - -import android.accessibilityservice.AccessibilityService -import android.accessibilityservice.GestureDescription -import android.graphics.Path -import android.graphics.Rect -import android.view.accessibility.AccessibilityNodeInfo -import li.songe.node_selector.parser.Tools -import li.songe.node_selector.selector.Position -import li.songe.node_selector.wrapper.PropertySelectorWrapper -import java.util.concurrent.Executors -import java.util.concurrent.Future - -data class GkdSelector(val wrapper: PropertySelectorWrapper, val position: Position?) { - - override fun toString() = wrapper.toString() + (position ?: "").toString() - fun collect(nodeInfo: AccessibilityNodeInfo) = nodeInfo.traverseAll { wrapper.match(it) } - fun collectParallel(nodeInfo: AccessibilityNodeInfo): AccessibilityNodeInfo? { - var resultNodeInfo: AccessibilityNodeInfo? = null - nodeInfo.traverseAll { child -> - taskList.add( - executorService.submit { - if (resultNodeInfo != null) { - return@submit - } - if (wrapper.match(child)) { - resultNodeInfo = child - } - } - ) - resultNodeInfo != null - } - if (resultNodeInfo != null) { - return resultNodeInfo - } - taskList.forEach { task -> task.get() } - taskList.clear() - return resultNodeInfo - } - - fun click(nodeInfo: AccessibilityNodeInfo, service: AccessibilityService) = when { - position != null -> { - val react = Rect() - nodeInfo.getBoundsInScreen(react) - val x = react.left + position.x.number.toFloat() / 100f * (react.right - react.left) - val y = react.top + position.x.number.toFloat() / 100f * (react.bottom - react.top) - val gestureDescription = GestureDescription.Builder() - val path = Path() - path.moveTo(x, y) - gestureDescription.addStroke(GestureDescription.StrokeDescription(path, 0, 300)) - service.dispatchGesture(gestureDescription.build(), null, null) - position.toString() - } - nodeInfo.isClickable -> { - nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK) - "self" - } - else -> { - val react = Rect() - nodeInfo.getBoundsInScreen(react) - val x = react.left + 50f / 100f * (react.right - react.left) - val y = react.top + 50f / 100f * (react.bottom - react.top) - val gestureDescription = GestureDescription.Builder() - val path = Path() - path.moveTo(x, y) - gestureDescription.addStroke(GestureDescription.StrokeDescription(path, 0, 300)) - service.dispatchGesture(gestureDescription.build(), null, null) - "(50%, 50%)" - } - - } - - companion object { - val gkdSelectorParser = Tools.gkdSelectorParser - - private val executorService by lazy { - Executors.newFixedThreadPool( - Runtime.getRuntime().availableProcessors(), - ) - } - - private val taskList = mutableListOf>() - - } -} diff --git a/node_selector/src/main/java/li/songe/node_selector/parser/Parser.kt b/node_selector/src/main/java/li/songe/node_selector/parser/Parser.kt deleted file mode 100644 index e7e96dc..0000000 --- a/node_selector/src/main/java/li/songe/node_selector/parser/Parser.kt +++ /dev/null @@ -1,38 +0,0 @@ -package li.songe.node_selector.parser - -class Parser( - val prefix: String = "", - private val invokeTemp: (source: String, offset: Int, prefix: String) -> ParserResult -) : (String, Int) -> ParserResult { - - - companion object { - fun assert(source: String, i: Int) { - if (i >= source.length) { - throw ParserException(source, i) - } - } - - fun assert(source: String, i: Int, expectValue: Char) { - assert(source, i, expectValue.toString()) - } - - fun assert(source: String, i: Int, expectValue: String) { - if (i >= source.length) { - throw ParserException(source, i, expectValue) - } - if (!expectValue.contains(source[i])) { - throw ParserException(source, i, expectValue) - } - } - - fun throwError(source: String, i: Int, expectValue: String? = ""): Nothing { - throw ParserException(source, i, expectValue) - } - } - - override fun invoke(source: String, offset: Int): ParserResult { - return invokeTemp(source, offset, prefix) - } - -} \ No newline at end of file diff --git a/node_selector/src/main/java/li/songe/node_selector/parser/ParserException.kt b/node_selector/src/main/java/li/songe/node_selector/parser/ParserException.kt deleted file mode 100644 index b5a46ec..0000000 --- a/node_selector/src/main/java/li/songe/node_selector/parser/ParserException.kt +++ /dev/null @@ -1,40 +0,0 @@ -package li.songe.node_selector.parser - -class ParserException(private val source: String, val i: Int, val expectValue: String? = "") : - Exception() { - override val message: String - get() { - return if (expectValue != null) { - when { - expectValue.isEmpty() -> { - return "source: $source, index:${i}, expect:any, actual:${source.getOrNull(i) ?: "array bound"}" - } - expectValue.length == 1 -> { - return "source: $source, index:${i}, expect:${expectValue}, actual:${source.getOrNull(i) ?: "array bound"}" - } - else -> { - val list = expectValue.toList().map { - when (it) { - '\n' -> "\\n" - '\t' -> "\\t" - '\r' -> "\\r" - '\u0020' -> "\\u0020" - else -> it.toString() - } - } - return "source: $source, index:${i}, expect one of:${list.joinToString(",")}, actual:${ - source.getOrNull( - i - ) ?: "array bound" - }" - } - } - } else { - "source: $source, index:${i}, expect eof, actual:${ - source.getOrNull( - i - ) ?: "array bound" - }" - } - } -} \ No newline at end of file diff --git a/node_selector/src/main/java/li/songe/node_selector/parser/ParserResult.kt b/node_selector/src/main/java/li/songe/node_selector/parser/ParserResult.kt deleted file mode 100644 index 83a7d0a..0000000 --- a/node_selector/src/main/java/li/songe/node_selector/parser/ParserResult.kt +++ /dev/null @@ -1,3 +0,0 @@ -package li.songe.node_selector.parser - -data class ParserResult(val data: T, val length: Int=0) diff --git a/node_selector/src/main/java/li/songe/node_selector/selector/Position.kt b/node_selector/src/main/java/li/songe/node_selector/selector/Position.kt deleted file mode 100644 index bf7b9b2..0000000 --- a/node_selector/src/main/java/li/songe/node_selector/selector/Position.kt +++ /dev/null @@ -1,14 +0,0 @@ -package li.songe.node_selector.selector - -data class Position(val x: NumberUnit, val y: NumberUnit) { - override fun toString() = "(${x}, ${y})" - - data class NumberUnit(val number: Number, val unit: Unit) { - override fun toString() = number.toString() + unit.toString() - } - - sealed class Unit(private val key: String) { - override fun toString() = key - object Percentage : Unit("%") - } -} diff --git a/node_selector/src/main/java/li/songe/node_selector/selector/PropertySelector.kt b/node_selector/src/main/java/li/songe/node_selector/selector/PropertySelector.kt deleted file mode 100644 index bf2e820..0000000 --- a/node_selector/src/main/java/li/songe/node_selector/selector/PropertySelector.kt +++ /dev/null @@ -1,83 +0,0 @@ -package li.songe.node_selector.selector - -/** - * 属性选择器 - */ -data class PropertySelector( - val name: String, - val expressionList: List -) { - override fun toString() = "${name}${expressionList.joinToString("")}" - sealed class Operator(private val key: String) { - abstract fun compare(a: Any?, b: Any?): Boolean - abstract fun acceptValue(a: Any?): Boolean - - object More : Operator(">") { - override fun compare(a: Any?, b: Any?) = - (a is Int && b is Int && a > b) || (a is Float && b is Int && a > b) || (a is Float && b is Float && a > b) || (a is Int && b is Float && a > b) - override fun acceptValue(a: Any?) = a is Int || a is Float - } - - object Less : Operator("<") { - override fun compare(a: Any?, b: Any?) = - (a is Int && b is Int && a < b) || (a is Float && b is Int && a < b) || (a is Float && b is Float && a < b) || (a is Int && b is Float && a < b) - - override fun acceptValue(a: Any?) = a is Int || a is Float - } - - object Equal : Operator("=") { - override fun compare(a: Any?, b: Any?) = a == b - override fun acceptValue(a: Any?) = - a is Int? || a is String? || a is Boolean? || a is Float? - } - - object NotEqual : Operator("!=") { - override fun compare(a: Any?, b: Any?) = a != b - override fun acceptValue(a: Any?) = - a is Int? || a is String? || a is Boolean? || a is Float? - } - - object MoreEqual : Operator(">=") { - override fun compare(a: Any?, b: Any?) = - (a is Int && b is Int && a >= b) || (a is Float && b is Int && a >= b) || (a is Float && b is Float && a >= b) || (a is Int && b is Float && a >= b) - - override fun acceptValue(a: Any?) = a is Int || a is Float - } - - object LessEqual : Operator("<=") { - override fun compare(a: Any?, b: Any?) = - (a is Int && b is Int && a <= b) || (a is Float && b is Int && a <= b) || (a is Float && b is Float && a <= b) || (a is Int && b is Float && a <= b) - - override fun acceptValue(a: Any?) = a is Int || a is Float - } - - object Include : Operator("*=") { - override fun compare(a: Any?, b: Any?) = (a is String && b is String && a.contains(b)) - override fun acceptValue(a: Any?) = a is String - } - - object Start : Operator("^=") { - override fun compare(a: Any?, b: Any?) = (a is String && b is String && a.startsWith(b)) - override fun acceptValue(a: Any?) = a is String - } - - object End : Operator("$=") { - override fun compare(a: Any?, b: Any?) = (a is String && b is String && a.endsWith(b)) - override fun acceptValue(a: Any?) = a is String - } - - override fun toString() = key - } - - data class BinaryExpression(val name: String, val operator: Operator, val value: Any?) { - fun compare(otherValue: Any?) = operator.compare(otherValue, value) - override fun toString() = "[${name}${operator}${ - if (value is String) { - "`${value.replace("`", "\\`")}`" - } else { - value - } - }]" - } - -} diff --git a/node_selector/src/main/java/li/songe/node_selector/wrapper/PropertySelectorWrapper.kt b/node_selector/src/main/java/li/songe/node_selector/wrapper/PropertySelectorWrapper.kt deleted file mode 100644 index 21a5fe9..0000000 --- a/node_selector/src/main/java/li/songe/node_selector/wrapper/PropertySelectorWrapper.kt +++ /dev/null @@ -1,70 +0,0 @@ -package li.songe.node_selector.wrapper - -import android.graphics.Rect -import android.view.accessibility.AccessibilityNodeInfo -import li.songe.node_selector.getDepth -import li.songe.node_selector.getIndex -import li.songe.node_selector.selector.PropertySelector - -data class PropertySelectorWrapper( - private val propertySelector: PropertySelector, - val to: CombinatorSelectorWrapper? = null -) { - override fun toString(): String { - return (if (to != null) { - to.toString() + "\u0020" - } else { - "" - }) + propertySelector.toString() - } - - fun match(nodeInfo: AccessibilityNodeInfo): Boolean { - val className = nodeInfo.className.toString() - if (!((className.endsWith(propertySelector.name) && className[className.length - propertySelector.name.length - 1] == '.') - || className == propertySelector.name - ) - ) { - return false - } - - val index by lazy { nodeInfo.getIndex() } - val childCount by lazy { nodeInfo.childCount } - val depth by lazy { nodeInfo.getDepth() } - val id by lazy { nodeInfo.viewIdResourceName?.toString() } - val text by lazy { nodeInfo.text?.toString() } -// val text = nodeInfo.text?.toString() ?: "" - val hintText by lazy { nodeInfo.hintText?.toString() } - val contentDescription by lazy { nodeInfo.contentDescription?.toString() } - val isPassword by lazy { nodeInfo.isPassword } -// val rect = Rect() -// nodeInfo.getBoundsInScreen(rect) - val isChecked by lazy { nodeInfo.isChecked } - - propertySelector.expressionList.forEach { expression -> - val nodeValue: Any? = when (expression.name) { - "index" -> index - "childCount" -> childCount - "depth" -> depth - "id" -> id - "text" -> text - "text.length" -> text?.length - "hintText" -> hintText - "hintText.length" -> hintText?.length - "contentDescription" -> contentDescription - "contentDescription.length" -> contentDescription?.length - "isPassword" -> isPassword - "isChecked" -> isChecked - else -> return false - } - if (!expression.compare(nodeValue)) { - return false - } - } - if (to == null) { - return true - } - return to.match(nodeInfo) - } - - -} diff --git a/room_processor/build.gradle.kts b/room_processor/build.gradle.kts index 168ae32..9bc4dec 100644 --- a/room_processor/build.gradle.kts +++ b/room_processor/build.gradle.kts @@ -20,7 +20,7 @@ tasks.withType { dependencies{ //ksp依赖 - implementation("com.google.devtools.ksp:symbol-processing-api:1.7.10-1.0.6") + implementation("com.google.devtools.ksp:symbol-processing-api:1.7.20-1.0.7") //poet依赖 implementation("com.squareup:kotlinpoet:1.11.0") implementation("com.squareup:kotlinpoet-ksp:1.11.0") diff --git a/node_selector/.gitignore b/selector/.gitignore similarity index 100% rename from node_selector/.gitignore rename to selector/.gitignore diff --git a/node_selector/build.gradle.kts b/selector/build.gradle.kts similarity index 100% rename from node_selector/build.gradle.kts rename to selector/build.gradle.kts diff --git a/node_selector/src/androidTest/java/li/songe/node_selector/ExampleInstrumentedTest.kt b/selector/src/androidTest/java/li/songe/selector/ExampleInstrumentedTest.kt similarity index 95% rename from node_selector/src/androidTest/java/li/songe/node_selector/ExampleInstrumentedTest.kt rename to selector/src/androidTest/java/li/songe/selector/ExampleInstrumentedTest.kt index 35d89ed..8de153c 100644 --- a/node_selector/src/androidTest/java/li/songe/node_selector/ExampleInstrumentedTest.kt +++ b/selector/src/androidTest/java/li/songe/selector/ExampleInstrumentedTest.kt @@ -1,4 +1,4 @@ -package li.songe.node_selector +package li.songe.selector import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.runners.AndroidJUnit4 diff --git a/node_selector/src/main/AndroidManifest.xml b/selector/src/main/AndroidManifest.xml similarity index 75% rename from node_selector/src/main/AndroidManifest.xml rename to selector/src/main/AndroidManifest.xml index dfb036a..b36f07a 100644 --- a/node_selector/src/main/AndroidManifest.xml +++ b/selector/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="li.songe.selector"> \ No newline at end of file diff --git a/node_selector/src/main/java/li/songe/node_selector/Extension.kt b/selector/src/main/java/li/songe/selector/Extension.kt similarity index 98% rename from node_selector/src/main/java/li/songe/node_selector/Extension.kt rename to selector/src/main/java/li/songe/selector/Extension.kt index 4dda7ed..7ff2ff0 100644 --- a/node_selector/src/main/java/li/songe/node_selector/Extension.kt +++ b/selector/src/main/java/li/songe/selector/Extension.kt @@ -1,4 +1,4 @@ -package li.songe.node_selector +package li.songe.selector import android.view.accessibility.AccessibilityNodeInfo import java.util.* diff --git a/selector/src/main/java/li/songe/selector/GkdSelector.kt b/selector/src/main/java/li/songe/selector/GkdSelector.kt new file mode 100644 index 0000000..e30b0f5 --- /dev/null +++ b/selector/src/main/java/li/songe/selector/GkdSelector.kt @@ -0,0 +1,51 @@ +package li.songe.selector + +import android.accessibilityservice.AccessibilityService +import android.accessibilityservice.GestureDescription +import android.graphics.Path +import android.graphics.Rect +import android.view.accessibility.AccessibilityNodeInfo +import li.songe.selector.parser.Transform +import li.songe.selector.wrapper.PropertySelectorWrapper + +data class GkdSelector(val wrapper: PropertySelectorWrapper) { + + override fun toString() = wrapper.toString() + + fun collect(nodeInfo: AccessibilityNodeInfo): AccessibilityNodeInfo? { + val trackList: MutableList = mutableListOf() + nodeInfo.traverseAll { + val match = wrapper.match(it, trackList) + if (!match) { + trackList.clear() + } + return@traverseAll match + } + return trackList.findLast { it != null } + } + + + fun click(nodeInfo: AccessibilityNodeInfo, service: AccessibilityService) = when { + nodeInfo.isClickable -> { + nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK) + "self" + } + else -> { + val react = Rect() + nodeInfo.getBoundsInScreen(react) + val x = react.left + 50f / 100f * (react.right - react.left) + val y = react.top + 50f / 100f * (react.bottom - react.top) + val gestureDescription = GestureDescription.Builder() + val path = Path() + path.moveTo(x, y) + gestureDescription.addStroke(GestureDescription.StrokeDescription(path, 0, 300)) + service.dispatchGesture(gestureDescription.build(), null, null) + "(50%, 50%)" + } + + } + + companion object { + val gkdSelectorParser = Transform.gkdSelectorParser + } +} diff --git a/selector/src/main/java/li/songe/selector/parser/Parser.kt b/selector/src/main/java/li/songe/selector/parser/Parser.kt new file mode 100644 index 0000000..ab4884d --- /dev/null +++ b/selector/src/main/java/li/songe/selector/parser/Parser.kt @@ -0,0 +1,8 @@ +package li.songe.selector.parser + +open class Parser( + val prefix: String = "", + private val invokeTemp: (source: String, offset: Int, prefix: String) -> ParserResult +) : (String, Int) -> ParserResult { + override fun invoke(source: String, offset: Int) = invokeTemp(source, offset, prefix) +} \ No newline at end of file diff --git a/selector/src/main/java/li/songe/selector/parser/ParserResult.kt b/selector/src/main/java/li/songe/selector/parser/ParserResult.kt new file mode 100644 index 0000000..ccf04a4 --- /dev/null +++ b/selector/src/main/java/li/songe/selector/parser/ParserResult.kt @@ -0,0 +1,3 @@ +package li.songe.selector.parser + +data class ParserResult(val data: T, val length: Int = 0) diff --git a/selector/src/main/java/li/songe/selector/parser/SyntaxError.kt b/selector/src/main/java/li/songe/selector/parser/SyntaxError.kt new file mode 100644 index 0000000..cc8beeb --- /dev/null +++ b/selector/src/main/java/li/songe/selector/parser/SyntaxError.kt @@ -0,0 +1,22 @@ +package li.songe.selector.parser + +data class SyntaxError(val expectedValue: String, val position: Int, val source: String) : + Exception( + "expected $expectedValue in selector at position $position, but got ${ + source.getOrNull( + position + ) + }" + ) { + companion object { + fun assert(source: String, offset: Int, value: String = "", expectedValue: String? = null) { + if (offset >= source.length || (value.isNotEmpty() && !value.contains(source[offset]))) { + throw SyntaxError(expectedValue ?: value, offset, source) + } + } + + fun throwError(source: String, offset: Int, expectedValue: String = ""):Nothing { + throw SyntaxError(expectedValue, offset, source) + } + } +} \ No newline at end of file diff --git a/node_selector/src/main/java/li/songe/node_selector/parser/Tools.kt b/selector/src/main/java/li/songe/selector/parser/Transform.kt similarity index 71% rename from node_selector/src/main/java/li/songe/node_selector/parser/Tools.kt rename to selector/src/main/java/li/songe/selector/parser/Transform.kt index 904ea79..5557861 100644 --- a/node_selector/src/main/java/li/songe/node_selector/parser/Tools.kt +++ b/selector/src/main/java/li/songe/selector/parser/Transform.kt @@ -1,13 +1,12 @@ -package li.songe.node_selector.parser +package li.songe.selector.parser -import li.songe.node_selector.GkdSelector -import li.songe.node_selector.selector.CombinatorSelector -import li.songe.node_selector.selector.Position -import li.songe.node_selector.selector.PropertySelector -import li.songe.node_selector.wrapper.CombinatorSelectorWrapper -import li.songe.node_selector.wrapper.PropertySelectorWrapper +import li.songe.selector.GkdSelector +import li.songe.selector.selector.CombinatorSelector +import li.songe.selector.selector.PropertySelector +import li.songe.selector.wrapper.CombinatorSelectorWrapper +import li.songe.selector.wrapper.PropertySelectorWrapper -object Tools { +object Transform { val whiteCharParser = Parser("\u0020\t\r\n") { source, offset, prefix -> var i = offset var data = "" @@ -19,27 +18,28 @@ object Tools { } val whiteCharStrictParser = Parser("\u0020\t\r\n") { source, offset, prefix -> - var i = offset - Parser.assert(source, i, prefix) - var data = source[i].toString() - i++ - while (i < source.length && prefix.contains(source[i])) { - data += source[i] - i++ - } - ParserResult(data, i - offset) + SyntaxError.assert(source, offset, prefix, "whitespace") + whiteCharParser(source, offset) } val nameParser = - Parser("1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_") { source, offset, prefix -> + Parser("*1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_") { source, offset, prefix -> var i = offset - Parser.assert(source, i, prefix) + val s0 = source.getOrNull(i) + if (s0 != null && !prefix.contains(s0)) { + return@Parser ParserResult("", i - offset) + } + SyntaxError.assert(source, i, prefix, "*0-9a-zA-Z_") var data = source[i].toString() i++ + if (data == "*") { // 范匹配 + return@Parser ParserResult(data, i - offset) + } val center = "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_." while (i < source.length) { +// . 不能在开头和结尾 if (data[i - offset - 1] == '.') { - Parser.assert(source, i, prefix) + SyntaxError.assert(source, i, prefix, "[0-9a-zA-Z_]") } if (center.contains(source[i])) { data += source[i] @@ -53,19 +53,19 @@ object Tools { val combinatorOperatorParser = Parser("+-><") { source, offset, prefix -> var i = offset - Parser.assert(source, i, prefix) + SyntaxError.assert(source, i, prefix) return@Parser when (source[i]) { '+' -> ParserResult(CombinatorSelector.Operator.ElderBrother, ++i - offset) '-' -> ParserResult(CombinatorSelector.Operator.YoungerBrother, ++i - offset) '>' -> ParserResult(CombinatorSelector.Operator.Ancestor, ++i - offset) '<' -> ParserResult(CombinatorSelector.Operator.Child, ++i - offset) - else -> throw ParserException(source, i, prefix) + else -> SyntaxError.throwError(source, i, prefix) } } val integerParser = Parser("1234567890") { source, offset, prefix -> var i = offset - Parser.assert(source, i, prefix) + SyntaxError.assert(source, i, prefix, "number") var s = "" while (prefix.contains(source[i]) && i < source.length) { s += source[i] @@ -74,8 +74,13 @@ object Tools { ParserResult(s.toInt(), i - offset) } - val monomialParser = Parser("+-") { source, offset, _ -> + // [+-][a][n[^b]] + val monomialParser = Parser("+-1234567890n") { source, offset, prefix -> var i = offset + SyntaxError.assert(source, i, prefix) + /** + * one of 1, -1 + */ val signal = when (source[i]) { '+' -> { i++ @@ -87,9 +92,9 @@ object Tools { } else -> 1 } - Parser.assert(source, i) i += whiteCharParser(source, i).length - Parser.assert(source, i) + // [a][n[^b]] + SyntaxError.assert(source, i, integerParser.prefix + "n") val coefficient = if (integerParser.prefix.contains(source[i])) { val coefficientResult = integerParser(source, i) @@ -98,6 +103,7 @@ object Tools { } else { 1 } * signal + // [n[^b]] if (i < source.length && source[i] == 'n') { i++ if (i < source.length && source[i] == '^') { @@ -113,28 +119,29 @@ object Tools { } } + // ([+-][a][n[^b]] [+-][a][n[^b]]) val expressionParser = Parser("(0123456789n") { source, offset, prefix -> var i = offset - Parser.assert(source, i, prefix) + SyntaxError.assert(source, i, prefix) val monomialResultList = mutableListOf>>() when (source[i]) { '(' -> { - i += 1 + i++ i += whiteCharParser(source, i).length - Parser.assert(source, i, "+-n1234567890") + SyntaxError.assert(source, i, monomialParser.prefix) while (source[i] != ')') { if (monomialResultList.size > 0) { - Parser.assert(source, i, "+-") + SyntaxError.assert(source, i, "+-") } val monomialResult = monomialParser(source, i) monomialResultList.add(monomialResult) i += monomialResult.length i += whiteCharParser(source, i).length if (i >= source.length) { - Parser.assert(source, i, ")") + SyntaxError.assert(source, i, ")") } } - i += 1 + i++ } else -> { val monomialResult = monomialParser(source, i) @@ -152,6 +159,7 @@ object Tools { }), i - offset) } + // [+-><](a*n^b) val combinatorParser = Parser(combinatorOperatorParser.prefix) { source, offset, _ -> var i = offset val operatorResult = combinatorOperatorParser(source, i) @@ -171,7 +179,7 @@ object Tools { val attrOperatorParser = Parser("> var i = offset - Parser.assert(source, i, prefix) + SyntaxError.assert(source, i, prefix) val attrOperator = when (source[i]) { '=' -> { @@ -212,30 +220,30 @@ object Tools { } '!' -> { i++ - Parser.assert(source, i, '=') + SyntaxError.assert(source, i, "=") i++ PropertySelector.Operator.NotEqual } '*' -> { i++ - Parser.assert(source, i, '=') + SyntaxError.assert(source, i, "=") i++ PropertySelector.Operator.Include } '^' -> { i++ - Parser.assert(source, i, '=') + SyntaxError.assert(source, i, "=") i++ PropertySelector.Operator.Start } '$' -> { i++ - Parser.assert(source, i, '=') + SyntaxError.assert(source, i, "=") i++ PropertySelector.Operator.End } else -> { - Parser.throwError(source, i, prefix) + SyntaxError.throwError(source, i, prefix) } } ParserResult(attrOperator, i - offset) @@ -243,20 +251,20 @@ object Tools { val stringParser = Parser("`") { source, offset, prefix -> var i = offset - Parser.assert(source, i, prefix) + SyntaxError.assert(source, i, prefix) i++ var data = "" while (source[i] != '`') { if (i == source.length - 1) { - Parser.assert(source, i, '`') + SyntaxError.assert(source, i, "`") break } if (source[i] == '\\') { i++ - Parser.assert(source, i) + SyntaxError.assert(source, i) if (source[i] == '`') { data += source[i] - Parser.assert(source, i + 1) + SyntaxError.assert(source, i + 1) } else { data += '\\' + source[i].toString() } @@ -271,12 +279,12 @@ object Tools { val numberParser = Parser("1234567890.") { source, offset, prefix -> var i = offset - Parser.assert(source, i, prefix) + SyntaxError.assert(source, i, prefix) var value = "" value = if (source[i] == '.') { value += source[i] i++ - Parser.assert(source, i, "1234567890") + SyntaxError.assert(source, i, "1234567890") while ("1234567890".contains(source[i])) { value += source[i] i++ @@ -316,40 +324,49 @@ object Tools { ParserResult(data, i - offset) } + val propertyParser = + Parser("1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_") { source, offset, prefix -> + var i = offset + SyntaxError.assert(source, i, prefix) + var data = source[i].toString() + i++ + while (i < source.length) { + if (!prefix.contains(source[i])) { + break + } + data += source[i] + i++ + } + + ParserResult(data, i - offset) + } + val valueParser = Parser("tfn`1234567890.") { source, offset, prefix -> var i = offset - Parser.assert(source, i, prefix) + SyntaxError.assert(source, i, prefix) val value: Any? = when (source[i]) { 't' -> { i++ - Parser.assert(source, i, 'r') - i++ - Parser.assert(source, i, 'u') - i++ - Parser.assert(source, i, 'e') - i++ + "rue".forEach { c -> + SyntaxError.assert(source, i, c.toString()) + i++ + } true } 'f' -> { i++ - Parser.assert(source, i, 'a') - i++ - Parser.assert(source, i, 'l') - i++ - Parser.assert(source, i, 's') - i++ - Parser.assert(source, i, 'e') - i++ + "alse".forEach { c -> + SyntaxError.assert(source, i, c.toString()) + i++ + } false } 'n' -> { i++ - Parser.assert(source, i, 'u') - i++ - Parser.assert(source, i, 'l') - i++ - Parser.assert(source, i, 'l') - i++ + "ull".forEach { c -> + SyntaxError.assert(source, i, c.toString()) + i++ + } null } '`' -> { @@ -363,7 +380,7 @@ object Tools { n.data } else -> { - Parser.throwError(source, i, prefix) + SyntaxError.throwError(source, i, prefix) } } ParserResult(value, i - offset) @@ -371,27 +388,32 @@ object Tools { val attrParser = Parser("[") { source, offset, prefix -> var i = offset - Parser.assert(source, i, prefix) + SyntaxError.assert(source, i, prefix) i++ - val nameResult = nameParser(source, i) - i += nameResult.length + val parserResult = propertyParser(source, i) + i += parserResult.length val operatorResult = attrOperatorParser(source, i) i += operatorResult.length val valueResult = valueParser(source, i) i += valueResult.length - Parser.assert(source, i, ']') + SyntaxError.assert(source, i, "]") i++ ParserResult( PropertySelector.BinaryExpression( - nameResult.data, + parserResult.data, operatorResult.data, valueResult.data ), i - offset ) } - val selectorParser = Parser(nameParser.prefix) { source, offset, _ -> + val selectorParser = Parser { source, offset, _ -> var i = offset + var match = false + if (source.getOrNull(i) == '@') { + match = true + i++ + } val nameResult = nameParser(source, i) i += nameResult.length val attrList = mutableListOf() @@ -400,7 +422,10 @@ object Tools { i += attrResult.length attrList.add(attrResult.data) } - ParserResult(PropertySelector(nameResult.data, attrList), i - offset) + if (nameResult.length == 0 && attrList.size == 0) { + SyntaxError.throwError(source, i, "[") + } + ParserResult(PropertySelector(match, nameResult.data, attrList), i - offset) } val combinatorSelectorParser = Parser { source, offset, _ -> @@ -426,72 +451,26 @@ object Tools { ParserResult(topSelector.data to selectorList, i - offset) } - val unitParser = Parser { source, offset, _ -> - var i = offset - Parser.assert(source, i, "%") - i++ - ParserResult(Position.Unit.Percentage, i - offset) - } - - val numberUnitParser = Parser { source, offset, _ -> - var i = offset - val valueResult = numberParser(source, i) - i += valueResult.length - val unitResult = unitParser(source, i) - i += unitResult.length - ParserResult(Position.NumberUnit(valueResult.data, unitResult.data), i - offset) - } - - val positionParser = Parser("(") { source, offset, _ -> - var i = offset - Parser.assert(source, i, '(') - i++ - i += whiteCharParser(source, i).length - val xNumberUnitResult = numberUnitParser(source, i) - i += xNumberUnitResult.length - i += whiteCharParser(source, i).length - Parser.assert(source, i, ',') - i++ - i += whiteCharParser(source, i).length - val yNumberUnitResult = numberUnitParser(source, i) - i += yNumberUnitResult.length - i += whiteCharParser(source, i).length - Parser.assert(source, i, ')') - i++ - ParserResult(Position(xNumberUnitResult.data, yNumberUnitResult.data), i - offset) - } - val endParser = Parser { source, offset, _ -> if (offset != source.length) { - Parser.throwError(source, offset, null) + SyntaxError.throwError(source, offset, "end") } ParserResult(Unit, 0) } - val combinatorPositionSelectorParser = Parser { source, offset, _ -> - var i = offset + val gkdSelectorParser: (String) -> GkdSelector = { source -> + var i = 0 i += whiteCharParser(source, i).length val combinatorSelectorResult = combinatorSelectorParser(source, i) i += combinatorSelectorResult.length - val position = - if (i < source.length && positionParser.prefix.contains(source[i])) { - val positionResult = positionParser(source, i) - i += positionResult.length - positionResult.data - } else { - null - } + i += whiteCharParser(source, i).length i += endParser(source, i).length - ParserResult(Pair(combinatorSelectorResult.data, position), i - offset) - } - - val gkdSelectorParser: (String) -> GkdSelector = { source -> - val (data) = combinatorPositionSelectorParser(source, 0) + val data = combinatorSelectorResult.data val propertySelectorList = mutableListOf() val combinatorSelectorList = mutableListOf() - propertySelectorList.add(data.first.first) - data.first.second.forEach { + propertySelectorList.add(data.first) + data.second.forEach { propertySelectorList.add(it.second) combinatorSelectorList.add(it.first) } @@ -499,9 +478,10 @@ object Tools { combinatorSelectorList.forEachIndexed { index, combinatorSelector -> val combinatorSelectorWrapper = CombinatorSelectorWrapper(combinatorSelector, wrapperList.last()) - val propertySelectorWrapper = PropertySelectorWrapper(propertySelectorList[index + 1], combinatorSelectorWrapper) + val propertySelectorWrapper = + PropertySelectorWrapper(propertySelectorList[index + 1], combinatorSelectorWrapper) wrapperList.add(propertySelectorWrapper) } - GkdSelector(wrapperList.last(), data.second) + GkdSelector(wrapperList.last()) } } \ No newline at end of file diff --git a/node_selector/src/main/java/li/songe/node_selector/selector/CombinatorSelector.kt b/selector/src/main/java/li/songe/selector/selector/CombinatorSelector.kt similarity index 98% rename from node_selector/src/main/java/li/songe/node_selector/selector/CombinatorSelector.kt rename to selector/src/main/java/li/songe/selector/selector/CombinatorSelector.kt index 97ae38c..5a1614b 100644 --- a/node_selector/src/main/java/li/songe/node_selector/selector/CombinatorSelector.kt +++ b/selector/src/main/java/li/songe/selector/selector/CombinatorSelector.kt @@ -1,4 +1,4 @@ -package li.songe.node_selector.selector +package li.songe.selector.selector /** * 关系连接选择器 diff --git a/selector/src/main/java/li/songe/selector/selector/PropertySelector.kt b/selector/src/main/java/li/songe/selector/selector/PropertySelector.kt new file mode 100644 index 0000000..7035c05 --- /dev/null +++ b/selector/src/main/java/li/songe/selector/selector/PropertySelector.kt @@ -0,0 +1,350 @@ +package li.songe.selector.selector + +import android.view.accessibility.AccessibilityNodeInfo +import li.songe.selector.getDepth +import li.songe.selector.getIndex + +/** + * 属性选择器 + */ +data class PropertySelector( + val match: Boolean, + val name: String, + val expressionList: List +) { + override fun toString() = "${if (match) "@" else ""}${name}${expressionList.joinToString("")}" + sealed class Operator(private val key: String) { + + object More : Operator(">") + + object Less : Operator("<") + + object Equal : Operator("=") + + object NotEqual : Operator("!=") + + object MoreEqual : Operator(">=") + + object LessEqual : Operator("<=") + + object Include : Operator("*=") + + object Start : Operator("^=") + + object End : Operator("$=") + + override fun toString() = key + } + + data class BinaryExpression(val name: String, val operator: Operator, val value: Any?) { + + override fun toString() = "[${name}${operator}${ + if (value is String) { + "`${value.replace("`", "\\`")}`" + } else { + value + } + }]" + + val matchNodeInfo: (nodeInfo: AccessibilityNodeInfo) -> Boolean = when (operator) { + Operator.Start -> when (name) { + "id" -> when (value) { + is String -> ({ nodeInfo -> nodeInfo.viewIdResourceName?.startsWith(value) == true }) + else -> ({ false }) + } + "text" -> when (value) { + is String -> ({ nodeInfo -> nodeInfo.text?.startsWith(value) == true }) + else -> ({ false }) + } + "hintText" -> when (value) { + is String -> ({ nodeInfo -> nodeInfo.hintText?.startsWith(value) == true }) + else -> ({ false }) + } + "contentDesc" -> when (value) { + is String -> ({ nodeInfo -> nodeInfo.contentDescription?.startsWith(value) == true }) + else -> ({ false }) + } + "className" -> when (value) { + is String -> ({ nodeInfo -> nodeInfo.className?.startsWith(value) == true }) + else -> ({ false }) + } + else -> ({ false }) + } + Operator.Include -> when (name) { + "id" -> when (value) { + is String -> ({ nodeInfo -> nodeInfo.viewIdResourceName?.contains(value) == true }) + else -> ({ false }) + } + "text" -> when (value) { + is String -> ({ nodeInfo -> nodeInfo.text?.contains(value) == true }) + else -> ({ false }) + } + "hintText" -> when (value) { + is String -> ({ nodeInfo -> nodeInfo.hintText?.contains(value) == true }) + else -> ({ false }) + } + "contentDesc" -> when (value) { + is String -> ({ nodeInfo -> nodeInfo.contentDescription?.contains(value) == true }) + else -> ({ false }) + } + "className" -> when (value) { + is String -> ({ nodeInfo -> nodeInfo.className?.contains(value) == true }) + else -> ({ false }) + } + else -> ({ false }) + } + Operator.End -> { + when (name) { + "id" -> when (value) { + is String -> ({ nodeInfo -> nodeInfo.viewIdResourceName?.endsWith(value) == true }) + else -> ({ false }) + } + "text" -> when (value) { + is String -> ({ nodeInfo -> nodeInfo.text?.endsWith(value) == true }) + else -> ({ false }) + } + "hintText" -> when (value) { + is String -> ({ nodeInfo -> nodeInfo.hintText?.endsWith(value) == true }) + else -> ({ false }) + } + "contentDesc" -> when (value) { + is String -> ({ nodeInfo -> nodeInfo.contentDescription?.endsWith(value) == true }) + else -> ({ false }) + } + "className" -> when (value) { + is String -> ({ nodeInfo -> nodeInfo.className?.endsWith(value) == true }) + else -> ({ false }) + } + else -> ({ false }) + } + } + Operator.Less -> when (name) { + "index" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.getIndex()?.let { it < value } == true }) + else -> ({ false }) + } + "childCount" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.childCount < value }) + else -> ({ false }) + } + "depth" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.getDepth() < value }) + else -> ({ false }) + } + "text.length" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.text?.length?.let { it < value } == true }) + else -> ({ false }) + } + "hintText.length" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.hintText?.length?.let { it < value } == true }) + else -> ({ false }) + } + "contentDesc.length" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.contentDescription?.length?.let { it < value } == true }) + else -> ({ false }) + } + "className.length" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.className?.length?.let { it < value } == true }) + else -> ({ false }) + } + else -> ({ false }) + } + Operator.LessEqual -> when (name) { + "index" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.getIndex()?.let { it <= value } == true }) + else -> ({ false }) + } + "childCount" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.childCount <= value }) + else -> ({ false }) + } + "depth" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.getDepth() <= value }) + else -> ({ false }) + } + "text.length" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.text?.length?.let { it <= value } == true }) + else -> ({ false }) + } + "hintText.length" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.hintText?.length?.let { it <= value } == true }) + else -> ({ false }) + } + "contentDesc.length" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.contentDescription?.length?.let { it <= value } == true }) + else -> ({ false }) + } + "className.length" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.className?.length?.let { it <= value } == true }) + else -> ({ false }) + } + else -> ({ false }) + } + Operator.More -> when (name) { + "index" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.getIndex()?.let { it > value } == true }) + else -> ({ false }) + } + "childCount" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.childCount > value }) + else -> ({ false }) + } + "depth" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.getDepth() > value }) + else -> ({ false }) + } + "text.length" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.text?.length?.let { it > value } == true }) + else -> ({ false }) + } + "hintText.length" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.hintText?.length?.let { it > value } == true }) + else -> ({ false }) + } + "contentDesc.length" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.contentDescription?.length?.let { it > value } == true }) + else -> ({ false }) + } + "className.length" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.className?.length?.let { it > value } == true }) + else -> ({ false }) + } + else -> ({ false }) + } + Operator.MoreEqual -> when (name) { + "index" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.getIndex()?.let { it >= value } == true }) + else -> ({ false }) + } + "childCount" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.childCount >= value }) + else -> ({ false }) + } + "depth" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.getDepth() >= value }) + else -> ({ false }) + } + "text.length" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.text?.length?.let { it >= value } == true }) + else -> ({ false }) + } + "hintText.length" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.hintText?.length?.let { it >= value } == true }) + else -> ({ false }) + } + "contentDesc.length" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.contentDescription?.length?.let { it >= value } == true }) + else -> ({ false }) + } + "className.length" -> when (value) { + is Int -> ({ nodeInfo -> nodeInfo.className?.length?.let { it >= value } == true }) + else -> ({ false }) + } + else -> ({ false }) + } + Operator.Equal -> when (name) { + "index" -> when (value) { + is Int? -> ({ nodeInfo -> nodeInfo.getIndex() == value }) + else -> ({ false }) + } + "childCount" -> when (value) { + is Int? -> ({ nodeInfo -> nodeInfo.getIndex() == value }) + else -> ({ false }) + } + "depth" -> when (value) { + is Int? -> ({ nodeInfo -> nodeInfo.getDepth() == value }) + else -> ({ false }) + } + "text" -> when (value) { + is String? -> ({ nodeInfo -> nodeInfo.text == value }) + else -> ({ false }) + } + "text.length" -> when (value) { + is Int? -> ({ nodeInfo -> nodeInfo.text?.length == value }) + else -> ({ false }) + } + "hintText" -> when (value) { + is String? -> ({ nodeInfo -> nodeInfo.hintText == value }) + else -> ({ false }) + } + "hintText.length" -> when (value) { + is Int? -> ({ nodeInfo -> nodeInfo.hintText?.length == value }) + else -> ({ false }) + } + "contentDesc" -> when (value) { + is String? -> ({ nodeInfo -> nodeInfo.contentDescription == value }) + else -> ({ false }) + } + "contentDesc.length" -> when (value) { + is Int? -> ({ nodeInfo -> nodeInfo.contentDescription?.length == value }) + else -> ({ false }) + } + "isPassword" -> when (value) { + is Boolean? -> ({ nodeInfo -> nodeInfo.isPassword == value }) + else -> ({ false }) + } + "isChecked" -> when (value) { + is Boolean? -> ({ nodeInfo -> nodeInfo.isChecked == value }) + else -> ({ false }) + } + "className" -> when (value) { + is String? -> ({ nodeInfo -> nodeInfo.className == value }) + else -> ({ false }) + } + else -> ({ false }) + } + Operator.NotEqual -> when (name) { + "index" -> when (value) { + is Int? -> ({ nodeInfo -> nodeInfo.getIndex() != value }) + else -> ({ false }) + } + "childCount" -> when (value) { + is Int? -> ({ nodeInfo -> nodeInfo.getIndex() != value }) + else -> ({ false }) + } + "depth" -> when (value) { + is Int? -> ({ nodeInfo -> nodeInfo.getDepth() != value }) + else -> ({ false }) + } + "text" -> when (value) { + is String? -> ({ nodeInfo -> nodeInfo.text != value }) + else -> ({ false }) + } + "text.length" -> when (value) { + is Int? -> ({ nodeInfo -> nodeInfo.text?.length != value }) + else -> ({ false }) + } + "hintText" -> when (value) { + is String? -> ({ nodeInfo -> nodeInfo.hintText != value }) + else -> ({ false }) + } + "hintText.length" -> when (value) { + is Int? -> ({ nodeInfo -> nodeInfo.hintText?.length != value }) + else -> ({ false }) + } + "contentDesc" -> when (value) { + is String? -> ({ nodeInfo -> nodeInfo.contentDescription != value }) + else -> ({ false }) + } + "contentDesc.length" -> when (value) { + is Int? -> ({ nodeInfo -> nodeInfo.contentDescription?.length != value }) + else -> ({ false }) + } + "isPassword" -> when (value) { + is Boolean? -> ({ nodeInfo -> nodeInfo.isPassword != value }) + else -> ({ false }) + } + "isChecked" -> when (value) { + is Boolean? -> ({ nodeInfo -> nodeInfo.isChecked != value }) + else -> ({ false }) + } + "className" -> when (value) { + is String? -> ({ nodeInfo -> nodeInfo.className != value }) + else -> ({ false }) + } + else -> ({ false }) + } + } + } + +} diff --git a/node_selector/src/main/java/li/songe/node_selector/wrapper/CombinatorSelectorWrapper.kt b/selector/src/main/java/li/songe/selector/wrapper/CombinatorSelectorWrapper.kt similarity index 88% rename from node_selector/src/main/java/li/songe/node_selector/wrapper/CombinatorSelectorWrapper.kt rename to selector/src/main/java/li/songe/selector/wrapper/CombinatorSelectorWrapper.kt index fd49b03..885779d 100644 --- a/node_selector/src/main/java/li/songe/node_selector/wrapper/CombinatorSelectorWrapper.kt +++ b/selector/src/main/java/li/songe/selector/wrapper/CombinatorSelectorWrapper.kt @@ -1,8 +1,8 @@ -package li.songe.node_selector.wrapper +package li.songe.selector.wrapper import android.view.accessibility.AccessibilityNodeInfo -import li.songe.node_selector.* -import li.songe.node_selector.selector.CombinatorSelector +import li.songe.selector.* +import li.songe.selector.selector.CombinatorSelector data class CombinatorSelectorWrapper( private val combinatorSelector: CombinatorSelector, @@ -12,7 +12,10 @@ data class CombinatorSelectorWrapper( return to.toString() + "\u0020" + combinatorSelector.toString() } - fun match(nodeInfo: AccessibilityNodeInfo): Boolean { + fun match( + nodeInfo: AccessibilityNodeInfo, + trackList: MutableList + ): Boolean { val expression = combinatorSelector.polynomialExpression val isConstant = expression.isConstant when (combinatorSelector.operator) { @@ -22,9 +25,9 @@ data class CombinatorSelectorWrapper( if (constantValue > 0) { nodeInfo.traverseAncestor { depth, ancestorNode -> if (depth == constantValue) { - val targetNode = to.match(ancestorNode) + val targetNode = to.match(ancestorNode, trackList) if (targetNode) { - return targetNode + return true } return@traverseAncestor true } @@ -46,9 +49,9 @@ data class CombinatorSelectorWrapper( nodeInfo.traverseAncestor { depth, ancestorNode -> if (set.contains(depth)) { set.remove(depth) - val targetNode = to.match(ancestorNode) + val targetNode = to.match(ancestorNode, trackList) if (targetNode) { - return targetNode + return true } } } @@ -60,7 +63,7 @@ data class CombinatorSelectorWrapper( if (0 < constantValue && constantValue <= nodeInfo.childCount) { val childNode: AccessibilityNodeInfo? = nodeInfo.getChild(constantValue - 1) if (childNode != null) { - return to.match(childNode) + return to.match(childNode, trackList) } } } else { @@ -77,9 +80,9 @@ data class CombinatorSelectorWrapper( nodeInfo.forEachIndexed { index, childNode -> if (set.contains(index)) { set.remove(index) - val targetNode = to.match(childNode) + val targetNode = to.match(childNode, trackList) if (targetNode) { - return targetNode + return true } } } @@ -92,9 +95,9 @@ data class CombinatorSelectorWrapper( if (constantValue in 1..i) { nodeInfo.traverseElderBrother { offset, brotherNode -> if (offset == constantValue) { - val targetNode = to.match(brotherNode) + val targetNode = to.match(brotherNode, trackList) if (targetNode) { - return targetNode + return true } return@traverseElderBrother true } @@ -115,9 +118,9 @@ data class CombinatorSelectorWrapper( nodeInfo.traverseElderBrother { offset, brotherNode -> if (set.contains(offset)) { set.remove(offset) - val targetNode = to.match(brotherNode) + val targetNode = to.match(brotherNode, trackList) if (targetNode) { - return targetNode + return true } } } @@ -130,9 +133,9 @@ data class CombinatorSelectorWrapper( if (0 < constantValue && i + constantValue < nodeInfo.parent.childCount) { nodeInfo.traverseYoungerBrother { offset, brotherNode -> if (offset == constantValue) { - val targetNode = to.match(brotherNode) + val targetNode = to.match(brotherNode, trackList) if (targetNode) { - return targetNode + return true } return@traverseYoungerBrother true } @@ -154,9 +157,9 @@ data class CombinatorSelectorWrapper( nodeInfo.traverseYoungerBrother { offset, brotherNode -> if (set.contains(offset)) { set.remove(offset) - val targetNode = to.match(brotherNode) + val targetNode = to.match(brotherNode, trackList) if (targetNode) { - return targetNode + return true } } } diff --git a/selector/src/main/java/li/songe/selector/wrapper/PropertySelectorWrapper.kt b/selector/src/main/java/li/songe/selector/wrapper/PropertySelectorWrapper.kt new file mode 100644 index 0000000..303958b --- /dev/null +++ b/selector/src/main/java/li/songe/selector/wrapper/PropertySelectorWrapper.kt @@ -0,0 +1,51 @@ +package li.songe.selector.wrapper + +import android.view.accessibility.AccessibilityNodeInfo +import li.songe.selector.selector.PropertySelector + +data class PropertySelectorWrapper( + private val propertySelector: PropertySelector, + val to: CombinatorSelectorWrapper? = null +) { + override fun toString(): String { + return (if (to != null) { + to.toString() + "\u0020" + } else { + "" + }) + propertySelector.toString() + } + + fun match( + nodeInfo: AccessibilityNodeInfo, + trackList: MutableList + ): Boolean { + if (propertySelector.name != "*" || propertySelector.name.isNotEmpty()) { + val className = nodeInfo.className.toString() + if (!((className.endsWith(propertySelector.name) && + className[className.length - propertySelector.name.length - 1] == '.') + || className == propertySelector.name + ) + ) { + return false + } + } + propertySelector.expressionList.forEach { expression -> + if (!expression.matchNodeInfo(nodeInfo)) { + return false + } + } +// 属性匹配单元 完全匹配 之后 + + if (propertySelector.match || trackList.isEmpty()) { + trackList.add(nodeInfo) + } else { + trackList.add(null) + } + if (to == null) { + return true + } + return to.match(nodeInfo, trackList) + } + + +} diff --git a/node_selector/src/test/java/li/songe/node_selector/ExampleUnitTest.kt b/selector/src/test/java/li/songe/selector/ExampleUnitTest.kt similarity index 84% rename from node_selector/src/test/java/li/songe/node_selector/ExampleUnitTest.kt rename to selector/src/test/java/li/songe/selector/ExampleUnitTest.kt index 5e1d650..218fbee 100644 --- a/node_selector/src/test/java/li/songe/node_selector/ExampleUnitTest.kt +++ b/selector/src/test/java/li/songe/selector/ExampleUnitTest.kt @@ -1,6 +1,6 @@ -package li.songe.node_selector +package li.songe.selector -import li.songe.node_selector.parser.Tools.gkdSelectorParser +import li.songe.selector.parser.Transform.gkdSelectorParser import org.json.JSONObject import org.junit.Test @@ -28,7 +28,7 @@ class ExampleUnitTest { @Test fun check_property() { - val source = "View[k^=.3] <2 P + Z - G <2 P[k=1][k==2] T Sing V > V(1.%, 50%)" + val source = "View[k^=.3] <2 @P + Z - G <2 P[k=1][k==2] T Sing V > V" println("source:$source") // val result = combinatorPositionSelectorParser(source, 0) // println("first:"+result.data.first.first) diff --git a/settings.gradle.kts b/settings.gradle.kts index facf64a..54573a9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,7 +20,7 @@ pluginManagement { rootProject.name = "gkd" include(":app") -include(":node_selector") +include(":selector") include(":room_processor") //include(":shizuku_automator") //include(":shizuku_automator:hidden_api")