perf: some changes

This commit is contained in:
lisonge 2024-02-22 22:34:21 +08:00
parent d683296a7b
commit 3d10cfe45a
14 changed files with 700 additions and 675 deletions

View File

@ -182,8 +182,12 @@ dependencies {
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)
implementation(libs.ktor.server.core)
implementation(libs.ktor.server.cio)
implementation(libs.ktor.server.content.negotiation)

View File

@ -1,6 +1,7 @@
package li.songe.gkd.data
import android.os.Parcelable
import androidx.paging.PagingSource
import androidx.room.ColumnInfo
import androidx.room.Dao
import androidx.room.Delete
@ -49,6 +50,9 @@ data class ClickLog(
@Query("SELECT * FROM click_log ORDER BY id DESC LIMIT 1000")
fun query(): Flow<List<ClickLog>>
@Query("SELECT * FROM click_log ORDER BY id DESC ")
fun pagingSource(): PagingSource<Int, ClickLog>
@Query("SELECT COUNT(*) FROM click_log")
fun count(): Flow<Int>

View File

@ -295,53 +295,51 @@ fun AppItemPage(
.padding(16.dp),
shape = RoundedCornerShape(16.dp),
) {
Column {
Text(text = "编辑禁用", modifier = Modifier
Text(text = "编辑禁用", modifier = Modifier
.clickable {
setExcludeGroupRaw(menuGroupRaw)
setMenuGroupRaw(null)
}
.padding(16.dp)
.fillMaxWidth())
if (editable) {
Text(text = "编辑规则组", modifier = Modifier
.clickable {
setExcludeGroupRaw(menuGroupRaw)
setEditGroupRaw(menuGroupRaw)
setMenuGroupRaw(null)
}
.padding(16.dp)
.fillMaxWidth())
if (editable) {
Text(text = "编辑规则组", modifier = Modifier
.clickable {
setEditGroupRaw(menuGroupRaw)
Text(text = "删除规则组", modifier = Modifier
.clickable {
vm.viewModelScope.launchTry(Dispatchers.IO) {
subsRaw ?: return@launchTry
val newSubsRaw = subsRaw.copy(
apps = subsRaw.apps
.toMutableList()
.apply {
set(
indexOfFirst { a -> a.id == appRawVal.id },
appRawVal.copy(
groups = appRawVal.groups
.filter { g -> g.key != menuGroupRaw.key }
)
)
}
)
updateSubscription(newSubsRaw)
DbSet.subsItemDao.update(subsItemVal.copy(mtime = System.currentTimeMillis()))
DbSet.subsConfigDao.delete(
subsItemVal.id, appRawVal.id, menuGroupRaw.key
)
toast("删除成功")
setMenuGroupRaw(null)
}
.padding(16.dp)
.fillMaxWidth())
Text(text = "删除规则组", modifier = Modifier
.clickable {
vm.viewModelScope.launchTry(Dispatchers.IO) {
subsRaw ?: return@launchTry
val newSubsRaw = subsRaw.copy(
apps = subsRaw.apps
.toMutableList()
.apply {
set(
indexOfFirst { a -> a.id == appRawVal.id },
appRawVal.copy(
groups = appRawVal.groups
.filter { g -> g.key != menuGroupRaw.key }
)
)
}
)
updateSubscription(newSubsRaw)
DbSet.subsItemDao.update(subsItemVal.copy(mtime = System.currentTimeMillis()))
DbSet.subsConfigDao.delete(
subsItemVal.id, appRawVal.id, menuGroupRaw.key
)
toast("删除成功")
setMenuGroupRaw(null)
}
}
.padding(16.dp)
.fillMaxWidth(),
color = MaterialTheme.colorScheme.error
)
}
}
.padding(16.dp)
.fillMaxWidth(),
color = MaterialTheme.colorScheme.error
)
}
}
}

View File

@ -181,37 +181,35 @@ fun CategoryPage(subsItemId: Long) {
.padding(16.dp),
shape = RoundedCornerShape(16.dp),
) {
Column {
enableGroupRadioOptions.forEach { option ->
val onClick: () -> Unit = {
vm.viewModelScope.launchTry(Dispatchers.IO) {
DbSet.categoryConfigDao.insert(
(categoryConfig ?: CategoryConfig(
enable = option.second,
subsItemId = subsItemId,
categoryKey = category.key
)).copy(enable = option.second)
)
}
enableGroupRadioOptions.forEach { option ->
val onClick: () -> Unit = {
vm.viewModelScope.launchTry(Dispatchers.IO) {
DbSet.categoryConfigDao.insert(
(categoryConfig ?: CategoryConfig(
enable = option.second,
subsItemId = subsItemId,
categoryKey = category.key
)).copy(enable = option.second)
)
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.selectable(
selected = (option.second == enable),
onClick = onClick
)
.padding(horizontal = 16.dp)
) {
RadioButton(
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.selectable(
selected = (option.second == enable),
onClick = onClick
)
Text(
text = option.first, modifier = Modifier.padding(start = 16.dp)
)
}
.padding(horizontal = 16.dp)
) {
RadioButton(
selected = (option.second == enable),
onClick = onClick
)
Text(
text = option.first, modifier = Modifier.padding(start = 16.dp)
)
}
}
}
@ -328,36 +326,34 @@ fun CategoryPage(subsItemId: Long) {
.padding(16.dp),
shape = RoundedCornerShape(16.dp),
) {
Column {
Text(text = "编辑", modifier = Modifier
.clickable {
setEditNameCategory(menuCategory)
Text(text = "编辑", modifier = Modifier
.clickable {
setEditNameCategory(menuCategory)
setMenuCategory(null)
}
.padding(16.dp)
.fillMaxWidth())
Text(text = "删除", modifier = Modifier
.clickable {
vm.viewModelScope.launchTry(Dispatchers.IO) {
subsItem?.apply {
updateSubscription(subsRawVal.copy(
categories = subsRawVal.categories.filter { c -> c.key != menuCategory.key }
))
DbSet.subsItemDao.update(copy(mtime = System.currentTimeMillis()))
}
DbSet.categoryConfigDao.deleteByCategoryKey(
subsItemId,
menuCategory.key
)
toast("删除成功")
setMenuCategory(null)
}
.padding(16.dp)
.fillMaxWidth())
Text(text = "删除", modifier = Modifier
.clickable {
vm.viewModelScope.launchTry(Dispatchers.IO) {
subsItem?.apply {
updateSubscription(subsRawVal.copy(
categories = subsRawVal.categories.filter { c -> c.key != menuCategory.key }
))
DbSet.subsItemDao.update(copy(mtime = System.currentTimeMillis()))
}
DbSet.categoryConfigDao.deleteByCategoryKey(
subsItemId,
menuCategory.key
)
toast("删除成功")
setMenuCategory(null)
}
}
.padding(16.dp)
.fillMaxWidth(),
color = MaterialTheme.colorScheme.error
)
}
}
.padding(16.dp)
.fillMaxWidth(),
color = MaterialTheme.colorScheme.error
)
}
}
}

View File

@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
@ -33,15 +32,18 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import com.blankj.utilcode.util.LogUtils
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootNavGraph
import kotlinx.coroutines.Dispatchers
@ -75,8 +77,9 @@ fun ClickLogPage() {
val navController = LocalNavController.current
val vm = hiltViewModel<ClickLogVm>()
val clickDataList by vm.clickDataListFlow.collectAsState()
// val clickDataList by vm.clickDataListFlow.collectAsState()
val clickLogCount by vm.clickLogCountFlow.collectAsState()
val clickDataItems = vm.pagingDataFlow.collectAsLazyPagingItems()
val appInfoCache by appInfoCacheFlow.collectAsState()
val subsIdToRaw by subsIdToRawFlow.collectAsState()
@ -105,6 +108,10 @@ fun ClickLogPage() {
mutableStateOf(false)
}
LaunchedEffect(key1 = clickDataItems.itemSnapshotList.items, block = {
LogUtils.d(clickDataItems.itemSnapshotList.items.size)
})
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
Scaffold(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = {
TopAppBar(
@ -121,7 +128,7 @@ fun ClickLogPage() {
},
title = { Text(text = "触发记录" + if (clickLogCount <= 0) "" else ("-$clickLogCount")) },
actions = {
if (clickDataList.isNotEmpty()) {
if (clickLogCount > 0) {
IconButton(onClick = { showDeleteDlg = true }) {
Icon(
imageVector = Icons.Outlined.Delete,
@ -131,71 +138,70 @@ fun ClickLogPage() {
}
})
}, content = { contentPadding ->
if (clickDataList.isNotEmpty()) {
LazyColumn(
modifier = Modifier.padding(contentPadding),
) {
items(clickDataList, { it.t0.id }) { (clickLog, group, rule) ->
Column(modifier = Modifier
.clickable {
previewClickLog = clickLog
}
.fillMaxWidth()
.padding(10.dp)) {
Row {
Text(
text = clickLog.id.format("MM-dd HH:mm:ss"),
fontFamily = FontFamily.Monospace
)
Spacer(modifier = Modifier.width(10.dp))
Text(
text = appInfoCache[clickLog.appId]?.name ?: clickLog.appId ?: ""
)
}
Spacer(modifier = Modifier.width(10.dp))
val showActivityId = if (clickLog.activityId != null) {
if (clickLog.appId != null && clickLog.activityId.startsWith(
clickLog.appId
)
) {
clickLog.activityId.substring(clickLog.appId.length)
} else {
clickLog.activityId
}
} else {
null
}
if (showActivityId != null) {
Text(
text = showActivityId,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
}
if (group?.name != null) {
Text(text = group.name)
}
if (rule?.name != null) {
Text(text = rule.name ?: "")
} else if ((group?.rules?.size ?: 0) > 1) {
Text(text = (if (clickLog.ruleKey != null) "key=${clickLog.ruleKey}, " else "") + "index=${clickLog.ruleIndex}")
}
LazyColumn(
modifier = Modifier.padding(contentPadding),
) {
items(
count = clickDataItems.itemCount,
key = clickDataItems.itemKey { c -> c.t0.id }
) { i ->
val (clickLog, group, rule) = clickDataItems[i] ?: return@items
Column(modifier = Modifier
.clickable {
previewClickLog = clickLog
}
.fillMaxWidth()
.padding(10.dp)) {
Row {
Text(
text = clickLog.id.format("MM-dd HH:mm:ss"),
fontFamily = FontFamily.Monospace
)
Spacer(modifier = Modifier.width(10.dp))
Text(
text = appInfoCache[clickLog.appId]?.name ?: clickLog.appId ?: ""
)
}
Spacer(modifier = Modifier.width(10.dp))
val showActivityId = if (clickLog.activityId != null) {
if (clickLog.appId != null && clickLog.activityId.startsWith(
clickLog.appId
)
) {
clickLog.activityId.substring(clickLog.appId.length)
} else {
clickLog.activityId
}
} else {
null
}
if (showActivityId != null) {
Text(
text = showActivityId,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
}
if (group?.name != null) {
Text(text = group.name)
}
if (rule?.name != null) {
Text(text = rule.name ?: "")
} else if ((group?.rules?.size ?: 0) > 1) {
Text(text = (if (clickLog.ruleKey != null) "key=${clickLog.ruleKey}, " else "") + "index=${clickLog.ruleIndex}")
}
HorizontalDivider()
}
item {
Spacer(modifier = Modifier.height(10.dp))
}
HorizontalDivider()
}
} else {
Column(
modifier = Modifier
.padding(contentPadding)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
item {
Spacer(modifier = Modifier.height(40.dp))
Text(text = "暂无记录")
if (clickLogCount == 0) {
Text(
text = "暂无记录",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
}
}
})
@ -214,124 +220,121 @@ fun ClickLogPage() {
}
val appInfo = appInfoCache[clickLog.appId]
Column {
Text(text = "查看规则组", modifier = Modifier
.clickable {
clickLog.appId ?: return@clickable
if (clickLog.groupType == SubsConfig.AppGroupType) {
navController.navigate(
AppItemPageDestination(
clickLog.subsId, clickLog.appId, clickLog.groupKey
)
Text(text = "查看规则组", modifier = Modifier
.clickable {
clickLog.appId ?: return@clickable
if (clickLog.groupType == SubsConfig.AppGroupType) {
navController.navigate(
AppItemPageDestination(
clickLog.subsId, clickLog.appId, clickLog.groupKey
)
} else if (clickLog.groupType == SubsConfig.GlobalGroupType) {
navController.navigate(
GlobalRulePageDestination(
clickLog.subsId, clickLog.groupKey
)
)
}
previewClickLog = null
}
.fillMaxWidth()
.padding(16.dp))
if (clickLog.groupType == SubsConfig.GlobalGroupType && clickLog.appId != null) {
val group =
subsIdToRaw[clickLog.subsId]?.globalGroups?.find { g -> g.key == clickLog.groupKey }
val appChecked = if (group != null) {
getChecked(
oldExclude,
group,
clickLog.appId,
appInfo
)
} else {
null
}
if (appChecked != null) {
Text(
text = if (appChecked) "在此应用禁用" else "移除在此应用的禁用",
modifier = Modifier
.clickable(
onClick = vm.viewModelScope.launchAsFn(
Dispatchers.IO
) {
val subsConfig = previewConfig ?: SubsConfig(
type = SubsConfig.GlobalGroupType,
subsItemId = clickLog.subsId,
groupKey = clickLog.groupKey,
)
val newSubsConfig = subsConfig.copy(
exclude = oldExclude
.copy(
appIds = oldExclude.appIds
.toMutableMap()
.apply {
set(clickLog.appId, appChecked)
})
.stringify()
)
DbSet.subsConfigDao.insert(newSubsConfig)
toast("更新禁用")
})
.fillMaxWidth()
.padding(16.dp),
} else if (clickLog.groupType == SubsConfig.GlobalGroupType) {
navController.navigate(
GlobalRulePageDestination(
clickLog.subsId, clickLog.groupKey
)
)
}
previewClickLog = null
}
if (clickLog.appId != null && clickLog.activityId != null) {
val disabled =
oldExclude.activityIds.contains(clickLog.appId to clickLog.activityId)
.fillMaxWidth()
.padding(16.dp))
if (clickLog.groupType == SubsConfig.GlobalGroupType && clickLog.appId != null) {
val group =
subsIdToRaw[clickLog.subsId]?.globalGroups?.find { g -> g.key == clickLog.groupKey }
val appChecked = if (group != null) {
getChecked(
oldExclude,
group,
clickLog.appId,
appInfo
)
} else {
null
}
if (appChecked != null) {
Text(
text = if (disabled) "移除在此页面的禁用" else "在此页面禁用",
text = if (appChecked) "在此应用禁用" else "移除在此应用的禁用",
modifier = Modifier
.clickable(onClick = vm.viewModelScope.launchAsFn(Dispatchers.IO) {
val subsConfig =
if (clickLog.groupType == SubsConfig.AppGroupType) {
previewConfig ?: SubsConfig(
type = SubsConfig.AppGroupType,
subsItemId = clickLog.subsId,
appId = clickLog.appId,
groupKey = clickLog.groupKey,
)
} else {
previewConfig ?: SubsConfig(
type = SubsConfig.GlobalGroupType,
subsItemId = clickLog.subsId,
groupKey = clickLog.groupKey,
)
}
val newSubsConfig = subsConfig.copy(
exclude = oldExclude
.switch(
clickLog.appId,
clickLog.activityId
)
.stringify()
)
DbSet.subsConfigDao.insert(newSubsConfig)
toast("更新禁用")
})
.clickable(
onClick = vm.viewModelScope.launchAsFn(
Dispatchers.IO
) {
val subsConfig = previewConfig ?: SubsConfig(
type = SubsConfig.GlobalGroupType,
subsItemId = clickLog.subsId,
groupKey = clickLog.groupKey,
)
val newSubsConfig = subsConfig.copy(
exclude = oldExclude
.copy(
appIds = oldExclude.appIds
.toMutableMap()
.apply {
set(clickLog.appId, appChecked)
})
.stringify()
)
DbSet.subsConfigDao.insert(newSubsConfig)
toast("更新禁用")
})
.fillMaxWidth()
.padding(16.dp),
)
}
}
if (clickLog.appId != null && clickLog.activityId != null) {
val disabled =
oldExclude.activityIds.contains(clickLog.appId to clickLog.activityId)
Text(
text = "删除记录",
text = if (disabled) "移除在此页面的禁用" else "在此页面禁用",
modifier = Modifier
.clickable(onClick = scope.launchAsFn {
previewClickLog = null
DbSet.clickLogDao.delete(clickLog)
toast("删除成功")
.clickable(onClick = vm.viewModelScope.launchAsFn(Dispatchers.IO) {
val subsConfig =
if (clickLog.groupType == SubsConfig.AppGroupType) {
previewConfig ?: SubsConfig(
type = SubsConfig.AppGroupType,
subsItemId = clickLog.subsId,
appId = clickLog.appId,
groupKey = clickLog.groupKey,
)
} else {
previewConfig ?: SubsConfig(
type = SubsConfig.GlobalGroupType,
subsItemId = clickLog.subsId,
groupKey = clickLog.groupKey,
)
}
val newSubsConfig = subsConfig.copy(
exclude = oldExclude
.switch(
clickLog.appId,
clickLog.activityId
)
.stringify()
)
DbSet.subsConfigDao.insert(newSubsConfig)
toast("更新禁用")
})
.fillMaxWidth()
.padding(16.dp),
color = MaterialTheme.colorScheme.error
)
}
}
Text(
text = "删除记录",
modifier = Modifier
.clickable(onClick = scope.launchAsFn {
previewClickLog = null
DbSet.clickLogDao.delete(clickLog)
toast("删除成功")
})
.fillMaxWidth()
.padding(16.dp),
color = MaterialTheme.colorScheme.error
)
}
}
}

View File

@ -2,6 +2,10 @@ package li.songe.gkd.ui
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import androidx.paging.map
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
@ -15,9 +19,10 @@ import javax.inject.Inject
@HiltViewModel
class ClickLogVm @Inject constructor() : ViewModel() {
val clickDataListFlow =
combine(DbSet.clickLogDao.query(), subsIdToRawFlow) { clickLogs, subsIdToRaw ->
clickLogs.map { c ->
val pagingDataFlow = Pager(PagingConfig(pageSize = 100)) { DbSet.clickLogDao.pagingSource() }
.flow.cachedIn(viewModelScope)
.combine(subsIdToRawFlow) { pagingData, subsIdToRaw ->
pagingData.map { c ->
val group = if (c.groupType == SubsConfig.AppGroupType) {
val app = subsIdToRaw[c.subsId]?.apps?.find { a -> a.id == c.appId }
app?.groups?.find { g -> g.key == c.groupKey }
@ -33,7 +38,28 @@ class ClickLogVm @Inject constructor() : ViewModel() {
}
Tuple3(c, group, rule)
}
}.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
}
// val clickDataListFlow =
// combine(DbSet.clickLogDao.query(), subsIdToRawFlow) { clickLogs, subsIdToRaw ->
// clickLogs.map { c ->
// val group = if (c.groupType == SubsConfig.AppGroupType) {
// val app = subsIdToRaw[c.subsId]?.apps?.find { a -> a.id == c.appId }
// app?.groups?.find { g -> g.key == c.groupKey }
// } else {
// subsIdToRaw[c.subsId]?.globalGroups?.find { g -> g.key == c.groupKey }
// }
// val rule = group?.rules?.run {
// if (c.ruleKey != null) {
// find { r -> r.key == c.ruleKey }
// } else {
// getOrNull(c.ruleIndex)
// }
// }
// Tuple3(c, group, rule)
// }
// }.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
val clickLogCountFlow =
DbSet.clickLogDao.count().stateIn(viewModelScope, SharingStarted.Eagerly, 0)

View File

@ -122,7 +122,7 @@ fun GlobalRuleExcludePage(subsItemId: Long, groupKey: Int) {
AppBarTextField(
value = searchStr,
onValueChange = { newValue -> vm.searchStrFlow.value = newValue.trim() },
hint = "请输入应用名称",
hint = "请输入应用名称/ID",
modifier = Modifier.focusRequester(focusRequester)
)
} else {

View File

@ -272,50 +272,48 @@ fun GlobalRulePage(subsItemId: Long, focusGroupKey: Int? = null) {
.padding(16.dp),
shape = RoundedCornerShape(16.dp),
) {
Column {
Text(text = "编辑禁用", modifier = Modifier
.clickable {
setMenuGroupRaw(null)
navController.navigate(
GlobalRuleExcludePageDestination(
subsItemId,
menuGroupRaw.key
)
Text(text = "编辑禁用", modifier = Modifier
.clickable {
setMenuGroupRaw(null)
navController.navigate(
GlobalRuleExcludePageDestination(
subsItemId,
menuGroupRaw.key
)
)
}
.padding(16.dp)
.fillMaxWidth())
if (editable) {
Text(text = "编辑规则组", modifier = Modifier
.clickable {
setEditGroupRaw(menuGroupRaw)
setMenuGroupRaw(null)
}
.padding(16.dp)
.fillMaxWidth())
if (editable) {
Text(text = "编辑规则组", modifier = Modifier
.clickable {
setEditGroupRaw(menuGroupRaw)
setMenuGroupRaw(null)
}
.padding(16.dp)
.fillMaxWidth())
Text(text = "删除规则组", modifier = Modifier
.clickable {
setMenuGroupRaw(null)
vm.viewModelScope.launchTry {
if (!getDialogResult("是否删除${menuGroupRaw.name}")) return@launchTry
updateSubscription(
rawSubs.copy(
globalGroups = rawSubs.globalGroups.filter { g -> g.key != menuGroupRaw.key }
)
Text(text = "删除规则组", modifier = Modifier
.clickable {
setMenuGroupRaw(null)
vm.viewModelScope.launchTry {
if (!getDialogResult("是否删除${menuGroupRaw.name}")) return@launchTry
updateSubscription(
rawSubs.copy(
globalGroups = rawSubs.globalGroups.filter { g -> g.key != menuGroupRaw.key }
)
val subsConfig =
subsConfigs.find { it.groupKey == menuGroupRaw.key }
if (subsConfig != null) {
DbSet.subsConfigDao.delete(subsConfig)
}
DbSet.subsItemDao.updateMtime(rawSubs.id)
)
val subsConfig =
subsConfigs.find { it.groupKey == menuGroupRaw.key }
if (subsConfig != null) {
DbSet.subsConfigDao.delete(subsConfig)
}
DbSet.subsItemDao.updateMtime(rawSubs.id)
}
.padding(16.dp)
.fillMaxWidth(),
color = MaterialTheme.colorScheme.error
)
}
}
.padding(16.dp)
.fillMaxWidth(),
color = MaterialTheme.colorScheme.error
)
}
}
}

View File

@ -37,11 +37,11 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
@ -123,63 +123,59 @@ fun SnapshotPage() {
}
})
}, content = { contentPadding ->
if (snapshots.isNotEmpty()) {
LazyColumn(
modifier = Modifier.padding(contentPadding),
) {
items(snapshots, { it.id }) { snapshot ->
Column(modifier = Modifier
.fillMaxWidth()
.clickable {
selectedSnapshot = snapshot
}
.padding(10.dp)) {
Row {
Text(
text = snapshot.id.format("MM-dd HH:mm:ss"),
fontFamily = FontFamily.Monospace
)
Spacer(modifier = Modifier.width(10.dp))
Text(
text = snapshot.appName ?: snapshot.appId ?: snapshot.id.toString(),
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
}
if (snapshot.activityId != null) {
val showActivityId =
if (snapshot.appId != null && snapshot.activityId.startsWith(
snapshot.appId
)
) {
snapshot.activityId.substring(snapshot.appId.length)
} else {
snapshot.activityId
}
Spacer(modifier = Modifier.width(10.dp))
Text(
text = showActivityId, overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
}
LazyColumn(
modifier = Modifier.padding(contentPadding),
) {
items(snapshots, { it.id }) { snapshot ->
Column(modifier = Modifier
.fillMaxWidth()
.clickable {
selectedSnapshot = snapshot
}
.padding(10.dp)) {
Row {
Text(
text = snapshot.id.format("MM-dd HH:mm:ss"),
fontFamily = FontFamily.Monospace
)
Spacer(modifier = Modifier.width(10.dp))
Text(
text = snapshot.appName ?: snapshot.appId ?: snapshot.id.toString(),
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
}
if (snapshot.activityId != null) {
val showActivityId =
if (snapshot.appId != null && snapshot.activityId.startsWith(
snapshot.appId
)
) {
snapshot.activityId.substring(snapshot.appId.length)
} else {
snapshot.activityId
}
Spacer(modifier = Modifier.width(10.dp))
Text(
text = showActivityId, overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
}
HorizontalDivider()
}
item {
Spacer(modifier = Modifier.height(10.dp))
}
HorizontalDivider()
}
} else {
Column(
modifier = Modifier
.padding(contentPadding)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
item {
Spacer(modifier = Modifier.height(40.dp))
Text(text = "暂无记录")
if (snapshots.isEmpty()) {
Text(
text = "暂无记录",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
}
}
})
selectedSnapshot?.let { snapshotVal ->
@ -190,121 +186,119 @@ fun SnapshotPage() {
.padding(16.dp),
shape = RoundedCornerShape(16.dp),
) {
Column {
val modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
Text(
text = "查看", modifier = Modifier
.clickable(onClick = scope.launchAsFn {
navController.navigate(
ImagePreviewPageDestination(
filePath = snapshotVal.screenshotFile.absolutePath,
title = snapshotVal.appName,
)
val modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
Text(
text = "查看", modifier = Modifier
.clickable(onClick = scope.launchAsFn {
navController.navigate(
ImagePreviewPageDestination(
filePath = snapshotVal.screenshotFile.absolutePath,
title = snapshotVal.appName,
)
)
selectedSnapshot = null
})
.then(modifier)
)
HorizontalDivider()
Text(
text = "分享",
modifier = Modifier
.clickable(onClick = vm.viewModelScope.launchAsFn {
val zipFile = SnapshotExt.getSnapshotZipFile(snapshotVal.id)
context.shareFile(zipFile, "分享快照文件")
selectedSnapshot = null
})
.then(modifier)
)
HorizontalDivider()
if (snapshotVal.githubAssetId != null) {
Text(
text = "复制链接", modifier = Modifier
.clickable(onClick = {
selectedSnapshot = null
ClipboardUtils.copyText(IMPORT_BASE_URL + snapshotVal.githubAssetId)
toast("复制成功")
})
.then(modifier)
)
HorizontalDivider()
} else {
Text(
text = "分享",
modifier = Modifier
.clickable(onClick = vm.viewModelScope.launchAsFn {
val zipFile = SnapshotExt.getSnapshotZipFile(snapshotVal.id)
context.shareFile(zipFile, "分享快照文件")
text = "生成链接(需科学上网)", modifier = Modifier
.clickable(onClick = {
selectedSnapshot = null
vm.uploadZip(snapshotVal)
})
.then(modifier)
)
HorizontalDivider()
if (snapshotVal.githubAssetId != null) {
Text(
text = "复制链接", modifier = Modifier
.clickable(onClick = {
selectedSnapshot = null
ClipboardUtils.copyText(IMPORT_BASE_URL + snapshotVal.githubAssetId)
toast("复制成功")
})
.then(modifier)
)
} else {
Text(
text = "生成链接(需科学上网)", modifier = Modifier
.clickable(onClick = {
selectedSnapshot = null
vm.uploadZip(snapshotVal)
})
.then(modifier)
)
}
HorizontalDivider()
Text(
text = "保存截图到相册",
modifier = Modifier
.clickable(onClick = vm.viewModelScope.launchAsFn {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
val isGranted =
requestPermissionLauncher.launchForResult(Manifest.permission.WRITE_EXTERNAL_STORAGE)
if (!isGranted) {
toast("保存失败,暂无权限")
return@launchAsFn
}
}
ImageUtils.save2Album(
ImageUtils.getBitmap(snapshotVal.screenshotFile),
Bitmap.CompressFormat.PNG,
true
)
toast("保存成功")
selectedSnapshot = null
})
.then(modifier)
)
HorizontalDivider()
Text(
text = "替换截图(去除隐私)",
modifier = Modifier
.clickable(onClick = vm.viewModelScope.launchAsFn {
val uri = pickContentLauncher.launchForImageResult()
withContext(Dispatchers.IO) {
val oldBitmap = ImageUtils.getBitmap(snapshotVal.screenshotFile)
val newBytes = UriUtils.uri2Bytes(uri)
val newBitmap = ImageUtils.getBitmap(newBytes, 0)
if (oldBitmap.width == newBitmap.width && oldBitmap.height == newBitmap.height) {
snapshotVal.screenshotFile.writeBytes(newBytes)
File(snapshotZipDir, "${snapshotVal.id}.zip").apply {
if (exists()) delete()
}
if (snapshotVal.githubAssetId != null) {
// 当本地快照变更时, 移除快照链接
DbSet.snapshotDao.update(snapshotVal.copy(githubAssetId = null))
}
} else {
toast("截图尺寸不一致,无法替换")
return@withContext
}
}
toast("替换成功")
selectedSnapshot = null
})
.then(modifier)
)
HorizontalDivider()
Text(
text = "删除", modifier = Modifier
.clickable(onClick = scope.launchAsFn {
DbSet.snapshotDao.delete(snapshotVal)
withContext(Dispatchers.IO) {
SnapshotExt.removeAssets(snapshotVal.id)
}
selectedSnapshot = null
})
.then(modifier), color = colorScheme.error
)
}
HorizontalDivider()
Text(
text = "保存截图到相册",
modifier = Modifier
.clickable(onClick = vm.viewModelScope.launchAsFn {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
val isGranted =
requestPermissionLauncher.launchForResult(Manifest.permission.WRITE_EXTERNAL_STORAGE)
if (!isGranted) {
toast("保存失败,暂无权限")
return@launchAsFn
}
}
ImageUtils.save2Album(
ImageUtils.getBitmap(snapshotVal.screenshotFile),
Bitmap.CompressFormat.PNG,
true
)
toast("保存成功")
selectedSnapshot = null
})
.then(modifier)
)
HorizontalDivider()
Text(
text = "替换截图(去除隐私)",
modifier = Modifier
.clickable(onClick = vm.viewModelScope.launchAsFn {
val uri = pickContentLauncher.launchForImageResult()
withContext(Dispatchers.IO) {
val oldBitmap = ImageUtils.getBitmap(snapshotVal.screenshotFile)
val newBytes = UriUtils.uri2Bytes(uri)
val newBitmap = ImageUtils.getBitmap(newBytes, 0)
if (oldBitmap.width == newBitmap.width && oldBitmap.height == newBitmap.height) {
snapshotVal.screenshotFile.writeBytes(newBytes)
File(snapshotZipDir, "${snapshotVal.id}.zip").apply {
if (exists()) delete()
}
if (snapshotVal.githubAssetId != null) {
// 当本地快照变更时, 移除快照链接
DbSet.snapshotDao.update(snapshotVal.copy(githubAssetId = null))
}
} else {
toast("截图尺寸不一致,无法替换")
return@withContext
}
}
toast("替换成功")
selectedSnapshot = null
})
.then(modifier)
)
HorizontalDivider()
Text(
text = "删除", modifier = Modifier
.clickable(onClick = scope.launchAsFn {
DbSet.snapshotDao.delete(snapshotVal)
withContext(Dispatchers.IO) {
SnapshotExt.removeAssets(snapshotVal.id)
}
selectedSnapshot = null
})
.then(modifier), color = colorScheme.error
)
}
}
}

View File

@ -2,7 +2,6 @@ package li.songe.gkd.ui
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
@ -50,6 +49,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
@ -145,7 +145,7 @@ fun SubsPage(
AppBarTextField(
value = searchStr,
onValueChange = { newValue -> vm.searchStrFlow.value = newValue.trim() },
hint = "请输入应用名称",
hint = "请输入应用名称/ID",
modifier = Modifier.focusRequester(focusRequester)
)
} else {
@ -265,24 +265,15 @@ fun SubsPage(
})
}
item {
Spacer(modifier = Modifier.height(40.dp))
if (appAndConfigs.isEmpty()) {
Spacer(modifier = Modifier.height(40.dp))
Column(
Text(
text = if (searchStr.isNotEmpty()) "暂无搜索结果" else "暂无规则",
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
if (searchStr.isNotEmpty()) {
Text(text = "暂无搜索结果")
} else {
Text(text = "暂无规则")
}
}
textAlign = TextAlign.Center
)
}
}
item {
Spacer(modifier = Modifier.height(20.dp))
}
}
}
@ -431,31 +422,29 @@ fun SubsPage(
.padding(16.dp),
shape = RoundedCornerShape(16.dp),
) {
Column {
Text(text = "复制", modifier = Modifier
.clickable {
ClipboardUtils.copyText(
json.encodeToJson5String(menuAppRawVal)
)
toast("复制成功")
menuRawApp = null
Text(text = "复制", modifier = Modifier
.clickable {
ClipboardUtils.copyText(
json.encodeToJson5String(menuAppRawVal)
)
toast("复制成功")
menuRawApp = null
}
.fillMaxWidth()
.padding(16.dp))
Text(text = "删除", modifier = Modifier
.clickable {
// 也许需要二次确认
vm.viewModelScope.launchTry(Dispatchers.IO) {
updateSubscription(subsRaw.copy(apps = subsRaw.apps.filter { a -> a.id != menuAppRawVal.id }))
DbSet.subsItemDao.update(subsItemVal.copy(mtime = System.currentTimeMillis()))
DbSet.subsConfigDao.delete(subsItemVal.id, menuAppRawVal.id)
toast("删除成功")
}
.fillMaxWidth()
.padding(16.dp))
Text(text = "删除", modifier = Modifier
.clickable {
// 也许需要二次确认
vm.viewModelScope.launchTry(Dispatchers.IO) {
updateSubscription(subsRaw.copy(apps = subsRaw.apps.filter { a -> a.id != menuAppRawVal.id }))
DbSet.subsItemDao.update(subsItemVal.copy(mtime = System.currentTimeMillis()))
DbSet.subsConfigDao.delete(subsItemVal.id, menuAppRawVal.id)
toast("删除成功")
}
menuRawApp = null
}
.fillMaxWidth()
.padding(16.dp), color = MaterialTheme.colorScheme.error)
}
menuRawApp = null
}
.fillMaxWidth()
.padding(16.dp), color = MaterialTheme.colorScheme.error)
}
}
}

View File

@ -48,6 +48,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
@ -106,7 +107,7 @@ fun useAppListPage(): ScaffoldExt {
AppBarTextField(
value = searchStr,
onValueChange = { newValue -> vm.searchStrFlow.value = newValue.trim() },
hint = "请输入应用名称",
hint = "请输入应用名称/ID",
modifier = Modifier.focusRequester(focusRequester)
)
} else {
@ -280,6 +281,16 @@ fun useAppListPage(): ScaffoldExt {
}
}
}
item {
Spacer(modifier = Modifier.height(40.dp))
if (orderedAppInfos.isEmpty() && searchStr.isNotEmpty()) {
Text(
text = "暂无搜索结果",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
}
}
}
}

View File

@ -102,29 +102,27 @@ fun useSettingsPage(): ScaffoldExt {
.padding(16.dp),
shape = RoundedCornerShape(16.dp),
) {
Column {
updateTimeRadioOptions.forEach { option ->
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.selectable(selected = (option.second == store.updateSubsInterval),
onClick = {
storeFlow.value =
store.copy(updateSubsInterval = option.second)
})
.padding(horizontal = 16.dp)
) {
RadioButton(
selected = (option.second == store.updateSubsInterval),
updateTimeRadioOptions.forEach { option ->
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.selectable(selected = (option.second == store.updateSubsInterval),
onClick = {
storeFlow.value = store.copy(updateSubsInterval = option.second)
storeFlow.value =
store.copy(updateSubsInterval = option.second)
})
Text(
text = option.first, modifier = Modifier.padding(start = 16.dp)
)
}
.padding(horizontal = 16.dp)
) {
RadioButton(
selected = (option.second == store.updateSubsInterval),
onClick = {
storeFlow.value = store.copy(updateSubsInterval = option.second)
})
Text(
text = option.first, modifier = Modifier.padding(start = 16.dp)
)
}
}
}
@ -139,28 +137,26 @@ fun useSettingsPage(): ScaffoldExt {
.padding(16.dp),
shape = RoundedCornerShape(16.dp),
) {
Column {
darkThemeRadioOptions.forEach { option ->
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.selectable(selected = (option.second == store.enableDarkTheme),
onClick = {
storeFlow.value =
store.copy(enableDarkTheme = option.second)
})
.padding(horizontal = 16.dp)
) {
RadioButton(
selected = (option.second == store.enableDarkTheme),
darkThemeRadioOptions.forEach { option ->
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.selectable(selected = (option.second == store.enableDarkTheme),
onClick = {
storeFlow.value = store.copy(enableDarkTheme = option.second)
storeFlow.value =
store.copy(enableDarkTheme = option.second)
})
Text(
text = option.first, modifier = Modifier.padding(start = 16.dp)
)
}
.padding(horizontal = 16.dp)
) {
RadioButton(
selected = (option.second == store.enableDarkTheme),
onClick = {
storeFlow.value = store.copy(enableDarkTheme = option.second)
})
Text(
text = option.first, modifier = Modifier.padding(start = 16.dp)
)
}
}
}
@ -215,49 +211,47 @@ fun useSettingsPage(): ScaffoldExt {
.padding(16.dp),
shape = RoundedCornerShape(16.dp),
) {
Column {
val modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
Text(
text = "调用系统分享", modifier = Modifier
.clickable(onClick = {
showShareLogDlg = false
vm.viewModelScope.launchTry(Dispatchers.IO) {
val logZipFile = File(logZipDir, "log.zip")
ZipUtils.zipFiles(LogUtils.getLogFiles(), logZipFile)
val uri = FileProvider.getUriForFile(
context, "${context.packageName}.provider", logZipFile
)
val intent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_STREAM, uri)
type = "application/zip"
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(
Intent.createChooser(
intent, "分享日志文件"
)
)
val modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
Text(
text = "调用系统分享", modifier = Modifier
.clickable(onClick = {
showShareLogDlg = false
vm.viewModelScope.launchTry(Dispatchers.IO) {
val logZipFile = File(logZipDir, "log.zip")
ZipUtils.zipFiles(LogUtils.getLogFiles(), logZipFile)
val uri = FileProvider.getUriForFile(
context, "${context.packageName}.provider", logZipFile
)
val intent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_STREAM, uri)
type = "application/zip"
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
})
.then(modifier)
)
Text(
text = "生成链接(需科学上网)", modifier = Modifier
.clickable(onClick = {
showShareLogDlg = false
vm.viewModelScope.launchTry(Dispatchers.IO) {
val logZipFile = File(logZipDir, "log.zip")
ZipUtils.zipFiles(LogUtils.getLogFiles(), logZipFile)
vm.uploadZip(logZipFile)
}
})
.then(modifier)
)
}
context.startActivity(
Intent.createChooser(
intent, "分享日志文件"
)
)
}
})
.then(modifier)
)
Text(
text = "生成链接(需科学上网)", modifier = Modifier
.clickable(onClick = {
showShareLogDlg = false
vm.viewModelScope.launchTry(Dispatchers.IO) {
val logZipFile = File(logZipDir, "log.zip")
ZipUtils.zipFiles(LogUtils.getLogFiles(), logZipFile)
vm.uploadZip(logZipFile)
}
})
.then(modifier)
)
}
}
}

View File

@ -6,9 +6,9 @@ import android.webkit.URLUtil
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@ -22,6 +22,7 @@ import androidx.compose.material.icons.automirrored.filled.FormatListBulleted
import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
@ -114,83 +115,81 @@ fun useSubsManagePage(): ScaffoldExt {
.padding(16.dp),
shape = RoundedCornerShape(16.dp),
) {
Column {
val subsRawVal = subsIdToRaw[menuSubItemVal.id]
if (subsRawVal != null) {
Text(text = "应用规则", modifier = Modifier
.clickable {
menuSubItem = null
navController.navigate(SubsPageDestination(subsRawVal.id))
val subsRawVal = subsIdToRaw[menuSubItemVal.id]
if (subsRawVal != null) {
Text(text = "应用规则", modifier = Modifier
.clickable {
menuSubItem = null
navController.navigate(SubsPageDestination(subsRawVal.id))
}
.fillMaxWidth()
.padding(16.dp))
HorizontalDivider()
Text(text = "查看类别", modifier = Modifier
.clickable {
menuSubItem = null
navController.navigate(CategoryPageDestination(subsRawVal.id))
}
.fillMaxWidth()
.padding(16.dp))
HorizontalDivider()
Text(text = "全局规则", modifier = Modifier
.clickable {
menuSubItem = null
navController.navigate(GlobalRulePageDestination(subsRawVal.id))
}
.fillMaxWidth()
.padding(16.dp))
HorizontalDivider()
}
if (menuSubItemVal.id < 0 && subsRawVal != null) {
Text(text = "分享文件", modifier = Modifier
.clickable {
menuSubItem = null
vm.viewModelScope.launchTry {
val subsFile = subsFolder.resolve("${menuSubItemVal.id}.json")
context.shareFile(subsFile, "分享订阅文件")
}
.fillMaxWidth()
.padding(16.dp))
HorizontalDivider()
Text(text = "查看类别", modifier = Modifier
.clickable {
menuSubItem = null
navController.navigate(CategoryPageDestination(subsRawVal.id))
}
.fillMaxWidth()
.padding(16.dp))
HorizontalDivider()
Text(text = "全局规则", modifier = Modifier
.clickable {
menuSubItem = null
navController.navigate(GlobalRulePageDestination(subsRawVal.id))
}
.fillMaxWidth()
.padding(16.dp))
HorizontalDivider()
}
if (menuSubItemVal.id < 0 && subsRawVal != null) {
Text(text = "分享文件", modifier = Modifier
.clickable {
menuSubItem = null
vm.viewModelScope.launchTry {
val subsFile = subsFolder.resolve("${menuSubItemVal.id}.json")
context.shareFile(subsFile, "分享订阅文件")
}
}
.fillMaxWidth()
.padding(16.dp))
HorizontalDivider()
}
if (menuSubItemVal.updateUrl != null) {
Text(text = "复制链接", modifier = Modifier
.clickable {
menuSubItem = null
ClipboardUtils.copyText(menuSubItemVal.updateUrl)
toast("复制成功")
}
.fillMaxWidth()
.padding(16.dp))
HorizontalDivider()
}
if (subsRawVal?.supportUri != null) {
Text(text = "问题反馈", modifier = Modifier
.clickable {
menuSubItem = null
context.startActivity(
Intent(
Intent.ACTION_VIEW, Uri.parse(subsRawVal.supportUri)
)
}
.fillMaxWidth()
.padding(16.dp))
HorizontalDivider()
}
if (menuSubItemVal.updateUrl != null) {
Text(text = "复制链接", modifier = Modifier
.clickable {
menuSubItem = null
ClipboardUtils.copyText(menuSubItemVal.updateUrl)
toast("复制成功")
}
.fillMaxWidth()
.padding(16.dp))
HorizontalDivider()
}
if (subsRawVal?.supportUri != null) {
Text(text = "问题反馈", modifier = Modifier
.clickable {
menuSubItem = null
context.startActivity(
Intent(
Intent.ACTION_VIEW, Uri.parse(subsRawVal.supportUri)
)
)
}
.fillMaxWidth()
.padding(16.dp))
HorizontalDivider()
}
if (menuSubItemVal.id != -2L) {
Text(text = "删除订阅",
modifier = Modifier
.clickable {
deleteSubItem = menuSubItemVal
menuSubItem = null
}
.fillMaxWidth()
.padding(16.dp))
HorizontalDivider()
}
if (menuSubItemVal.id != -2L) {
Text(text = "删除订阅",
modifier = Modifier
.clickable {
deleteSubItem = menuSubItemVal
menuSubItem = null
}
.fillMaxWidth()
.padding(16.dp),
color = MaterialTheme.colorScheme.error)
}
.padding(16.dp),
color = MaterialTheme.colorScheme.error)
}
}
}
@ -291,35 +290,39 @@ fun useSubsManagePage(): ScaffoldExt {
if (isDragging) 1.dp else 0.dp,
label = "width",
)
val interactionSource = remember { MutableInteractionSource() }
Card(
onClick = { menuSubItem = subItem },
modifier = Modifier
.longPressDraggableHandle(onDragStopped = {
val changeItems = mutableListOf<SubsItem>()
orderSubItems.forEachIndexed { i, subsItem ->
if (subItems[i] != subsItem) {
changeItems.add(
subsItem.copy(
order = i
.longPressDraggableHandle(
interactionSource = interactionSource,
onDragStopped = {
val changeItems = mutableListOf<SubsItem>()
orderSubItems.forEachIndexed { i, subsItem ->
if (subItems[i] != subsItem) {
changeItems.add(
subsItem.copy(
order = i
)
)
)
}
}
}
if (orderSubItems.isNotEmpty()) {
vm.viewModelScope.launchTry {
DbSet.subsItemDao.update(*changeItems.toTypedArray())
if (orderSubItems.isNotEmpty()) {
vm.viewModelScope.launchTry {
DbSet.subsItemDao.update(*changeItems.toTypedArray())
}
}
}
})
},
)
.animateItemPlacement()
.padding(vertical = 3.dp, horizontal = 8.dp)
.clickable {
menuSubItem = subItem
},
.padding(vertical = 3.dp, horizontal = 8.dp),
elevation = CardDefaults.cardElevation(draggedElevation = 10.dp),
shape = RoundedCornerShape(8.dp),
border = if (isDragging) BorderStroke(
width,
MaterialTheme.colorScheme.primary
) else null
) else null,
interactionSource = interactionSource,
) {
SubsItemCard(
subsItem = subItem,

View File

@ -128,9 +128,14 @@ dependencyResolutionManagement {
library("androidx.room.runtime", "androidx.room:room-runtime:$roomVersion")
library("androidx.room.compiler", "androidx.room:room-compiler:$roomVersion")
library("androidx.room.ktx", "androidx.room:room-ktx:$roomVersion")
library("androidx.room.paging", "androidx.room:room-paging:$roomVersion")
library("androidx.splashscreen", "androidx.core:core-splashscreen:1.0.1")
val pagingVersion = "3.2.1"
library("androidx.paging.runtime", "androidx.paging:paging-runtime:$pagingVersion")
library("androidx.paging.compose", "androidx.paging:paging-compose:$pagingVersion")
library(
"google.accompanist.drawablepainter",
"com.google.accompanist:accompanist-drawablepainter:0.34.0"
@ -203,7 +208,7 @@ dependencyResolutionManagement {
library("coil.gif", "io.coil-kt:coil-gif:$coilVersion")
// https://github.com/Calvin-LL/Reorderable
library("others.reorderable", "sh.calvin.reorderable:reorderable:1.3.1")
library("others.reorderable", "sh.calvin.reorderable:reorderable:1.3.2")
// https://www.objecthunter.net/exp4j/
library("exp4j", "net.objecthunter:exp4j:0.4.8")