mirror of
https://github.com/gkd-kit/gkd.git
synced 2026-09-03 07:19:59 +08:00
refactor: extract database module
This commit is contained in:
@@ -16,12 +16,10 @@ val debugSuffixResources = project.readDebugSuffixResources()
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.androidx.room)
|
||||
alias(libs.plugins.kotlin.parcelize)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
alias(libs.plugins.kotlinx.atomicfu)
|
||||
alias(libs.plugins.google.ksp)
|
||||
alias(libs.plugins.remap)
|
||||
alias(libs.plugins.loc)
|
||||
}
|
||||
@@ -195,11 +193,6 @@ if (buildProperty("GKD_RENAME_APK_FLAG").isPresent) {
|
||||
}
|
||||
}
|
||||
|
||||
// https://developer.android.com/jetpack/androidx/releases/room?hl=zh-cn#compiler-options
|
||||
room {
|
||||
schemaDirectory("$projectDir/schemas")
|
||||
}
|
||||
|
||||
composeCompiler {
|
||||
if (providers.gradleProperty("composeReports").isPresent) {
|
||||
reportsDestination = layout.buildDirectory.dir("compose_compiler")
|
||||
@@ -216,6 +209,7 @@ loc {
|
||||
dependencies {
|
||||
implementation(libs.kotlin.stdlib)
|
||||
|
||||
implementation(project(":gkd-db"))
|
||||
implementation(project(":gkd-selector"))
|
||||
|
||||
implementation(libs.androidx.appcompat)
|
||||
@@ -253,11 +247,6 @@ dependencies {
|
||||
implementation(libs.priv.kit.ui)
|
||||
implementation(libs.lsposed.hiddenapibypass)
|
||||
|
||||
implementation(libs.androidx.room.runtime)
|
||||
implementation(libs.androidx.room.ktx)
|
||||
implementation(libs.androidx.room.paging)
|
||||
ksp(libs.androidx.room.compiler)
|
||||
|
||||
implementation(libs.androidx.paging.runtime)
|
||||
implementation(libs.androidx.paging.compose)
|
||||
|
||||
|
||||
@@ -41,12 +41,14 @@ import li.gkd.app.store.initStore
|
||||
import li.gkd.app.util.AndroidTarget
|
||||
import li.gkd.app.util.LogUtils
|
||||
import li.gkd.app.util.PKG_FLAGS
|
||||
import li.gkd.app.util.dbFolder
|
||||
import li.gkd.app.util.deviceInfoDesc
|
||||
import li.gkd.app.util.initAppState
|
||||
import li.gkd.app.util.SubscriptionStore
|
||||
import li.gkd.app.util.initToast
|
||||
import li.gkd.app.util.launchTry
|
||||
import li.gkd.app.util.toast
|
||||
import li.gkd.db.Db
|
||||
import org.lsposed.hiddenapibypass.HiddenApiBypass
|
||||
import priv.kit.core.PrivilegeConfig
|
||||
import priv.kit.ui.PrivilegeUi
|
||||
@@ -221,6 +223,7 @@ class App : Application() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Db.initialize(this, dbFolder.resolve("gkd.db").absolutePath)
|
||||
LogUtils.d()
|
||||
Thread.setDefaultUncaughtExceptionHandler { t, e ->
|
||||
toast(e.message ?: e.toString())
|
||||
|
||||
@@ -18,7 +18,7 @@ import li.gkd.app.a11y.useA11yServiceEnabledFlow
|
||||
import li.gkd.app.a11y.useEnabledA11yServicesFlow
|
||||
import li.gkd.app.data.CrashData
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.entry.EntryActivity
|
||||
import li.gkd.app.entry.OpenFileActivity
|
||||
import li.gkd.app.priv.AutomationService
|
||||
@@ -158,8 +158,8 @@ class MainViewModel : BaseViewModel(), OnSimpleLife by DefaultSimpleLifeImpl() {
|
||||
|
||||
val subsSheet = SubsSheetState()
|
||||
|
||||
val appOrderListState = DbSet.actionLogDao.queryLatestUniqueAppIds().stateLoadable()
|
||||
val appVisitOrderMapState = DbSet.appVisitLogDao.query().map {
|
||||
val appOrderListState = Db.actionLogDao.queryLatestUniqueAppIds().stateLoadable()
|
||||
val appVisitOrderMapState = Db.appVisitLogDao.query().map {
|
||||
it.mapIndexed { i, appId -> appId to i }.toMap()
|
||||
}.debounce(500).stateLoadable()
|
||||
|
||||
|
||||
@@ -13,15 +13,16 @@ import kotlinx.coroutines.sync.withLock
|
||||
import li.gkd.app.META
|
||||
import li.gkd.app.app
|
||||
import li.gkd.app.appScope
|
||||
import li.gkd.app.data.ActionLog
|
||||
import li.gkd.db.ActionLog
|
||||
import li.gkd.app.data.ActionResult
|
||||
import li.gkd.app.data.ActivityLog
|
||||
import li.gkd.db.ActivityLog
|
||||
import li.gkd.app.data.AttrInfo
|
||||
import li.gkd.app.data.ResetMatchType
|
||||
import li.gkd.app.data.ResolvedRule
|
||||
import li.gkd.app.data.RuleStatus
|
||||
import li.gkd.app.data.insert
|
||||
import li.gkd.app.data.isSystem
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.service.updateTopTaskAppId
|
||||
import li.gkd.app.store.actionCountFlow
|
||||
import li.gkd.app.store.checkAppBlockMatch
|
||||
@@ -187,11 +188,11 @@ fun updateTopActivity(
|
||||
val logs = tempActivityLogList.toTypedArray()
|
||||
tempActivityLogList.clear()
|
||||
appScope.launchTry {
|
||||
DbSet.activityLogDao.insert(*logs)
|
||||
Db.activityLogDao.insert(*logs)
|
||||
}
|
||||
}
|
||||
if (activityLogCount++ % 100 == 0) {
|
||||
appScope.launchTry { DbSet.activityLogDao.deleteKeepLatest() }
|
||||
appScope.launchTry { Db.activityLogDao.deleteKeepLatest() }
|
||||
}
|
||||
val topActivity = topActivityFlow.value
|
||||
val ruleSummary = ruleSummaryFlow.value
|
||||
@@ -206,7 +207,7 @@ fun updateTopActivity(
|
||||
val oldAppId = lastAppId
|
||||
lastAppId = appId
|
||||
appScope.launchTry {
|
||||
DbSet.appVisitLogDao.insert(oldAppId, appId, t)
|
||||
Db.appVisitLogDao.insert(oldAppId, appId, t)
|
||||
}
|
||||
appChangeTime = t
|
||||
ruleSummary.globalRules.forEach { it.resetState(t) }
|
||||
@@ -297,9 +298,9 @@ fun addActionLog(
|
||||
ruleKey = rule.key,
|
||||
ctime = ctime,
|
||||
)
|
||||
DbSet.actionLogDao.insert(actionLog)
|
||||
Db.actionLogDao.insert(actionLog)
|
||||
if (actionCountFlow.value % 100 == 0L) {
|
||||
DbSet.actionLogDao.deleteKeepLatest()
|
||||
Db.actionLogDao.deleteKeepLatest()
|
||||
}
|
||||
}
|
||||
LogUtils.d(
|
||||
|
||||
34
gkd-app/src/main/kotlin/li/gkd/app/data/A11yEventLogExt.kt
Normal file
34
gkd-app/src/main/kotlin/li/gkd/app/data/A11yEventLogExt.kt
Normal file
@@ -0,0 +1,34 @@
|
||||
package li.gkd.app.data
|
||||
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import li.gkd.db.A11yEventLog
|
||||
|
||||
fun AccessibilityEvent.toA11yEventLog(id: Int) = A11yEventLog(
|
||||
id = id,
|
||||
ctime = System.currentTimeMillis(),
|
||||
type = eventType,
|
||||
appId = packageName.toString(),
|
||||
name = className.toString(),
|
||||
desc = contentDescription?.toString(),
|
||||
text = text.map { it.toString() },
|
||||
)
|
||||
|
||||
val A11yEventLog.isStateChanged: Boolean
|
||||
get() = type == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
|
||||
|
||||
val A11yEventLog.fixedName: String
|
||||
get() {
|
||||
if (isStateChanged && name.startsWith(appId)) {
|
||||
return name.substring(appId.length)
|
||||
}
|
||||
if (name.contains("View") || name.contains("Layout") || viewSuffixes.any(name::startsWith)) {
|
||||
return name.substring(name.lastIndexOf('.') + 1)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
private val viewSuffixes = listOf(
|
||||
"android.widget.",
|
||||
"android.view.",
|
||||
"android.support.",
|
||||
)
|
||||
11
gkd-app/src/main/kotlin/li/gkd/app/data/ActionLogExt.kt
Normal file
11
gkd-app/src/main/kotlin/li/gkd/app/data/ActionLogExt.kt
Normal file
@@ -0,0 +1,11 @@
|
||||
package li.gkd.app.data
|
||||
|
||||
import li.gkd.app.util.format
|
||||
import li.gkd.app.util.getShowActivityId
|
||||
import li.gkd.db.ActionLog
|
||||
|
||||
val ActionLog.showActivityId: String?
|
||||
get() = getShowActivityId(appId, activityId)
|
||||
|
||||
val ActionLog.date: String
|
||||
get() = ctime.format("MM-dd HH:mm:ss SSS")
|
||||
11
gkd-app/src/main/kotlin/li/gkd/app/data/ActivityLogExt.kt
Normal file
11
gkd-app/src/main/kotlin/li/gkd/app/data/ActivityLogExt.kt
Normal file
@@ -0,0 +1,11 @@
|
||||
package li.gkd.app.data
|
||||
|
||||
import li.gkd.app.util.format
|
||||
import li.gkd.app.util.getShowActivityId
|
||||
import li.gkd.db.ActivityLog
|
||||
|
||||
val ActivityLog.showActivityId: String?
|
||||
get() = getShowActivityId(appId, activityId)
|
||||
|
||||
val ActivityLog.date: String
|
||||
get() = ctime.format("HH:mm:ss SSS")
|
||||
27
gkd-app/src/main/kotlin/li/gkd/app/data/AppVisitLogExt.kt
Normal file
27
gkd-app/src/main/kotlin/li/gkd/app/data/AppVisitLogExt.kt
Normal file
@@ -0,0 +1,27 @@
|
||||
package li.gkd.app.data
|
||||
|
||||
import li.gkd.app.META
|
||||
import li.gkd.app.a11y.launcherAppId
|
||||
import li.gkd.app.util.systemUiAppId
|
||||
import li.gkd.db.AppVisitLog
|
||||
import li.gkd.db.Db
|
||||
|
||||
private var appLogCount = 0
|
||||
|
||||
suspend fun AppVisitLog.AppLogDao.insert(oldAppId: String, newAppId: String, mtime: Long) {
|
||||
Db.withTransaction {
|
||||
insert(
|
||||
AppVisitLog(oldAppId, fixAppVisitTime(oldAppId, mtime - 1)),
|
||||
AppVisitLog(newAppId, fixAppVisitTime(newAppId, mtime)),
|
||||
)
|
||||
if (appLogCount++ % 100 == 0) {
|
||||
deleteKeepLatest()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fixAppVisitTime(appId: String, time: Long): Long = when (appId) {
|
||||
META.appId -> time - 120_000
|
||||
launcherAppId, systemUiAppId -> time - 60_000
|
||||
else -> time
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package li.gkd.app.data
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import li.gkd.app.util.appInfoMapFlow
|
||||
import li.gkd.db.BaseSnapshot
|
||||
import li.gkd.db.Snapshot
|
||||
|
||||
@Serializable
|
||||
data class ComplexSnapshot(
|
||||
|
||||
104
gkd-app/src/main/kotlin/li/gkd/app/data/ExcludeData.kt
Normal file
104
gkd-app/src/main/kotlin/li/gkd/app/data/ExcludeData.kt
Normal file
@@ -0,0 +1,104 @@
|
||||
package li.gkd.app.data
|
||||
|
||||
import li.gkd.app.util.isValidActivityId
|
||||
import li.gkd.app.util.isValidAppId
|
||||
|
||||
data class ExcludeData(
|
||||
val appIds: Map<String, Boolean>,
|
||||
val activityIds: Set<Pair<String, String>>,
|
||||
) {
|
||||
val excludeAppIds = appIds.entries.filter { entry -> entry.value }.map { entry -> entry.key }.toHashSet()
|
||||
val includeAppIds = appIds.entries.filter { entry -> !entry.value }.map { entry -> entry.key }.toHashSet()
|
||||
|
||||
fun stringify(appId: String? = null): String {
|
||||
return if (appId != null) {
|
||||
activityIds.filter { entry -> entry.first == appId }.map { entry -> entry.second }.sorted()
|
||||
.joinToString("\n\n")
|
||||
} else {
|
||||
(appIds.entries.map { entry ->
|
||||
if (entry.value) {
|
||||
entry.key
|
||||
} else {
|
||||
"!${entry.key}"
|
||||
}
|
||||
} + activityIds.map { entry -> "${entry.first}/${entry.second}" })
|
||||
.sorted()
|
||||
.joinToString("\n\n")
|
||||
}
|
||||
}
|
||||
|
||||
fun clear(appId: String): ExcludeData {
|
||||
return copy(
|
||||
appIds = appIds.toMutableMap().apply {
|
||||
remove(appId)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fun switch(appId: String, activityId: String? = null): ExcludeData {
|
||||
return if (activityId == null) {
|
||||
copy(
|
||||
appIds = appIds.toMutableMap().apply {
|
||||
if (get(appId) != false) {
|
||||
set(appId, false)
|
||||
} else {
|
||||
set(appId, true)
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
copy(activityIds = activityIds.toMutableSet().apply {
|
||||
val entry = appId to activityId
|
||||
if (contains(entry)) {
|
||||
remove(entry)
|
||||
} else {
|
||||
add(entry)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val empty = ExcludeData(emptyMap(), emptySet())
|
||||
|
||||
fun parse(exclude: String?): ExcludeData {
|
||||
if (exclude.isNullOrBlank()) {
|
||||
return empty
|
||||
}
|
||||
val appIds = HashMap<String, Boolean>()
|
||||
val activityIds = HashSet<Pair<String, String>>()
|
||||
exclude.split('\n')
|
||||
.filter { it.isNotBlank() }
|
||||
.forEach { value ->
|
||||
if (value[0] == '!') {
|
||||
val appId = value.substring(1)
|
||||
if (appId.isValidAppId()) {
|
||||
appIds[appId] = false
|
||||
}
|
||||
} else {
|
||||
val parts = value.split('/', limit = 2)
|
||||
val appId = parts[0]
|
||||
if (appId.isValidAppId()) {
|
||||
val activityId = parts.getOrNull(1)
|
||||
if (activityId != null) {
|
||||
if (activityId.isValidActivityId()) {
|
||||
activityIds.add(appId to activityId)
|
||||
}
|
||||
} else {
|
||||
appIds[appId] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ExcludeData(
|
||||
appIds = appIds,
|
||||
activityIds = activityIds,
|
||||
)
|
||||
}
|
||||
|
||||
fun parse(exclude: String?, appId: String): ExcludeData {
|
||||
if (exclude.isNullOrBlank()) return empty
|
||||
return parse(exclude.split('\n').joinToString("\n") { "$appId/$it" })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlinx.serialization.json.long
|
||||
import li.gkd.app.a11y.typeInfo
|
||||
import li.gkd.app.util.LOCAL_SUBS_IDS
|
||||
import li.gkd.app.util.LogUtils
|
||||
import li.gkd.app.util.ScreenUtils
|
||||
import li.gkd.app.util.appInfoMapFlow
|
||||
@@ -24,6 +23,8 @@ import li.gkd.app.util.filterIfNotAll
|
||||
import li.gkd.app.util.json
|
||||
import li.gkd.app.util.toJson5String
|
||||
import li.gkd.app.util.toast
|
||||
import li.gkd.db.LOCAL_SUBS_IDS
|
||||
import li.gkd.db.SubsConfig
|
||||
import li.songe.json5.Json5
|
||||
import li.gkd.selector.Selector
|
||||
import net.objecthunter.exp4j.Expression
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package li.gkd.app.data
|
||||
|
||||
import li.gkd.db.SubsConfig
|
||||
import li.gkd.db.SubsItem
|
||||
|
||||
sealed class ResolvedGroup(
|
||||
open val group: RawSubscription.RawGroupProps,
|
||||
val subscription: RawSubscription,
|
||||
@@ -35,4 +38,4 @@ class ResolvedGlobalGroup(
|
||||
val groupExcludeAppIds by lazy {
|
||||
subscription.globalGroupAppGroupNameDisableMap[group.key] ?: emptySet()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
gkd-app/src/main/kotlin/li/gkd/app/data/SnapshotExt.kt
Normal file
11
gkd-app/src/main/kotlin/li/gkd/app/data/SnapshotExt.kt
Normal file
@@ -0,0 +1,11 @@
|
||||
package li.gkd.app.data
|
||||
|
||||
import li.gkd.app.snapshot.SnapshotStore
|
||||
import li.gkd.app.util.format
|
||||
import li.gkd.db.Snapshot
|
||||
|
||||
val Snapshot.screenshotFile
|
||||
get() = SnapshotStore.screenshotFile(id)
|
||||
|
||||
val Snapshot.date: String
|
||||
get() = id.format("MM-dd HH:mm:ss")
|
||||
13
gkd-app/src/main/kotlin/li/gkd/app/data/SubsConfigExt.kt
Normal file
13
gkd-app/src/main/kotlin/li/gkd/app/data/SubsConfigExt.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
package li.gkd.app.data
|
||||
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.db.SubsConfig
|
||||
|
||||
suspend fun SubsConfig.SubsConfigDao.batchResetAppGroupEnable(
|
||||
subsItemId: Long,
|
||||
list: List<Pair<RawSubscription.RawAppGroup, RawSubscription.RawApp>>,
|
||||
): List<Pair<RawSubscription.RawAppGroup, RawSubscription.RawApp>> = Db.withTransaction {
|
||||
list.filter { (group, app) ->
|
||||
resetAppGroupTypeEnable(subsItemId, app.id, group.key) > 0
|
||||
}
|
||||
}
|
||||
7
gkd-app/src/main/kotlin/li/gkd/app/data/SubsItemExt.kt
Normal file
7
gkd-app/src/main/kotlin/li/gkd/app/data/SubsItemExt.kt
Normal file
@@ -0,0 +1,7 @@
|
||||
package li.gkd.app.data
|
||||
|
||||
import li.gkd.app.util.format
|
||||
import li.gkd.db.SubsItem
|
||||
|
||||
val SubsItem.mtimeStr: String
|
||||
get() = mtime.format("yyyy-MM-dd HH:mm:ss")
|
||||
@@ -36,9 +36,9 @@ import kotlinx.coroutines.launch
|
||||
import li.gkd.app.META
|
||||
import li.gkd.app.MainViewModel
|
||||
import li.gkd.app.appScope
|
||||
import li.gkd.app.data.A11yEventLog
|
||||
import li.gkd.db.A11yEventLog
|
||||
import li.gkd.app.data.toA11yEventLog
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.notif.NotificationCatalog
|
||||
import li.gkd.app.notif.StopServiceReceiver
|
||||
import li.gkd.app.permission.PermissionStates
|
||||
@@ -129,7 +129,7 @@ class EventService : OverlayWindowService(positionKey = "event") {
|
||||
delay(1000.milliseconds)
|
||||
val list = getAndUpdate { emptyList() }
|
||||
if (list.isNotEmpty()) {
|
||||
DbSet.a11yEventLogDao.insert(list)
|
||||
Db.a11yEventLogDao.insert(list)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -143,7 +143,7 @@ class EventService : OverlayWindowService(positionKey = "event") {
|
||||
logAutoId = 0
|
||||
}
|
||||
scope.launch {
|
||||
logAutoId = (DbSet.a11yEventLogDao.maxId() ?: 0).coerceAtLeast(1)
|
||||
logAutoId = (Db.a11yEventLogDao.maxId() ?: 0).coerceAtLeast(1)
|
||||
}
|
||||
|
||||
useLogLifecycle()
|
||||
@@ -171,7 +171,7 @@ class EventService : OverlayWindowService(positionKey = "event") {
|
||||
service.eventLogs.removeRange(0, 64)
|
||||
}
|
||||
if (eventLog.id % 100 == 0) {
|
||||
appScope.launchTry { DbSet.a11yEventLogDao.deleteKeepLatest() }
|
||||
appScope.launchTry { Db.a11yEventLogDao.deleteKeepLatest() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,15 +42,15 @@ import li.gkd.app.data.DeviceInfo
|
||||
import li.gkd.app.data.GkdAction
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.data.RpcError
|
||||
import li.gkd.app.data.SubsItem
|
||||
import li.gkd.db.SubsItem
|
||||
import li.gkd.app.data.selfAppInfo
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.notif.NotificationCatalog
|
||||
import li.gkd.app.notif.StopServiceReceiver
|
||||
import li.gkd.app.permission.PermissionStates
|
||||
import li.gkd.app.store.storeFlow
|
||||
import li.gkd.app.util.DefaultSimpleLifeImpl
|
||||
import li.gkd.app.util.LOCAL_HTTP_SUBS_ID
|
||||
import li.gkd.db.LOCAL_HTTP_SUBS_ID
|
||||
import li.gkd.app.util.LogUtils
|
||||
import li.gkd.app.util.OnSimpleLife
|
||||
import li.gkd.app.util.SERVER_SCRIPT_URL
|
||||
@@ -212,7 +212,7 @@ private fun CoroutineScope.createServer(port: Int) = embeddedServer(CIO, port) {
|
||||
call.respond(SnapshotCapture.capture())
|
||||
}
|
||||
post("/getSnapshots") {
|
||||
val list = DbSet.snapshotDao.query().first().mapNotNull {
|
||||
val list = Db.snapshotDao.query().first().mapNotNull {
|
||||
try {
|
||||
SnapshotStore.getMinSnapshot(it.id)
|
||||
} catch (_: Throwable) {
|
||||
@@ -223,7 +223,7 @@ private fun CoroutineScope.createServer(port: Int) = embeddedServer(CIO, port) {
|
||||
}
|
||||
post("/deleteSnapshot") {
|
||||
val data = call.receive<ReqId>()
|
||||
val allSnapshots = DbSet.snapshotDao.query().first()
|
||||
val allSnapshots = Db.snapshotDao.query().first()
|
||||
val snapshot = allSnapshots.find { it.id == data.id }
|
||||
if (snapshot != null) {
|
||||
SnapshotStore.delete(snapshot)
|
||||
|
||||
@@ -11,8 +11,8 @@ import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import li.gkd.app.data.ComplexSnapshot
|
||||
import li.gkd.app.data.Snapshot
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.Snapshot
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.util.LogUtils
|
||||
import li.gkd.app.util.ZipUtils
|
||||
import li.gkd.app.util.appInfoMapFlow
|
||||
@@ -64,7 +64,7 @@ object SnapshotStore {
|
||||
mutationMutex.withLock {
|
||||
withContext(Dispatchers.IO) {
|
||||
fileLayout.committed(snapshot.id).directory.deleteRecursivelyOrThrow()
|
||||
DbSet.snapshotDao.delete(snapshot)
|
||||
Db.snapshotDao.delete(snapshot)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ object SnapshotStore {
|
||||
snapshotFolder.listFiles()?.forEach { file ->
|
||||
file.deleteRecursivelyOrThrow()
|
||||
}
|
||||
DbSet.snapshotDao.deleteAll()
|
||||
Db.snapshotDao.deleteAll()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ object SnapshotStore {
|
||||
LogUtils.d("无法删除旧快照截图", files.legacyPngFile.absolutePath)
|
||||
}
|
||||
if (snapshot.githubAssetId != null) {
|
||||
DbSet.snapshotDao.deleteGithubAssetId(snapshot.id)
|
||||
Db.snapshotDao.deleteGithubAssetId(snapshot.id)
|
||||
}
|
||||
true
|
||||
} finally {
|
||||
@@ -201,7 +201,7 @@ object SnapshotStore {
|
||||
)
|
||||
},
|
||||
publish = {
|
||||
DbSet.snapshotDao.insert(snapshot.toSnapshot())
|
||||
Db.snapshotDao.insert(snapshot.toSnapshot())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -45,7 +45,8 @@ import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import li.gkd.app.MainActivity
|
||||
import li.gkd.app.data.A11yEventLog
|
||||
import li.gkd.app.data.fixedName
|
||||
import li.gkd.app.data.isStateChanged
|
||||
import li.gkd.app.ui.component.AppNameText
|
||||
import li.gkd.app.ui.component.CopyableText
|
||||
import li.gkd.app.ui.component.EmptyText
|
||||
@@ -68,6 +69,7 @@ import li.gkd.app.util.launchTry
|
||||
import li.gkd.app.util.throttle
|
||||
import li.gkd.app.util.toJson5String
|
||||
import li.gkd.app.util.toast
|
||||
import li.gkd.db.A11yEventLog
|
||||
|
||||
@Serializable
|
||||
data object A11yEventLogRoute : NavKey
|
||||
@@ -285,7 +287,8 @@ fun EventLogCard(eventLog: A11yEventLog, modifier: Modifier = Modifier) {
|
||||
softWrap = false,
|
||||
overflow = TextOverflow.MiddleEllipsis,
|
||||
)
|
||||
if (eventLog.desc != null) {
|
||||
val desc = eventLog.desc
|
||||
if (desc != null) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp)
|
||||
@@ -297,7 +300,7 @@ fun EventLogCard(eventLog: A11yEventLog, modifier: Modifier = Modifier) {
|
||||
),
|
||||
)
|
||||
Text(
|
||||
text = eventLog.desc,
|
||||
text = desc,
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.secondaryContainer,
|
||||
|
||||
@@ -5,13 +5,13 @@ import androidx.paging.PagingConfig
|
||||
import androidx.paging.cachedIn
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import li.gkd.app.data.A11yEventLog
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.A11yEventLog
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.ui.share.BaseViewModel
|
||||
|
||||
class A11yEventLogVm : BaseViewModel() {
|
||||
val pagingDataFlow =
|
||||
Pager(PagingConfig(pageSize = 100)) { DbSet.a11yEventLogDao.pagingSource() }
|
||||
Pager(PagingConfig(pageSize = 100)) { Db.a11yEventLogDao.pagingSource() }
|
||||
.flow.cachedIn(scope)
|
||||
|
||||
val showEventLogFlow: StateFlow<A11yEventLog?>
|
||||
@@ -26,6 +26,6 @@ class A11yEventLogVm : BaseViewModel() {
|
||||
}
|
||||
|
||||
suspend fun deleteAll() {
|
||||
DbSet.a11yEventLogDao.deleteAll()
|
||||
Db.a11yEventLogDao.deleteAll()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,8 @@ import androidx.paging.LoadState
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import androidx.paging.compose.itemKey
|
||||
import kotlinx.serialization.Serializable
|
||||
import li.gkd.app.data.SubsConfig
|
||||
import li.gkd.app.data.date
|
||||
import li.gkd.app.data.showActivityId
|
||||
import li.gkd.app.ui.component.AppNameText
|
||||
import li.gkd.app.ui.component.EmptyText
|
||||
import li.gkd.app.ui.component.FixedTimeText
|
||||
@@ -64,6 +65,7 @@ import li.gkd.app.ui.style.scaffoldPadding
|
||||
import li.gkd.app.util.throttle
|
||||
import li.gkd.app.util.launchTry
|
||||
import li.gkd.app.util.toast
|
||||
import li.gkd.db.SubsConfig
|
||||
|
||||
@Serializable
|
||||
data class ActionLogRoute(
|
||||
|
||||
@@ -10,11 +10,11 @@ import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import li.gkd.app.data.ActionLog
|
||||
import li.gkd.db.ActionLog
|
||||
import li.gkd.app.data.ExcludeData
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.data.SubsConfig
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.SubsConfig
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.ui.share.BaseViewModel
|
||||
import li.gkd.app.util.subsMapFlow
|
||||
|
||||
@@ -36,9 +36,9 @@ class ActionLogVm(val route: ActionLogRoute) : BaseViewModel() {
|
||||
|
||||
val pagingDataFlow = Pager(PagingConfig(pageSize = 100)) {
|
||||
when {
|
||||
route.subsId != null -> DbSet.actionLogDao.pagingSubsSource(subsId = route.subsId)
|
||||
route.appId != null -> DbSet.actionLogDao.pagingAppSource(appId = route.appId)
|
||||
else -> DbSet.actionLogDao.pagingSource()
|
||||
route.subsId != null -> Db.actionLogDao.pagingSubsSource(subsId = route.subsId)
|
||||
route.appId != null -> Db.actionLogDao.pagingAppSource(appId = route.appId)
|
||||
else -> Db.actionLogDao.pagingSource()
|
||||
}
|
||||
}
|
||||
.flow
|
||||
@@ -72,13 +72,13 @@ class ActionLogVm(val route: ActionLogRoute) : BaseViewModel() {
|
||||
flowOf(null)
|
||||
} else {
|
||||
val configFlow = if (actionLog.groupType == SubsConfig.AppGroupType) {
|
||||
DbSet.subsConfigDao.queryAppGroupTypeConfig(
|
||||
Db.subsConfigDao.queryAppGroupTypeConfig(
|
||||
actionLog.subsId,
|
||||
actionLog.appId,
|
||||
actionLog.groupKey,
|
||||
)
|
||||
} else {
|
||||
DbSet.subsConfigDao.queryGlobalGroupTypeConfig(
|
||||
Db.subsConfigDao.queryGlobalGroupTypeConfig(
|
||||
actionLog.subsId,
|
||||
actionLog.groupKey,
|
||||
)
|
||||
@@ -121,9 +121,9 @@ class ActionLogVm(val route: ActionLogRoute) : BaseViewModel() {
|
||||
|
||||
suspend fun deleteLogs() {
|
||||
when {
|
||||
route.subsId != null -> DbSet.actionLogDao.deleteSubsAll(route.subsId)
|
||||
route.appId != null -> DbSet.actionLogDao.deleteAppAll(route.appId)
|
||||
else -> DbSet.actionLogDao.deleteAll()
|
||||
route.subsId != null -> Db.actionLogDao.deleteSubsAll(route.subsId)
|
||||
route.appId != null -> Db.actionLogDao.deleteAppAll(route.appId)
|
||||
else -> Db.actionLogDao.deleteAll()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ class ActionLogVm(val route: ActionLogRoute) : BaseViewModel() {
|
||||
groupKey = actionLog.groupKey,
|
||||
)
|
||||
val oldExclude = ExcludeData.parse(subsConfig.exclude)
|
||||
DbSet.subsConfigDao.insert(
|
||||
Db.subsConfigDao.insert(
|
||||
subsConfig.copy(
|
||||
exclude = oldExclude.copy(
|
||||
appIds = oldExclude.appIds.toMutableMap().apply {
|
||||
@@ -167,7 +167,7 @@ class ActionLogVm(val route: ActionLogRoute) : BaseViewModel() {
|
||||
)
|
||||
}
|
||||
val oldExclude = ExcludeData.parse(subsConfig.exclude)
|
||||
DbSet.subsConfigDao.insert(
|
||||
Db.subsConfigDao.insert(
|
||||
subsConfig.copy(
|
||||
exclude = oldExclude.switch(actionLog.appId, activityId).stringify(),
|
||||
),
|
||||
|
||||
@@ -35,7 +35,8 @@ import androidx.paging.LoadState
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import androidx.paging.compose.itemKey
|
||||
import kotlinx.serialization.Serializable
|
||||
import li.gkd.app.data.ActivityLog
|
||||
import li.gkd.app.data.date
|
||||
import li.gkd.app.data.showActivityId
|
||||
import li.gkd.app.ui.component.AppNameText
|
||||
import li.gkd.app.ui.component.EmptyText
|
||||
import li.gkd.app.ui.component.FixedTimeText
|
||||
@@ -54,6 +55,7 @@ import li.gkd.app.util.appInfoMapFlow
|
||||
import li.gkd.app.util.launchTry
|
||||
import li.gkd.app.util.throttle
|
||||
import li.gkd.app.util.toast
|
||||
import li.gkd.db.ActivityLog
|
||||
|
||||
@Serializable
|
||||
data object ActivityLogRoute : NavKey
|
||||
|
||||
@@ -3,13 +3,13 @@ package li.gkd.app.ui
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.cachedIn
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.ui.share.BaseViewModel
|
||||
|
||||
class ActivityLogVm : BaseViewModel() {
|
||||
val pagingDataFlow = Pager(PagingConfig(pageSize = 100)) { DbSet.activityLogDao.pagingSource() }
|
||||
val pagingDataFlow = Pager(PagingConfig(pageSize = 100)) { Db.activityLogDao.pagingSource() }
|
||||
.flow.cachedIn(scope)
|
||||
suspend fun deleteAll() {
|
||||
DbSet.activityLogDao.deleteAll()
|
||||
Db.activityLogDao.deleteAll()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.serialization.Serializable
|
||||
import li.gkd.app.data.ActionLog
|
||||
import li.gkd.db.ActionLog
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.store.storeFlow
|
||||
import li.gkd.app.ui.component.AnimatedBooleanContent
|
||||
@@ -65,7 +65,7 @@ import li.gkd.app.ui.share.noRippleClickable
|
||||
import li.gkd.app.ui.style.EmptyHeight
|
||||
import li.gkd.app.ui.style.iconTextSize
|
||||
import li.gkd.app.ui.style.scaffoldPadding
|
||||
import li.gkd.app.util.LOCAL_SUBS_ID
|
||||
import li.gkd.db.LOCAL_SUBS_ID
|
||||
import li.gkd.app.util.RuleSortOption
|
||||
import li.gkd.app.util.copyText
|
||||
import li.gkd.app.util.findOption
|
||||
|
||||
@@ -14,11 +14,11 @@ import kotlinx.coroutines.flow.runningFold
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.withContext
|
||||
import li.gkd.app.data.ActionLog
|
||||
import li.gkd.app.data.CategoryConfig
|
||||
import li.gkd.db.ActionLog
|
||||
import li.gkd.db.CategoryConfig
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.data.SubsConfig
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.SubsConfig
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.store.storeFlow
|
||||
import li.gkd.app.ui.component.ShowGroupState
|
||||
import li.gkd.app.ui.component.batchUpdateGroupEnable
|
||||
@@ -76,8 +76,8 @@ class AppConfigVm(val route: AppConfigRoute) : BaseViewModel() {
|
||||
Loadable.Loading -> flowOf(Loadable.Loading)
|
||||
is Loadable.Failure -> flowOf(snapshotState)
|
||||
is Loadable.Ready -> combine(
|
||||
DbSet.subsItemDao.query(),
|
||||
DbSet.appConfigDao.queryAppUsedList(route.appId),
|
||||
Db.subsItemDao.query(),
|
||||
Db.appConfigDao.queryAppUsedList(route.appId),
|
||||
) { items, appConfigs ->
|
||||
val usedSubsIds = items.filter { it.enable }.map { it.id }.sorted()
|
||||
val appUsedSubsIds = usedSubsIds.filter { id ->
|
||||
@@ -89,9 +89,9 @@ class AppConfigVm(val route: AppConfigRoute) : BaseViewModel() {
|
||||
appUsedSubsIds to entries
|
||||
}.distinctUntilChanged().flatMapLatest { (usedSubsIds, entries) ->
|
||||
combine(
|
||||
DbSet.subsConfigDao.queryUsedGlobalConfig(),
|
||||
DbSet.subsConfigDao.queryAppConfig(usedSubsIds, route.appId),
|
||||
DbSet.categoryConfigDao.queryBySubsIds(usedSubsIds),
|
||||
Db.subsConfigDao.queryUsedGlobalConfig(),
|
||||
Db.subsConfigDao.queryAppConfig(usedSubsIds, route.appId),
|
||||
Db.categoryConfigDao.queryBySubsIds(usedSubsIds),
|
||||
) { globalConfigs, appConfigs, categoryConfigs ->
|
||||
val subsPairs = entries.map { entry ->
|
||||
val globalGroups = entry.subscription.globalGroups.filter { group ->
|
||||
@@ -190,7 +190,7 @@ class AppConfigVm(val route: AppConfigRoute) : BaseViewModel() {
|
||||
private val sortStateFlow = storeFlow.flatMapLatest { store ->
|
||||
val option = RuleSortOption.objects.findOption(store.appRuleSort)
|
||||
val logsFlow = if (option == RuleSortOption.ByActionTime) {
|
||||
DbSet.actionLogDao.queryLatestByAppId(route.appId)
|
||||
Db.actionLogDao.queryLatestByAppId(route.appId)
|
||||
} else {
|
||||
flowOf(emptyList())
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.Serializable
|
||||
import li.gkd.app.MainActivity
|
||||
import li.gkd.app.data.Snapshot
|
||||
import li.gkd.app.data.date
|
||||
import li.gkd.app.data.screenshotFile
|
||||
import li.gkd.app.permission.PermissionStates
|
||||
import li.gkd.app.snapshot.SnapshotStore
|
||||
import li.gkd.app.ui.component.EmptyText
|
||||
@@ -61,6 +62,7 @@ import li.gkd.app.util.copyText
|
||||
import li.gkd.app.util.launchTry
|
||||
import li.gkd.app.util.throttle
|
||||
import li.gkd.app.util.toast
|
||||
import li.gkd.db.Snapshot
|
||||
|
||||
@Serializable
|
||||
data object SnapshotPageRoute : NavKey
|
||||
@@ -330,11 +332,12 @@ private fun SnapshotCard(
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
}
|
||||
val showActivityId = if (snapshot.activityId != null) {
|
||||
if (snapshot.activityId.startsWith(snapshot.appId)) {
|
||||
snapshot.activityId.substring(snapshot.appId.length)
|
||||
val activityId = snapshot.activityId
|
||||
val showActivityId = if (activityId != null) {
|
||||
if (activityId.startsWith(snapshot.appId)) {
|
||||
activityId.substring(snapshot.appId.length)
|
||||
} else {
|
||||
snapshot.activityId
|
||||
activityId
|
||||
}
|
||||
} else {
|
||||
null
|
||||
|
||||
@@ -4,12 +4,13 @@ import android.graphics.BitmapFactory
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.withContext
|
||||
import li.gkd.app.data.Snapshot
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.app.data.screenshotFile
|
||||
import li.gkd.app.snapshot.SnapshotStore
|
||||
import li.gkd.app.ui.share.BaseViewModel
|
||||
import li.gkd.app.util.ImageUtils
|
||||
import li.gkd.app.util.appInfoMapFlow
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.db.Snapshot
|
||||
import java.io.File
|
||||
|
||||
data class SnapshotUiState(
|
||||
@@ -18,7 +19,7 @@ data class SnapshotUiState(
|
||||
)
|
||||
|
||||
class SnapshotVm : BaseViewModel() {
|
||||
private val snapshotsFlow = DbSet.snapshotDao.query()
|
||||
private val snapshotsFlow = Db.snapshotDao.query()
|
||||
|
||||
val uiState = combine(
|
||||
snapshotsFlow,
|
||||
@@ -47,7 +48,7 @@ class SnapshotVm : BaseViewModel() {
|
||||
|
||||
suspend fun markUploaded(snapshot: Snapshot, githubAssetId: Int) =
|
||||
withContext(Dispatchers.IO) {
|
||||
DbSet.snapshotDao.update(snapshot.copy(githubAssetId = githubAssetId))
|
||||
Db.snapshotDao.update(snapshot.copy(githubAssetId = githubAssetId))
|
||||
}
|
||||
|
||||
suspend fun replaceScreenshot(snapshot: Snapshot, newBytes: ByteArray): Boolean {
|
||||
|
||||
@@ -5,11 +5,11 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.withContext
|
||||
import li.gkd.app.data.CategoryConfig
|
||||
import li.gkd.db.CategoryConfig
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.data.SubsConfig
|
||||
import li.gkd.db.SubsConfig
|
||||
import li.gkd.app.data.edit
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.ui.component.batchUpdateGroupEnable
|
||||
import li.gkd.app.ui.component.toGroupState
|
||||
import li.gkd.app.ui.component.updateRuleGroupEnable
|
||||
@@ -33,9 +33,9 @@ class SubsAppGroupListVm(val route: SubsAppGroupListRoute) : BaseViewModel() {
|
||||
private val subscription = requiredSubscription(route.subsItemId)
|
||||
|
||||
private val subsConfigsFlow =
|
||||
DbSet.subsConfigDao.queryAppGroupTypeConfig(route.subsItemId, route.appId)
|
||||
Db.subsConfigDao.queryAppGroupTypeConfig(route.subsItemId, route.appId)
|
||||
|
||||
private val categoryConfigsFlow = DbSet.categoryConfigDao.queryConfig(route.subsItemId)
|
||||
private val categoryConfigsFlow = Db.categoryConfigDao.queryConfig(route.subsItemId)
|
||||
|
||||
val uiState = subscription.buildUiState(
|
||||
initialValue = { rawSubscription ->
|
||||
|
||||
@@ -49,11 +49,11 @@ import li.gkd.app.ui.style.EmptyHeight
|
||||
import li.gkd.app.ui.style.scaffoldPadding
|
||||
import li.gkd.app.util.AppGroupOption
|
||||
import li.gkd.app.util.AppSortOption
|
||||
import li.gkd.app.util.LOCAL_SUBS_IDS
|
||||
import li.gkd.app.util.appInfoMapFlow
|
||||
import li.gkd.app.util.findOption
|
||||
import li.gkd.app.util.launchTry
|
||||
import li.gkd.app.util.throttle
|
||||
import li.gkd.db.LOCAL_SUBS_IDS
|
||||
|
||||
@Serializable
|
||||
data class SubsAppListRoute(val subsItemId: Long) : NavKey
|
||||
|
||||
@@ -9,10 +9,10 @@ import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.update
|
||||
import li.gkd.app.MainViewModel
|
||||
import li.gkd.app.data.AppConfig
|
||||
import li.gkd.db.AppConfig
|
||||
import li.gkd.app.data.AppInfo
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.store.blockMatchAppListFlow
|
||||
import li.gkd.app.store.storeFlow
|
||||
import li.gkd.app.ui.share.BaseViewModel
|
||||
@@ -36,12 +36,12 @@ class SubsAppListVm(
|
||||
|
||||
private val subscription = requiredSubscription(route.subsItemId)
|
||||
|
||||
private val appConfigsFlow = DbSet.appConfigDao.queryAppTypeConfig(route.subsItemId)
|
||||
private val appConfigsFlow = Db.appConfigDao.queryAppTypeConfig(route.subsItemId)
|
||||
|
||||
private val groupSubsConfigsFlow =
|
||||
DbSet.subsConfigDao.querySubsGroupTypeConfig(route.subsItemId)
|
||||
Db.subsConfigDao.querySubsGroupTypeConfig(route.subsItemId)
|
||||
|
||||
private val categoryConfigsFlow = DbSet.categoryConfigDao.queryConfig(route.subsItemId)
|
||||
private val categoryConfigsFlow = Db.categoryConfigDao.queryConfig(route.subsItemId)
|
||||
|
||||
val searchStrFlow: StateFlow<String>
|
||||
field = MutableStateFlow("")
|
||||
@@ -188,6 +188,6 @@ class SubsAppListVm(
|
||||
subsId = route.subsItemId,
|
||||
appId = appId,
|
||||
)
|
||||
DbSet.appConfigDao.insert(newConfig)
|
||||
Db.appConfigDao.insert(newConfig)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,10 @@ import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.update
|
||||
import li.gkd.app.MainViewModel
|
||||
import li.gkd.app.data.CategoryConfig
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.data.SubsConfig
|
||||
import li.gkd.app.data.batchResetAppGroupEnable
|
||||
import li.gkd.app.data.edit
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.store.storeFlow
|
||||
import li.gkd.app.ui.component.updateRuleGroupEnable
|
||||
import li.gkd.app.ui.share.BaseViewModel
|
||||
@@ -23,6 +22,8 @@ import li.gkd.app.util.EnableGroupOption
|
||||
import li.gkd.app.util.appInfoMapFlow
|
||||
import li.gkd.app.util.findOption
|
||||
import li.gkd.app.store.blockMatchAppListFlow
|
||||
import li.gkd.db.CategoryConfig
|
||||
import li.gkd.db.SubsConfig
|
||||
|
||||
data class SubsCategoryGroupConfigs(
|
||||
val subsConfigs: List<SubsConfig>,
|
||||
@@ -45,9 +46,9 @@ class SubsCategoryGroupVm(
|
||||
field = MutableStateFlow(false)
|
||||
|
||||
private val subscription = requiredSubscription(route.subsId)
|
||||
private val subsConfigsFlow = DbSet.subsConfigDao.querySubsGroupTypeConfig(route.subsId)
|
||||
private val subsConfigsFlow = Db.subsConfigDao.querySubsGroupTypeConfig(route.subsId)
|
||||
private val categoryConfigFlow =
|
||||
DbSet.categoryConfigDao.queryCategoryConfig(route.subsId, route.categoryKey)
|
||||
Db.categoryConfigDao.queryCategoryConfig(route.subsId, route.categoryKey)
|
||||
private val appActionOrderMapState = subsAppActionOrderMapState(route.subsId)
|
||||
|
||||
val uiState = subscription.buildUiState(
|
||||
@@ -142,7 +143,7 @@ class SubsCategoryGroupVm(
|
||||
true -> false
|
||||
}
|
||||
val option = EnableGroupOption.objects.findOption(newValue)
|
||||
DbSet.categoryConfigDao.insert(
|
||||
Db.categoryConfigDao.insert(
|
||||
(categoryConfig ?: CategoryConfig(
|
||||
enable = option.value,
|
||||
subsId = rawSubscription.id,
|
||||
@@ -155,7 +156,7 @@ class SubsCategoryGroupVm(
|
||||
suspend fun resetAllRuleSwitches(): Int {
|
||||
val state = uiState.value.value ?: error("订阅尚未加载")
|
||||
val rawSubscription = subscription.requireValue()
|
||||
return DbSet.subsConfigDao.batchResetAppGroupEnable(
|
||||
return Db.subsConfigDao.batchResetAppGroupEnable(
|
||||
rawSubscription.id,
|
||||
state.apps.flatMap { app -> app.groups }.map { group ->
|
||||
group to rawSubscription.getAppByGroup(group)
|
||||
@@ -197,7 +198,7 @@ class SubsCategoryGroupVm(
|
||||
subscription.update { current ->
|
||||
current.edit { removeCategory(route.categoryKey) }
|
||||
}
|
||||
DbSet.categoryConfigDao.deleteByCategoryKey(
|
||||
Db.categoryConfigDao.deleteByCategoryKey(
|
||||
route.subsId,
|
||||
route.categoryKey,
|
||||
)
|
||||
|
||||
@@ -31,7 +31,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import kotlinx.serialization.Serializable
|
||||
import li.gkd.app.data.CategoryConfig
|
||||
import li.gkd.db.CategoryConfig
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.ui.component.EmptyText
|
||||
import li.gkd.app.ui.component.FullscreenDialog
|
||||
|
||||
@@ -3,10 +3,10 @@ package li.gkd.app.ui
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import li.gkd.app.data.CategoryConfig
|
||||
import li.gkd.db.CategoryConfig
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.data.edit
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.ui.share.BaseViewModel
|
||||
import li.gkd.app.ui.share.Loadable
|
||||
import li.gkd.app.util.EnableGroupOption
|
||||
@@ -22,7 +22,7 @@ class SubsCategoryVm(val route: SubsCategoryRoute) : BaseViewModel() {
|
||||
field = MutableStateFlow(false)
|
||||
|
||||
private val subscription = requiredSubscription(route.subsItemId)
|
||||
private val categoryConfigsFlow = DbSet.categoryConfigDao.queryConfig(route.subsItemId)
|
||||
private val categoryConfigsFlow = Db.categoryConfigDao.queryConfig(route.subsItemId)
|
||||
|
||||
val uiState = subscription.buildUiState(
|
||||
initialValue = { rawSubscription ->
|
||||
@@ -61,7 +61,7 @@ class SubsCategoryVm(val route: SubsCategoryRoute) : BaseViewModel() {
|
||||
val categoryConfigMap = state.categoryConfigMap.value
|
||||
?: error("类别配置尚未加载")
|
||||
val oldConfig = categoryConfigMap[category.key]
|
||||
DbSet.categoryConfigDao.insert(
|
||||
Db.categoryConfigDao.insert(
|
||||
(oldConfig ?: CategoryConfig(
|
||||
enable = option.value,
|
||||
subsId = rawSubscription.id,
|
||||
|
||||
@@ -9,8 +9,8 @@ import li.gkd.app.MainViewModel
|
||||
import li.gkd.app.data.AppInfo
|
||||
import li.gkd.app.data.ExcludeData
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.data.SubsConfig
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.SubsConfig
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.store.storeFlow
|
||||
import li.gkd.app.ui.share.BaseViewModel
|
||||
import li.gkd.app.ui.share.Loadable
|
||||
@@ -38,7 +38,7 @@ class SubsGlobalGroupExcludeVm(
|
||||
) : BaseViewModel() {
|
||||
|
||||
private val subscription = requiredSubscription(route.subsItemId)
|
||||
private val subsConfigFlow = DbSet.subsConfigDao
|
||||
private val subsConfigFlow = Db.subsConfigDao
|
||||
.queryGlobalGroupTypeConfig(route.subsItemId, route.groupKey)
|
||||
|
||||
private val appFilter = useAppFilter(
|
||||
@@ -208,7 +208,7 @@ class SubsGlobalGroupExcludeVm(
|
||||
subsId = route.subsItemId,
|
||||
groupKey = route.groupKey,
|
||||
)).copy(exclude = newExclude.stringify())
|
||||
DbSet.subsConfigDao.insert(subsConfig)
|
||||
Db.subsConfigDao.insert(subsConfig)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ class SubsGlobalGroupExcludeVm(
|
||||
},
|
||||
).stringify(),
|
||||
)
|
||||
DbSet.subsConfigDao.insert(subsConfig)
|
||||
Db.subsConfigDao.insert(subsConfig)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.data.SubsConfig
|
||||
import li.gkd.db.SubsConfig
|
||||
import li.gkd.app.data.edit
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.ui.component.batchUpdateGroupEnable
|
||||
import li.gkd.app.ui.component.toGroupState
|
||||
import li.gkd.app.ui.component.updateRuleGroupEnable
|
||||
@@ -22,7 +22,7 @@ class SubsGlobalGroupListVm(val route: SubsGlobalGroupListRoute) : BaseViewModel
|
||||
private val subscription = requiredSubscription(route.subsItemId)
|
||||
|
||||
private val subsConfigsFlow =
|
||||
DbSet.subsConfigDao.queryGlobalGroupTypeConfig(route.subsItemId)
|
||||
Db.subsConfigDao.queryGlobalGroupTypeConfig(route.subsItemId)
|
||||
|
||||
val uiState = subscription.buildUiState(
|
||||
initialValue = { rawSubscription ->
|
||||
@@ -79,6 +79,6 @@ class SubsGlobalGroupListVm(val route: SubsGlobalGroupListRoute) : BaseViewModel
|
||||
subscription.update { current ->
|
||||
current.edit { removeGlobalGroups { it.key in selectedKeys } }
|
||||
}
|
||||
DbSet.subsConfigDao.batchDeleteGlobalGroupConfig(route.subsItemId, selectedKeys.toList())
|
||||
Db.subsConfigDao.batchDeleteGlobalGroupConfig(route.subsItemId, selectedKeys.toList())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package li.gkd.app.ui
|
||||
|
||||
import li.gkd.app.data.SubsItem
|
||||
import li.gkd.db.SubsItem
|
||||
import li.gkd.app.ui.share.BaseViewModel
|
||||
import li.gkd.app.util.SubscriptionResult
|
||||
import li.gkd.app.util.SubscriptionStore
|
||||
|
||||
@@ -28,10 +28,10 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import li.gkd.app.data.CategoryConfig
|
||||
import li.gkd.db.CategoryConfig
|
||||
import li.gkd.app.data.ExcludeData
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.data.SubsConfig
|
||||
import li.gkd.db.SubsConfig
|
||||
import li.gkd.app.ui.getGlobalGroupChecked
|
||||
import li.gkd.app.ui.icon.ResetSettings
|
||||
import li.gkd.app.ui.share.noRippleClickable
|
||||
|
||||
@@ -11,12 +11,12 @@ import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import li.gkd.app.MainViewModel
|
||||
import li.gkd.app.data.CategoryConfig
|
||||
import li.gkd.db.CategoryConfig
|
||||
import li.gkd.app.data.ExcludeData
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.data.SubsConfig
|
||||
import li.gkd.db.SubsConfig
|
||||
import li.gkd.app.data.edit
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.ui.SubsGlobalGroupExcludeRoute
|
||||
import li.gkd.app.ui.UpsertRuleGroupRoute
|
||||
import li.gkd.app.ui.getGlobalGroupChecked
|
||||
@@ -52,7 +52,7 @@ data class ShowGroupState(
|
||||
subs.globalGroups
|
||||
}?.find { it.key == groupKey } ?: error("require group")
|
||||
val category = subs.getCategory(group.name) ?: return null
|
||||
return DbSet.categoryConfigDao.queryCategoryConfig(subsId, category.key).first()
|
||||
return Db.categoryConfigDao.queryCategoryConfig(subsId, category.key).first()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,9 +60,9 @@ private fun ShowGroupState.querySubsConfigFlow(): Flow<SubsConfig?> {
|
||||
val groupKey = groupKey ?: error("require groupKey")
|
||||
return if (groupType == SubsConfig.AppGroupType) {
|
||||
val appId = appId ?: error("require appId")
|
||||
DbSet.subsConfigDao.queryAppGroupTypeConfig(subsId, appId, groupKey)
|
||||
Db.subsConfigDao.queryAppGroupTypeConfig(subsId, appId, groupKey)
|
||||
} else {
|
||||
DbSet.subsConfigDao.queryGlobalGroupTypeConfig(subsId, groupKey)
|
||||
Db.subsConfigDao.queryGlobalGroupTypeConfig(subsId, groupKey)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ suspend fun updateRuleGroupEnable(
|
||||
)
|
||||
}
|
||||
}
|
||||
DbSet.subsConfigDao.insert(newConfig)
|
||||
Db.subsConfigDao.insert(newConfig)
|
||||
}
|
||||
|
||||
suspend fun batchUpdateGroupEnable(
|
||||
@@ -245,7 +245,7 @@ suspend fun batchUpdateGroupEnable(
|
||||
val canDeleteList = newSubsConfigs.filter {
|
||||
it.type == SubsConfig.AppGroupType && it.enable == null && it.exclude.isEmpty()
|
||||
}
|
||||
DbSet.subsConfigDao.insertAndDelete(
|
||||
Db.subsConfigDao.insertAndDelete(
|
||||
newSubsConfigs.filterNot { canDeleteList.contains(it) },
|
||||
canDeleteList
|
||||
)
|
||||
@@ -303,14 +303,14 @@ class RuleGroupState(
|
||||
): String {
|
||||
if (group is RawSubscription.RawGlobalGroup && state.pageAppId != null) {
|
||||
val excludeData = ExcludeData.parse(subsConfig.exclude)
|
||||
DbSet.subsConfigDao.update(
|
||||
Db.subsConfigDao.update(
|
||||
subsConfig.copy(
|
||||
exclude = excludeData.clear(appId = state.pageAppId).stringify(),
|
||||
),
|
||||
)
|
||||
return "已重置局部开关至默认值"
|
||||
}
|
||||
DbSet.subsConfigDao.update(subsConfig.copy(enable = null))
|
||||
Db.subsConfigDao.update(subsConfig.copy(enable = null))
|
||||
return "已重置开关至默认值"
|
||||
}
|
||||
|
||||
@@ -352,7 +352,7 @@ class RuleGroupState(
|
||||
appId = appId,
|
||||
groupKey = groupKey,
|
||||
)).copy(exclude = excludeData.stringify())
|
||||
DbSet.subsConfigDao.insert(newSubsConfig)
|
||||
Db.subsConfigDao.insert(newSubsConfig)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -15,7 +15,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import li.gkd.app.data.AppConfig
|
||||
import li.gkd.db.AppConfig
|
||||
import li.gkd.app.data.AppInfo
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.store.blockMatchAppListFlow
|
||||
|
||||
@@ -32,7 +32,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import li.gkd.app.META
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.data.SubsItem
|
||||
import li.gkd.db.SubsItem
|
||||
import li.gkd.app.util.formatTimeAgo
|
||||
import li.gkd.app.util.throttle
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.util.isLocalNetworkUrl
|
||||
import li.gkd.app.util.throttle
|
||||
import li.gkd.app.util.toast
|
||||
@@ -80,7 +80,7 @@ class SubsLinkDialogState(
|
||||
|
||||
suspend fun request(initialValue: String = ""): String? {
|
||||
val existingUrls = withContext(Dispatchers.IO) {
|
||||
DbSet.subsItemDao.queryAll().mapNotNullTo(mutableSetOf()) { it.updateUrl }
|
||||
Db.subsItemDao.queryAll().mapNotNullTo(mutableSetOf()) { it.updateUrl }
|
||||
}
|
||||
val value = withContext(Dispatchers.Main.immediate) {
|
||||
requestMutex.withLock {
|
||||
|
||||
@@ -36,6 +36,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import li.gkd.app.META
|
||||
import li.gkd.app.data.mtimeStr
|
||||
import li.gkd.app.ui.ActionLogRoute
|
||||
import li.gkd.app.ui.SubsAppListRoute
|
||||
import li.gkd.app.ui.SubsCategoryRoute
|
||||
@@ -44,7 +45,7 @@ import li.gkd.app.ui.SubsSheetVm
|
||||
import li.gkd.app.ui.share.LocalMainViewModel
|
||||
import li.gkd.app.ui.style.EmptyHeight
|
||||
import li.gkd.app.ui.style.itemHorizontalPadding
|
||||
import li.gkd.app.util.LOCAL_SUBS_ID
|
||||
import li.gkd.db.LOCAL_SUBS_ID
|
||||
import li.gkd.app.util.SubscriptionResult
|
||||
import li.gkd.app.util.launchTry
|
||||
import li.gkd.app.util.subsItemsFlow
|
||||
@@ -312,7 +313,8 @@ class SubsSheetState {
|
||||
)
|
||||
}
|
||||
}
|
||||
if (!subsItem.isLocal && subsItem.updateUrl != null) {
|
||||
val updateUrl = subsItem.updateUrl
|
||||
if (!subsItem.isLocal && updateUrl != null) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable(onClickLabel = "编辑订阅链接", onClick = throttle {
|
||||
@@ -321,8 +323,9 @@ class SubsSheetState {
|
||||
return@throttle
|
||||
}
|
||||
scope.launchTry {
|
||||
val url =
|
||||
mainVm.subsLinkDialog.request(initialValue = subsItem.updateUrl)
|
||||
val url = mainVm.subsLinkDialog.request(
|
||||
initialValue = updateUrl,
|
||||
)
|
||||
?: return@launchTry
|
||||
vm.addOrModifySubscription(url, subsItem).message?.let {
|
||||
toast(it)
|
||||
@@ -340,7 +343,7 @@ class SubsSheetState {
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
)
|
||||
Text(
|
||||
text = subsItem.updateUrl,
|
||||
text = updateUrl,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
softWrap = false,
|
||||
@@ -348,7 +351,7 @@ class SubsSheetState {
|
||||
modifier = Modifier
|
||||
.clearAndSetSemantics {}
|
||||
.clickable(onClickLabel = "查看订阅链接", onClick = {
|
||||
mainVm.openUrl(subsItem.updateUrl)
|
||||
mainVm.openUrl(updateUrl)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import li.gkd.app.MainActivity
|
||||
import li.gkd.app.R
|
||||
import li.gkd.app.data.SubsConfig
|
||||
import li.gkd.db.SubsConfig
|
||||
import li.gkd.app.permission.PermissionStates
|
||||
import li.gkd.app.priv.PrivilegeServiceStatus
|
||||
import li.gkd.app.priv.privilegeContextFlow
|
||||
|
||||
@@ -62,7 +62,7 @@ import li.gkd.app.ui.share.ListPlaceholder
|
||||
import li.gkd.app.ui.share.Loadable
|
||||
import li.gkd.app.ui.share.LocalMainViewModel
|
||||
import li.gkd.app.ui.style.EmptyHeight
|
||||
import li.gkd.app.util.LOCAL_SUBS_ID
|
||||
import li.gkd.db.LOCAL_SUBS_ID
|
||||
import li.gkd.app.util.ShortUrlSet
|
||||
import li.gkd.app.util.SubscriptionResult
|
||||
import li.gkd.app.util.UpdateTimeOption
|
||||
|
||||
@@ -12,8 +12,8 @@ import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.data.SubsItem
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.SubsItem
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.store.storeFlow
|
||||
import li.gkd.app.ui.share.BaseViewModel
|
||||
import li.gkd.app.ui.share.Loadable
|
||||
@@ -55,7 +55,7 @@ class SubsManageVm : BaseViewModel() {
|
||||
Loadable.Loading -> flowOf(Loadable.Loading)
|
||||
is Loadable.Failure -> flowOf(snapshotState)
|
||||
is Loadable.Ready -> combine(
|
||||
DbSet.subsItemDao.query(),
|
||||
Db.subsItemDao.query(),
|
||||
SubscriptionStore.updating,
|
||||
) { subItems, refreshing ->
|
||||
buildSubsManageUiState(
|
||||
@@ -95,7 +95,7 @@ class SubsManageVm : BaseViewModel() {
|
||||
|
||||
fun updateOrder(items: List<SubsItem>) {
|
||||
scope.launchTry(Dispatchers.IO) {
|
||||
DbSet.subsItemDao.batchUpdateOrder(items)
|
||||
Db.subsItemDao.batchUpdateOrder(items)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ class SubsManageVm : BaseViewModel() {
|
||||
|
||||
private fun setSubscriptionEnabled(item: SubsItem, enabled: Boolean) {
|
||||
scope.launchTry(Dispatchers.IO) {
|
||||
DbSet.subsItemDao.updateEnable(item.id, enabled)
|
||||
Db.subsItemDao.updateEnable(item.id, enabled)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.map
|
||||
import li.gkd.app.MainViewModel
|
||||
import li.gkd.app.data.AppInfo
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.store.SettingsStore
|
||||
import li.gkd.app.store.blockMatchAppListFlow
|
||||
import li.gkd.app.store.storeFlow
|
||||
@@ -28,7 +28,7 @@ class AppFilter(
|
||||
fun BaseViewModel.subsAppActionOrderMapState(
|
||||
subsId: Long,
|
||||
): StateFlow<Loadable<Map<String, Int>>> =
|
||||
DbSet.actionLogDao.queryLatestUniqueAppIds(subsId).map { appIds ->
|
||||
Db.actionLogDao.queryLatestUniqueAppIds(subsId).map { appIds ->
|
||||
appIds.mapIndexed { index, appId -> appId to index }.toMap()
|
||||
}.stateLoadable()
|
||||
|
||||
@@ -36,7 +36,7 @@ fun BaseViewModel.globalGroupAppOrderListState(
|
||||
subsId: Long,
|
||||
groupKey: Int,
|
||||
): StateFlow<Loadable<List<String>>> =
|
||||
DbSet.actionLogDao.queryLatestUniqueAppIds(subsId, groupKey).stateLoadable()
|
||||
Db.actionLogDao.queryLatestUniqueAppIds(subsId, groupKey).stateLoadable()
|
||||
|
||||
fun BaseViewModel.useAppFilter(
|
||||
mainVm: MainViewModel,
|
||||
|
||||
@@ -5,12 +5,12 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.Serializable
|
||||
import li.gkd.app.data.AppConfig
|
||||
import li.gkd.app.data.CategoryConfig
|
||||
import li.gkd.db.AppConfig
|
||||
import li.gkd.db.CategoryConfig
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.data.SubsConfig
|
||||
import li.gkd.app.data.SubsItem
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.SubsConfig
|
||||
import li.gkd.db.SubsItem
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.store.a11yScopeAppListFlow
|
||||
import li.gkd.app.store.actionCountFlow
|
||||
import li.gkd.app.store.blockA11yAppListFlow
|
||||
@@ -53,10 +53,10 @@ object BackupUtils {
|
||||
tempDir.resolve("db.json").writeText(
|
||||
json.encodeToString(
|
||||
DbData(
|
||||
subsItems = DbSet.subsItemDao.queryAll(),
|
||||
subsConfigs = DbSet.subsConfigDao.queryAll(),
|
||||
categoryConfigs = DbSet.categoryConfigDao.queryAll(),
|
||||
appConfigs = DbSet.appConfigDao.queryAll(),
|
||||
subsItems = Db.subsItemDao.queryAll(),
|
||||
subsConfigs = Db.subsConfigDao.queryAll(),
|
||||
categoryConfigs = Db.categoryConfigDao.queryAll(),
|
||||
appConfigs = Db.appConfigDao.queryAll(),
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -90,18 +90,18 @@ object BackupUtils {
|
||||
|
||||
val prepared = prepareBackup(unzipDir)
|
||||
prepared.dbData?.let { dbData ->
|
||||
DbSet.withTransaction {
|
||||
Db.withTransaction {
|
||||
if (!dbData.subsItems.isNullOrEmpty()) {
|
||||
DbSet.subsItemDao.insertOrIgnore(*dbData.subsItems.toTypedArray())
|
||||
Db.subsItemDao.insertOrIgnore(*dbData.subsItems.toTypedArray())
|
||||
}
|
||||
if (!dbData.subsConfigs.isNullOrEmpty()) {
|
||||
DbSet.subsConfigDao.insertOrIgnore(*dbData.subsConfigs.toTypedArray())
|
||||
Db.subsConfigDao.insertOrIgnore(*dbData.subsConfigs.toTypedArray())
|
||||
}
|
||||
if (!dbData.categoryConfigs.isNullOrEmpty()) {
|
||||
DbSet.categoryConfigDao.insertOrIgnore(*dbData.categoryConfigs.toTypedArray())
|
||||
Db.categoryConfigDao.insertOrIgnore(*dbData.categoryConfigs.toTypedArray())
|
||||
}
|
||||
if (!dbData.appConfigs.isNullOrEmpty()) {
|
||||
DbSet.appConfigDao.insertOrIgnore(*dbData.appConfigs.toTypedArray())
|
||||
Db.appConfigDao.insertOrIgnore(*dbData.appConfigs.toTypedArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,6 @@ const val ISSUES_URL = "${REPOSITORY_URL}/issues"
|
||||
|
||||
const val HOME_PAGE_URL = "https://gkd.li"
|
||||
|
||||
const val LOCAL_SUBS_ID = -2L
|
||||
const val LOCAL_HTTP_SUBS_ID = -1L
|
||||
val LOCAL_SUBS_IDS = arrayOf(LOCAL_SUBS_ID, LOCAL_HTTP_SUBS_ID)
|
||||
|
||||
const val EMPTY_RULE_TIP = "暂无规则"
|
||||
|
||||
object ShortUrlSet {
|
||||
|
||||
@@ -8,18 +8,18 @@ import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import li.gkd.app.appScope
|
||||
import li.gkd.app.data.AppRule
|
||||
import li.gkd.app.data.CategoryConfig
|
||||
import li.gkd.db.CategoryConfig
|
||||
import li.gkd.app.data.GlobalRule
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.data.ResolvedAppGroup
|
||||
import li.gkd.app.data.ResolvedGlobalGroup
|
||||
import li.gkd.app.data.SubsConfig
|
||||
import li.gkd.app.data.SubsItem
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.SubsConfig
|
||||
import li.gkd.db.SubsItem
|
||||
import li.gkd.db.Db
|
||||
import java.net.URI
|
||||
|
||||
val subsItemsFlow by lazy {
|
||||
DbSet.subsItemDao.query().stateIn(appScope, SharingStarted.Eagerly, emptyList())
|
||||
Db.subsItemDao.query().stateIn(appScope, SharingStarted.Eagerly, emptyList())
|
||||
}
|
||||
|
||||
private fun getCheckUpdateUrl(
|
||||
@@ -78,7 +78,7 @@ val subsRefreshErrorsFlow by lazy {
|
||||
}
|
||||
|
||||
val latestRecordFlow by lazy {
|
||||
DbSet.actionLogDao.queryLatest().stateIn(appScope, SharingStarted.Eagerly, null)
|
||||
Db.actionLogDao.queryLatest().stateIn(appScope, SharingStarted.Eagerly, null)
|
||||
}
|
||||
val latestRecordDescFlow by lazy {
|
||||
combine(
|
||||
@@ -217,9 +217,9 @@ val ruleSummaryFlow by lazy {
|
||||
combine(
|
||||
usedSubsEntriesFlow,
|
||||
appInfoMapFlow,
|
||||
DbSet.appConfigDao.queryUsedList(),
|
||||
DbSet.subsConfigDao.queryUsedList(),
|
||||
DbSet.categoryConfigDao.queryUsedList(),
|
||||
Db.appConfigDao.queryUsedList(),
|
||||
Db.subsConfigDao.queryUsedList(),
|
||||
Db.categoryConfigDao.queryUsedList(),
|
||||
) { subsEntries, appInfoCache, appConfigs, subsConfigs, categoryConfigs ->
|
||||
val globalSubsConfigs = subsConfigs.filter { c -> c.type == SubsConfig.GlobalGroupType }
|
||||
val groupSubsConfigs = subsConfigs.filter { c -> c.type == SubsConfig.AppGroupType }
|
||||
|
||||
@@ -9,11 +9,13 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.withContext
|
||||
import li.gkd.app.data.RawSubscription
|
||||
import li.gkd.app.data.SubsConfig
|
||||
import li.gkd.app.data.SubsItem
|
||||
import li.gkd.db.SubsConfig
|
||||
import li.gkd.db.SubsItem
|
||||
import li.gkd.app.data.SubsVersion
|
||||
import li.gkd.app.db.DbSet
|
||||
import li.gkd.db.Db
|
||||
import li.gkd.app.ui.share.Loadable
|
||||
import li.gkd.db.LOCAL_HTTP_SUBS_ID
|
||||
import li.gkd.db.LOCAL_SUBS_ID
|
||||
import li.songe.json5.decodeFromJson5String
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
@@ -59,7 +61,7 @@ object SubscriptionStore {
|
||||
snapshotFlow.value = Loadable.Loading
|
||||
try {
|
||||
refreshRawSubscriptions(
|
||||
items = DbSet.subsItemDao.queryAll(),
|
||||
items = Db.subsItemDao.queryAll(),
|
||||
previous = SubscriptionSnapshot(),
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
@@ -73,7 +75,7 @@ object SubscriptionStore {
|
||||
private suspend fun ensureLocalSubscription() = withContext(Dispatchers.IO) {
|
||||
updateMutex.withStateLock {
|
||||
try {
|
||||
val items = DbSet.subsItemDao.queryAll()
|
||||
val items = Db.subsItemDao.queryAll()
|
||||
if (snapshotFlow.value !is Loadable.Ready) {
|
||||
refreshRawSubscriptions(
|
||||
items = items,
|
||||
@@ -87,7 +89,7 @@ object SubscriptionStore {
|
||||
)
|
||||
val file = subsFolder.resolve("$LOCAL_SUBS_ID.json")
|
||||
if (file.exists()) {
|
||||
DbSet.subsItemDao.insert(item)
|
||||
Db.subsItemDao.insert(item)
|
||||
refreshRawSubscriptions(listOf(item))
|
||||
} else {
|
||||
saveLocked(
|
||||
@@ -142,7 +144,7 @@ object SubscriptionStore {
|
||||
"订阅与订阅项id不一致: ${subscription.id} != ${defaultItem.id}"
|
||||
}
|
||||
updateMutex.withStateLock {
|
||||
val currentItem = DbSet.subsItemDao.queryAll().find { it.id == subscription.id }
|
||||
val currentItem = Db.subsItemDao.queryAll().find { it.id == subscription.id }
|
||||
try {
|
||||
saveLocked(
|
||||
subscription = subscription,
|
||||
@@ -185,12 +187,12 @@ object SubscriptionStore {
|
||||
var result: SubscriptionResult = SubscriptionResult.Busy
|
||||
updateMutex.withStateLock {
|
||||
val deleteSize = try {
|
||||
DbSet.withTransaction {
|
||||
val size = DbSet.subsItemDao.deleteById(*subscriptionIds)
|
||||
Db.withTransaction {
|
||||
val size = Db.subsItemDao.deleteById(*subscriptionIds)
|
||||
if (size > 0) {
|
||||
DbSet.subsConfigDao.deleteBySubsId(*subscriptionIds)
|
||||
DbSet.actionLogDao.deleteBySubsId(*subscriptionIds)
|
||||
DbSet.categoryConfigDao.deleteBySubsId(*subscriptionIds)
|
||||
Db.subsConfigDao.deleteBySubsId(*subscriptionIds)
|
||||
Db.actionLogDao.deleteBySubsId(*subscriptionIds)
|
||||
Db.categoryConfigDao.deleteBySubsId(*subscriptionIds)
|
||||
}
|
||||
size
|
||||
}
|
||||
@@ -251,7 +253,7 @@ object SubscriptionStore {
|
||||
if (updateMutex.mutex.isLocked) return@withContext SubscriptionResult.Busy
|
||||
var result: SubscriptionResult = SubscriptionResult.Busy
|
||||
updateMutex.withStateLock {
|
||||
val items = DbSet.subsItemDao.queryAll()
|
||||
val items = Db.subsItemDao.queryAll()
|
||||
if (items.any { it.updateUrl == url && it.id != oldItem?.id }) {
|
||||
result = failure("已有相同链接订阅")
|
||||
return@withStateLock
|
||||
@@ -326,7 +328,7 @@ object SubscriptionStore {
|
||||
var result: SubscriptionResult = SubscriptionResult.Busy
|
||||
updateMutex.withStateLock {
|
||||
val items = try {
|
||||
DbSet.subsItemDao.queryAll()
|
||||
Db.subsItemDao.queryAll()
|
||||
} catch (e: Exception) {
|
||||
if (snapshotFlow.value !is Loadable.Ready) {
|
||||
snapshotFlow.value = Loadable.Failure(e)
|
||||
@@ -380,7 +382,7 @@ object SubscriptionStore {
|
||||
val id = subscription.id
|
||||
val snapshot = snapshotFlow.value.value
|
||||
?: refreshRawSubscriptions(
|
||||
items = DbSet.subsItemDao.queryAll(),
|
||||
items = Db.subsItemDao.queryAll(),
|
||||
previous = SubscriptionSnapshot(),
|
||||
)
|
||||
val nextSubscription = if (
|
||||
@@ -398,15 +400,15 @@ object SubscriptionStore {
|
||||
val previousBytes = file.takeIf { it.exists() }?.readBytes()
|
||||
writeAtomic(file, json.encodeToString(nextSubscription).encodeToByteArray())
|
||||
try {
|
||||
DbSet.withTransaction {
|
||||
Db.withTransaction {
|
||||
if (newItem != null) {
|
||||
if (insertItem) {
|
||||
DbSet.subsItemDao.insert(newItem)
|
||||
Db.subsItemDao.insert(newItem)
|
||||
} else {
|
||||
DbSet.subsItemDao.update(newItem)
|
||||
Db.subsItemDao.update(newItem)
|
||||
}
|
||||
}
|
||||
DbSet.subsItemDao.updateMtime(id, System.currentTimeMillis())
|
||||
Db.subsItemDao.updateMtime(id, System.currentTimeMillis())
|
||||
cleanupConfigs(id, nextSubscription)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
@@ -510,7 +512,7 @@ object SubscriptionStore {
|
||||
val appKeys = subscription.apps.associate { app ->
|
||||
app.id to app.groups.map { it.key }.toHashSet()
|
||||
}
|
||||
val configs = DbSet.subsConfigDao.querySubsItemConfig(listOf(id))
|
||||
val configs = Db.subsConfigDao.querySubsItemConfig(listOf(id))
|
||||
val obsolete = configs.filter { config ->
|
||||
when (config.type) {
|
||||
SubsConfig.AppGroupType -> appKeys[config.appId]?.contains(config.groupKey) != true
|
||||
@@ -519,7 +521,7 @@ object SubscriptionStore {
|
||||
}
|
||||
}
|
||||
if (obsolete.isEmpty()) return 0
|
||||
DbSet.subsConfigDao.delete(*obsolete.toTypedArray())
|
||||
Db.subsConfigDao.delete(*obsolete.toTypedArray())
|
||||
LogUtils.d("清理已移除规则配置", "subsId=$id, delete=${obsolete.size}")
|
||||
return obsolete.size
|
||||
}
|
||||
@@ -527,7 +529,8 @@ object SubscriptionStore {
|
||||
private suspend fun fetchUpdate(entry: SubsEntry): RawSubscription? {
|
||||
val item = entry.subsItem
|
||||
val current = entry.subscription
|
||||
if (item.updateUrl == null || item.id < 0) return null
|
||||
val itemUpdateUrl = item.updateUrl ?: return null
|
||||
if (item.id < 0) return null
|
||||
val checkUrl = entry.checkUpdateUrl
|
||||
if (checkUrl != null && current != null) {
|
||||
try {
|
||||
@@ -539,7 +542,7 @@ object SubscriptionStore {
|
||||
LogUtils.d("快速检测更新失败", item, e.message)
|
||||
}
|
||||
}
|
||||
val updateUrl = current?.updateUrl ?: item.updateUrl
|
||||
val updateUrl = current?.updateUrl ?: itemUpdateUrl
|
||||
val text = try {
|
||||
client.get(updateUrl).bodyAsText()
|
||||
} catch (e: Exception) {
|
||||
|
||||
26
gkd-db/build.gradle.kts
Normal file
26
gkd-db/build.gradle.kts
Normal file
@@ -0,0 +1,26 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.androidx.room)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
alias(libs.plugins.google.ksp)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "li.gkd.db"
|
||||
}
|
||||
|
||||
room {
|
||||
schemaDirectory("$projectDir/schemas")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.kotlin.stdlib)
|
||||
|
||||
api(libs.androidx.room.runtime)
|
||||
implementation(libs.androidx.room.ktx)
|
||||
api(libs.androidx.room.paging)
|
||||
ksp(libs.androidx.room.compiler)
|
||||
|
||||
api(libs.kotlinx.serialization.core)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package li.gkd.app.data
|
||||
package li.gkd.db
|
||||
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Dao
|
||||
@@ -10,7 +9,6 @@ import androidx.room.PrimaryKey
|
||||
import androidx.room.Query
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.serialization.Serializable
|
||||
import li.gkd.app.a11y.STATE_CHANGED
|
||||
|
||||
@Serializable
|
||||
@Entity(tableName = "a11y_event_log")
|
||||
@@ -32,24 +30,6 @@ class A11yEventLog(
|
||||
return id
|
||||
}
|
||||
|
||||
val isStateChanged: Boolean
|
||||
get() = type == STATE_CHANGED
|
||||
|
||||
val fixedName: String
|
||||
get() {
|
||||
if (isStateChanged && name.startsWith(appId)) {
|
||||
return name.substring(appId.length)
|
||||
}
|
||||
if (name.contains("View") || name.contains("Layout") || viewSuffixes.any {
|
||||
name.startsWith(
|
||||
it
|
||||
)
|
||||
}) {
|
||||
return name.substring(name.lastIndexOf('.') + 1)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
@Dao
|
||||
interface A11yEventLogDao {
|
||||
@Insert
|
||||
@@ -88,19 +68,3 @@ class A11yEventLog(
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private val viewSuffixes = listOf(
|
||||
"android.widget.",
|
||||
"android.view.",
|
||||
"android.support.",
|
||||
)
|
||||
|
||||
fun AccessibilityEvent.toA11yEventLog(id: Int) = A11yEventLog(
|
||||
id = id,
|
||||
ctime = System.currentTimeMillis(),
|
||||
type = eventType,
|
||||
appId = packageName.toString(),
|
||||
name = className.toString(),
|
||||
desc = contentDescription?.toString(),
|
||||
text = text.map { it.toString() }
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
package li.gkd.app.data
|
||||
package li.gkd.db
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.room.ColumnInfo
|
||||
@@ -11,8 +11,6 @@ import androidx.room.Query
|
||||
import androidx.room.migration.AutoMigrationSpec
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.serialization.Serializable
|
||||
import li.gkd.app.util.format
|
||||
import li.gkd.app.util.getShowActivityId
|
||||
|
||||
@Serializable
|
||||
@Entity(
|
||||
@@ -30,11 +28,6 @@ data class ActionLog(
|
||||
@ColumnInfo(name = "rule_index") val ruleIndex: Int,
|
||||
@ColumnInfo(name = "rule_key") val ruleKey: Int? = null,
|
||||
) {
|
||||
|
||||
val showActivityId by lazy { getShowActivityId(appId, activityId) }
|
||||
|
||||
val date by lazy { ctime.format("MM-dd HH:mm:ss SSS") }
|
||||
|
||||
@DeleteTable.Entries(
|
||||
DeleteTable(tableName = "click_log")
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
package li.gkd.app.data
|
||||
package li.gkd.db
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.room.ColumnInfo
|
||||
@@ -10,23 +10,16 @@ import androidx.room.PrimaryKey
|
||||
import androidx.room.Query
|
||||
import androidx.room.migration.AutoMigrationSpec
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import li.gkd.app.util.format
|
||||
import li.gkd.app.util.getShowActivityId
|
||||
|
||||
@Entity(
|
||||
tableName = "activity_log_v2",
|
||||
)
|
||||
data class ActivityLog(
|
||||
// 不使用时间戳作为主键的原因
|
||||
// https://github.com/gkd-kit/gkd/issues/704
|
||||
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") val id: Int = 0,
|
||||
@ColumnInfo(name = "ctime") val ctime: Long,
|
||||
@ColumnInfo(name = "app_id") val appId: String,
|
||||
@ColumnInfo(name = "activity_id") val activityId: String? = null,
|
||||
) {
|
||||
val showActivityId by lazy { getShowActivityId(appId, activityId) }
|
||||
val date by lazy { ctime.format("HH:mm:ss SSS") }
|
||||
|
||||
@Dao
|
||||
interface ActivityLogDao {
|
||||
@Insert
|
||||
@@ -1,4 +1,4 @@
|
||||
package li.gkd.app.data
|
||||
package li.gkd.db
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Dao
|
||||
@@ -16,7 +16,7 @@ import kotlinx.serialization.Serializable
|
||||
tableName = "app_config",
|
||||
)
|
||||
data class AppConfig(
|
||||
@PrimaryKey @ColumnInfo(name = "id") val id: Long = System.currentTimeMillis(),
|
||||
@PrimaryKey @ColumnInfo(name = "id") val id: Long = buildUniqueTimeMillisId(),
|
||||
@ColumnInfo(name = "enable") val enable: Boolean,
|
||||
@ColumnInfo(name = "subs_id") val subsId: Long,
|
||||
@ColumnInfo(name = "app_id") val appId: String,
|
||||
@@ -1,27 +1,17 @@
|
||||
package li.gkd.app.db
|
||||
package li.gkd.db
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.AutoMigration
|
||||
import androidx.room.Database
|
||||
import androidx.room.DeleteColumn
|
||||
import androidx.room.RenameColumn
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.withTransaction
|
||||
import androidx.room.TypeConverter
|
||||
import androidx.room.TypeConverters
|
||||
import androidx.room.migration.AutoMigrationSpec
|
||||
import li.gkd.app.app
|
||||
import li.gkd.app.data.A11yEventLog
|
||||
import li.gkd.app.data.ActionLog
|
||||
import li.gkd.app.data.ActivityLog
|
||||
import li.gkd.app.data.AppConfig
|
||||
import li.gkd.app.data.AppVisitLog
|
||||
import li.gkd.app.data.CategoryConfig
|
||||
import li.gkd.app.data.Snapshot
|
||||
import li.gkd.app.data.SubsConfig
|
||||
import li.gkd.app.data.SubsItem
|
||||
import li.gkd.app.util.dbFolder
|
||||
import li.gkd.app.util.json
|
||||
import androidx.room.withTransaction
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@Database(
|
||||
version = 14,
|
||||
@@ -93,6 +83,12 @@ class Migration10To11Spec : AutoMigrationSpec
|
||||
|
||||
@Suppress("unused")
|
||||
class DbConverters {
|
||||
private val json = Json {
|
||||
ignoreUnknownKeys = true
|
||||
explicitNulls = false
|
||||
encodeDefaults = true
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromListStringToString(list: List<String>): String {
|
||||
return json.encodeToString(list)
|
||||
@@ -109,23 +105,35 @@ class DbConverters {
|
||||
}
|
||||
}
|
||||
|
||||
object DbSet {
|
||||
private val db by lazy {
|
||||
Room.databaseBuilder(
|
||||
app,
|
||||
AppDb::class.java,
|
||||
dbFolder.resolve("gkd.db").absolutePath
|
||||
).fallbackToDestructiveMigration(false).build()
|
||||
}
|
||||
val subsItemDao get() = db.subsItemDao()
|
||||
val subsConfigDao get() = db.subsConfigDao()
|
||||
val snapshotDao get() = db.snapshotDao()
|
||||
val actionLogDao get() = db.actionLogDao()
|
||||
val categoryConfigDao get() = db.categoryConfigDao()
|
||||
val activityLogDao get() = db.activityLogDao()
|
||||
val appConfigDao get() = db.appConfigDao()
|
||||
val appVisitLogDao get() = db.appVisitLogDao()
|
||||
val a11yEventLogDao get() = db.a11yEventLogDao()
|
||||
object Db {
|
||||
private var createDatabase: (() -> AppDb)? = null
|
||||
|
||||
suspend fun <T> withTransaction(block: suspend () -> T): T = db.withTransaction(block)
|
||||
fun initialize(context: Context, databasePath: String) {
|
||||
check(createDatabase == null) { "Db is already initialized" }
|
||||
val applicationContext = context.applicationContext
|
||||
createDatabase = {
|
||||
Room.databaseBuilder(
|
||||
applicationContext,
|
||||
AppDb::class.java,
|
||||
databasePath,
|
||||
).build()
|
||||
}
|
||||
}
|
||||
|
||||
private val database by lazy {
|
||||
checkNotNull(createDatabase) { "Db is not initialized" }.invoke()
|
||||
}
|
||||
|
||||
val subsItemDao get() = database.subsItemDao()
|
||||
val subsConfigDao get() = database.subsConfigDao()
|
||||
val snapshotDao get() = database.snapshotDao()
|
||||
val actionLogDao get() = database.actionLogDao()
|
||||
val categoryConfigDao get() = database.categoryConfigDao()
|
||||
val activityLogDao get() = database.activityLogDao()
|
||||
val appConfigDao get() = database.appConfigDao()
|
||||
val appVisitLogDao get() = database.appVisitLogDao()
|
||||
val a11yEventLogDao get() = database.a11yEventLogDao()
|
||||
|
||||
suspend fun <T> withTransaction(block: suspend () -> T): T =
|
||||
database.withTransaction(block)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package li.gkd.app.data
|
||||
package li.gkd.db
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Dao
|
||||
@@ -7,11 +7,7 @@ import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import li.gkd.app.META
|
||||
import li.gkd.app.a11y.launcherAppId
|
||||
import li.gkd.app.util.systemUiAppId
|
||||
|
||||
@Entity(
|
||||
tableName = "app_visit_log",
|
||||
@@ -25,17 +21,6 @@ data class AppVisitLog(
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insert(vararg objects: AppVisitLog): List<Long>
|
||||
|
||||
@Transaction
|
||||
suspend fun insert(oldAppId: String, newAppId: String, mtime: Long) {
|
||||
insert(
|
||||
AppVisitLog(oldAppId, fixAppVisitTime(oldAppId, mtime - 1)),
|
||||
AppVisitLog(newAppId, fixAppVisitTime(newAppId, mtime)),
|
||||
)
|
||||
if (appLogCount++ % 100 == 0) {
|
||||
deleteKeepLatest()
|
||||
}
|
||||
}
|
||||
|
||||
@Query("SELECT DISTINCT id FROM app_visit_log ORDER BY mtime DESC")
|
||||
fun query(): Flow<List<String>>
|
||||
|
||||
@@ -57,11 +42,3 @@ data class AppVisitLog(
|
||||
suspend fun deleteKeepLatest(): Int
|
||||
}
|
||||
}
|
||||
|
||||
private fun fixAppVisitTime(appId: String, t: Long): Long = when (appId) {
|
||||
META.appId -> t - 120_000
|
||||
launcherAppId, systemUiAppId -> t - 60_000
|
||||
else -> t
|
||||
}
|
||||
|
||||
private var appLogCount = 0
|
||||
@@ -1,4 +1,4 @@
|
||||
package li.gkd.app.data
|
||||
package li.gkd.db
|
||||
|
||||
interface BaseSnapshot {
|
||||
val id: Long
|
||||
@@ -1,4 +1,4 @@
|
||||
package li.gkd.app.data
|
||||
package li.gkd.db
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Dao
|
||||
@@ -17,7 +17,7 @@ import kotlinx.serialization.Serializable
|
||||
tableName = "category_config",
|
||||
)
|
||||
data class CategoryConfig(
|
||||
@PrimaryKey @ColumnInfo(name = "id") val id: Long = System.currentTimeMillis(),
|
||||
@PrimaryKey @ColumnInfo(name = "id") val id: Long = buildUniqueTimeMillisId(),
|
||||
@ColumnInfo(name = "enable") val enable: Boolean? = null,
|
||||
@ColumnInfo(name = "subs_id") val subsId: Long,
|
||||
@ColumnInfo(name = "category_key") val categoryKey: Int,
|
||||
14
gkd-db/src/main/kotlin/li/gkd/db/EntityId.kt
Normal file
14
gkd-db/src/main/kotlin/li/gkd/db/EntityId.kt
Normal file
@@ -0,0 +1,14 @@
|
||||
package li.gkd.db
|
||||
|
||||
private var lastEntityId = 0L
|
||||
|
||||
@Synchronized
|
||||
internal fun buildUniqueTimeMillisId(): Long {
|
||||
val currentTimeMillis = System.currentTimeMillis()
|
||||
if (currentTimeMillis > lastEntityId) {
|
||||
lastEntityId = currentTimeMillis
|
||||
} else {
|
||||
lastEntityId += 1
|
||||
}
|
||||
return lastEntityId
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package li.gkd.app.data
|
||||
package li.gkd.db
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Dao
|
||||
@@ -10,8 +10,6 @@ import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.serialization.Serializable
|
||||
import li.gkd.app.snapshot.SnapshotStore
|
||||
import li.gkd.app.util.format
|
||||
|
||||
@Entity(
|
||||
tableName = "snapshot",
|
||||
@@ -30,11 +28,6 @@ data class Snapshot(
|
||||
@ColumnInfo(name = "github_asset_id") val githubAssetId: Int? = null,
|
||||
|
||||
) : BaseSnapshot {
|
||||
|
||||
val date by lazy { id.format("MM-dd HH:mm:ss") }
|
||||
|
||||
val screenshotFile by lazy { SnapshotStore.screenshotFile(id) }
|
||||
|
||||
@Dao
|
||||
interface SnapshotDao {
|
||||
@Update
|
||||
@@ -60,5 +53,3 @@ data class Snapshot(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package li.gkd.app.data
|
||||
package li.gkd.db
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Dao
|
||||
@@ -12,22 +12,6 @@ import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.serialization.Serializable
|
||||
import li.gkd.app.util.isValidActivityId
|
||||
import li.gkd.app.util.isValidAppId
|
||||
|
||||
|
||||
private var lastId = 0L
|
||||
|
||||
@Synchronized
|
||||
private fun buildUniqueTimeMillisId(): Long {
|
||||
val id = System.currentTimeMillis()
|
||||
if (id > lastId) {
|
||||
lastId = id
|
||||
} else {
|
||||
lastId += 1
|
||||
}
|
||||
return lastId
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@Entity(
|
||||
@@ -134,113 +118,6 @@ data class SubsConfig(
|
||||
@Query("UPDATE subs_config SET enable = null WHERE type=${AppGroupType} AND subs_id=:subsItemId AND app_id=:appId AND group_key=:groupKey AND enable IS NOT NULL")
|
||||
suspend fun resetAppGroupTypeEnable(subsItemId: Long, appId: String, groupKey: Int): Int
|
||||
|
||||
@Transaction
|
||||
suspend fun batchResetAppGroupEnable(
|
||||
subsItemId: Long,
|
||||
list: List<Pair<RawSubscription.RawAppGroup, RawSubscription.RawApp>>
|
||||
): List<Pair<RawSubscription.RawAppGroup, RawSubscription.RawApp>> {
|
||||
return list.filter { (g, a) ->
|
||||
resetAppGroupTypeEnable(subsItemId, a.id, g.key) > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
data class ExcludeData(
|
||||
val appIds: Map<String, Boolean>,
|
||||
val activityIds: Set<Pair<String, String>>,
|
||||
) {
|
||||
val excludeAppIds = appIds.entries.filter { e -> e.value }.map { e -> e.key }.toHashSet()
|
||||
val includeAppIds = appIds.entries.filter { e -> !e.value }.map { e -> e.key }.toHashSet()
|
||||
|
||||
fun stringify(appId: String? = null): String {
|
||||
return if (appId != null) {
|
||||
activityIds.filter { e -> e.first == appId }.map { e -> e.second }.sorted()
|
||||
.joinToString("\n\n")
|
||||
} else {
|
||||
(appIds.entries.map { e ->
|
||||
if (e.value) {
|
||||
e.key
|
||||
} else {
|
||||
"!${e.key}"
|
||||
}
|
||||
} + activityIds.map { e -> "${e.first}/${e.second}" }).sorted().joinToString("\n\n")
|
||||
}
|
||||
}
|
||||
|
||||
fun clear(appId: String): ExcludeData {
|
||||
return copy(
|
||||
appIds = appIds.toMutableMap().apply {
|
||||
remove(appId)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fun switch(appId: String, activityId: String? = null): ExcludeData {
|
||||
return if (activityId == null) {
|
||||
copy(
|
||||
appIds = appIds.toMutableMap().apply {
|
||||
if (get(appId) != false) {
|
||||
set(appId, false)
|
||||
} else {
|
||||
set(appId, true)
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
copy(activityIds = activityIds.toMutableSet().apply {
|
||||
val e = appId to activityId
|
||||
if (contains(e)) {
|
||||
remove(e)
|
||||
} else {
|
||||
add(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val empty = ExcludeData(emptyMap(), emptySet())
|
||||
|
||||
fun parse(exclude: String?): ExcludeData {
|
||||
if (exclude.isNullOrBlank()) {
|
||||
return empty
|
||||
}
|
||||
val appIds = HashMap<String, Boolean>()
|
||||
val activityIds = HashSet<Pair<String, String>>()
|
||||
exclude.split('\n')
|
||||
.filter { it.isNotBlank() }
|
||||
.forEach { s ->
|
||||
if (s[0] == '!') {
|
||||
val appId = s.substring(1)
|
||||
if (appId.isValidAppId()) {
|
||||
appIds[appId] = false
|
||||
}
|
||||
} else {
|
||||
val a = s.split('/', limit = 2)
|
||||
val appId = a[0]
|
||||
if (appId.isValidAppId()) {
|
||||
val activityId = a.getOrNull(1)
|
||||
if (activityId != null) {
|
||||
if (activityId.isValidActivityId()) {
|
||||
activityIds.add(appId to activityId)
|
||||
}
|
||||
} else {
|
||||
appIds[appId] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ExcludeData(
|
||||
appIds = appIds,
|
||||
activityIds = activityIds,
|
||||
)
|
||||
}
|
||||
|
||||
fun parse(exclude: String?, appId: String): ExcludeData {
|
||||
if (exclude.isNullOrBlank()) return empty
|
||||
return parse(exclude.split('\n').joinToString("\n") { "$appId/$it" })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package li.gkd.app.data
|
||||
package li.gkd.db
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Dao
|
||||
@@ -12,8 +12,10 @@ import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.serialization.Serializable
|
||||
import li.gkd.app.util.LOCAL_SUBS_IDS
|
||||
import li.gkd.app.util.format
|
||||
|
||||
const val LOCAL_SUBS_ID = -2L
|
||||
const val LOCAL_HTTP_SUBS_ID = -1L
|
||||
val LOCAL_SUBS_IDS = arrayOf(LOCAL_SUBS_ID, LOCAL_HTTP_SUBS_ID)
|
||||
|
||||
@Serializable
|
||||
@Entity(
|
||||
@@ -34,8 +36,6 @@ data class SubsItem(
|
||||
val isLocal: Boolean
|
||||
get() = LOCAL_SUBS_IDS.contains(id)
|
||||
|
||||
val mtimeStr by lazy { mtime.format("yyyy-MM-dd HH:mm:ss") }
|
||||
|
||||
@Dao
|
||||
interface SubsItemDao {
|
||||
@Update
|
||||
@@ -20,6 +20,7 @@ plugins {
|
||||
rootProject.name = "gkd"
|
||||
include(
|
||||
":gkd-app",
|
||||
":gkd-db",
|
||||
":gkd-hidden-api",
|
||||
":gkd-selector",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user