mirror of
https://github.com/gkd-kit/gkd.git
synced 2024-11-15 19:22:26 +08:00
This commit is contained in:
parent
76b3049b36
commit
8892dd3da8
|
@ -74,6 +74,7 @@ import li.songe.gkd.util.ResolvedGroup
|
|||
import li.songe.gkd.util.RuleSortOption
|
||||
import li.songe.gkd.util.appInfoCacheFlow
|
||||
import li.songe.gkd.util.launchTry
|
||||
import li.songe.gkd.util.storeFlow
|
||||
import li.songe.gkd.util.throttle
|
||||
|
||||
@Destination<RootGraph>(style = ProfileTransitions::class)
|
||||
|
@ -148,12 +149,12 @@ fun AppConfigPage(appId: String) {
|
|||
RadioButton(
|
||||
selected = ruleSortType == s,
|
||||
onClick = {
|
||||
vm.ruleSortTypeFlow.update { s }
|
||||
storeFlow.update { it.copy(appRuleSortType = s.value) }
|
||||
}
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
vm.ruleSortTypeFlow.update { s }
|
||||
storeFlow.update { it.copy(appRuleSortType = s.value) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -164,7 +165,8 @@ fun AppConfigPage(appId: String) {
|
|||
floatingActionButton = {
|
||||
FloatingActionButton(
|
||||
onClick = throttle {
|
||||
navController.toDestinationsNavigator().navigate(AppItemPageDestination(LOCAL_SUBS_ID, appId))
|
||||
navController.toDestinationsNavigator()
|
||||
.navigate(AppItemPageDestination(LOCAL_SUBS_ID, appId))
|
||||
},
|
||||
content = {
|
||||
Icon(
|
||||
|
@ -295,7 +297,6 @@ private fun AppGroupCard(
|
|||
onClick: () -> Unit,
|
||||
onCheckedChange: ((Boolean) -> Unit)?,
|
||||
) {
|
||||
val context = LocalContext.current as MainActivity
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable(onClick = onClick)
|
||||
|
@ -349,6 +350,14 @@ private fun AppGroupCard(
|
|||
if (checked != null) {
|
||||
Switch(checked = checked, onCheckedChange = onCheckedChange)
|
||||
} else {
|
||||
InnerDisableSwitch()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun InnerDisableSwitch() {
|
||||
val context = LocalContext.current as MainActivity
|
||||
Switch(
|
||||
checked = false,
|
||||
enabled = false,
|
||||
|
@ -364,5 +373,3 @@ private fun AppGroupCard(
|
|||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@ import androidx.lifecycle.SavedStateHandle
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.ramcosta.composedestinations.generated.destinations.AppConfigPageDestination
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
|
@ -16,7 +15,10 @@ import li.songe.gkd.util.ResolvedAppGroup
|
|||
import li.songe.gkd.util.ResolvedGlobalGroup
|
||||
import li.songe.gkd.util.RuleSortOption
|
||||
import li.songe.gkd.util.collator
|
||||
import li.songe.gkd.util.findOption
|
||||
import li.songe.gkd.util.getGroupRawEnable
|
||||
import li.songe.gkd.util.map
|
||||
import li.songe.gkd.util.storeFlow
|
||||
import li.songe.gkd.util.subsIdToRawFlow
|
||||
import li.songe.gkd.util.subsItemsFlow
|
||||
|
||||
|
@ -33,7 +35,8 @@ class AppConfigVm (stateHandle: SavedStateHandle) : ViewModel() {
|
|||
SubsConfig.AppGroupType
|
||||
)
|
||||
|
||||
val ruleSortTypeFlow = MutableStateFlow<RuleSortOption>(RuleSortOption.Default)
|
||||
val ruleSortTypeFlow =
|
||||
storeFlow.map(viewModelScope) { RuleSortOption.allSubObject.findOption(it.appRuleSortType) }
|
||||
|
||||
private val subsFlow = combine(subsIdToRawFlow, subsItemsFlow) { subsIdToRaw, subsItems ->
|
||||
subsItems.mapNotNull { if (it.enable && subsIdToRaw[it.id] != null) it to subsIdToRaw[it.id]!! else null }
|
||||
|
|
|
@ -259,7 +259,7 @@ fun AuthA11yPage() {
|
|||
}
|
||||
}
|
||||
|
||||
private val commandText by lazy { "adb pm grant ${META.appId} android.permission.WRITE_SECURE_SETTINGS" }
|
||||
private val commandText by lazy { "adb shell pm grant ${META.appId} android.permission.WRITE_SECURE_SETTINGS" }
|
||||
|
||||
private suspend fun MainActivity.grantPermissionByShizuku() {
|
||||
if (shizukuOkState.stateFlow.value) {
|
||||
|
|
|
@ -61,6 +61,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
|||
import com.google.accompanist.drawablepainter.rememberDrawablePainter
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import kotlinx.coroutines.flow.update
|
||||
import li.songe.gkd.data.AppInfo
|
||||
import li.songe.gkd.data.ExcludeData
|
||||
import li.songe.gkd.data.RawSubscription
|
||||
|
@ -79,6 +80,8 @@ import li.songe.gkd.util.LocalNavController
|
|||
import li.songe.gkd.util.ProfileTransitions
|
||||
import li.songe.gkd.util.SortTypeOption
|
||||
import li.songe.gkd.util.launchTry
|
||||
import li.songe.gkd.util.mapHashCode
|
||||
import li.songe.gkd.util.storeFlow
|
||||
import li.songe.gkd.util.toast
|
||||
|
||||
@Destination<RootGraph>(style = ProfileTransitions::class)
|
||||
|
@ -109,9 +112,16 @@ fun GlobalRuleExcludePage(subsItemId: Long, groupKey: Int) {
|
|||
})
|
||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||
val listState = rememberLazyListState()
|
||||
LaunchedEffect(key1 = showAppInfos, block = {
|
||||
listState.animateScrollToItem(0)
|
||||
})
|
||||
var isFirstVisit by remember { mutableStateOf(true) }
|
||||
LaunchedEffect(
|
||||
key1 = showAppInfos.mapHashCode { it.id },
|
||||
) {
|
||||
if (isFirstVisit) {
|
||||
isFirstVisit = false
|
||||
} else {
|
||||
listState.scrollToItem(0)
|
||||
}
|
||||
}
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
Scaffold(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = {
|
||||
TopAppBar(scrollBehavior = scrollBehavior, navigationIcon = {
|
||||
|
@ -190,12 +200,12 @@ fun GlobalRuleExcludePage(subsItemId: Long, groupKey: Int) {
|
|||
trailingIcon = {
|
||||
RadioButton(selected = sortType == sortOption,
|
||||
onClick = {
|
||||
vm.sortTypeFlow.value = sortOption
|
||||
storeFlow.update { it.copy(subsExcludeSortType = sortOption.value) }
|
||||
}
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
vm.sortTypeFlow.value = sortOption
|
||||
storeFlow.update { it.copy(subsExcludeSortType = sortOption.value) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -213,11 +223,11 @@ fun GlobalRuleExcludePage(subsItemId: Long, groupKey: Int) {
|
|||
Checkbox(
|
||||
checked = showSystemApp,
|
||||
onCheckedChange = {
|
||||
vm.showSystemAppFlow.value = !vm.showSystemAppFlow.value
|
||||
storeFlow.update { it.copy(subsExcludeShowSystemApp = !it.subsExcludeShowSystemApp) }
|
||||
})
|
||||
},
|
||||
onClick = {
|
||||
vm.showSystemAppFlow.value = !vm.showSystemAppFlow.value
|
||||
storeFlow.update { it.copy(subsExcludeShowSystemApp = !it.subsExcludeShowSystemApp) }
|
||||
},
|
||||
)
|
||||
DropdownMenuItem(
|
||||
|
@ -228,12 +238,11 @@ fun GlobalRuleExcludePage(subsItemId: Long, groupKey: Int) {
|
|||
Checkbox(
|
||||
checked = showHiddenApp,
|
||||
onCheckedChange = {
|
||||
vm.showHiddenAppFlow.value =
|
||||
!vm.showHiddenAppFlow.value
|
||||
storeFlow.update { it.copy(subsExcludeShowHiddenApp = !it.subsExcludeShowHiddenApp) }
|
||||
})
|
||||
},
|
||||
onClick = {
|
||||
vm.showHiddenAppFlow.value = !vm.showHiddenAppFlow.value
|
||||
storeFlow.update { it.copy(subsExcludeShowHiddenApp = !it.subsExcludeShowHiddenApp) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -334,11 +343,7 @@ fun GlobalRuleExcludePage(subsItemId: Long, groupKey: Int) {
|
|||
},
|
||||
)
|
||||
} else {
|
||||
Switch(
|
||||
enabled = false,
|
||||
checked = false,
|
||||
onCheckedChange = {},
|
||||
)
|
||||
InnerDisableSwitch()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,8 +13,10 @@ import kotlinx.coroutines.flow.stateIn
|
|||
import li.songe.gkd.data.ExcludeData
|
||||
import li.songe.gkd.db.DbSet
|
||||
import li.songe.gkd.util.SortTypeOption
|
||||
import li.songe.gkd.util.findOption
|
||||
import li.songe.gkd.util.map
|
||||
import li.songe.gkd.util.orderedAppInfosFlow
|
||||
import li.songe.gkd.util.storeFlow
|
||||
import li.songe.gkd.util.subsIdToRawFlow
|
||||
|
||||
class GlobalRuleExcludeVm(stateHandle: SavedStateHandle) : ViewModel() {
|
||||
|
@ -40,9 +42,11 @@ class GlobalRuleExcludeVm (stateHandle: SavedStateHandle) : ViewModel() {
|
|||
DbSet.clickLogDao.queryLatestUniqueAppIds(args.subsItemId, args.groupKey).map { appIds ->
|
||||
appIds.mapIndexed { index, appId -> appId to index }.toMap()
|
||||
}
|
||||
val sortTypeFlow = MutableStateFlow<SortTypeOption>(SortTypeOption.SortByName)
|
||||
val showSystemAppFlow = MutableStateFlow(true)
|
||||
val showHiddenAppFlow = MutableStateFlow(false)
|
||||
val sortTypeFlow = storeFlow.map(viewModelScope) {
|
||||
SortTypeOption.allSubObject.findOption(it.subsExcludeSortType)
|
||||
}
|
||||
val showSystemAppFlow = storeFlow.map(viewModelScope) { it.subsExcludeShowSystemApp }
|
||||
val showHiddenAppFlow = storeFlow.map(viewModelScope) { it.subsExcludeShowHiddenApp }
|
||||
val showAppInfosFlow =
|
||||
combine(orderedAppInfosFlow.combine(showHiddenAppFlow) { appInfos, showHiddenApp ->
|
||||
if (showHiddenApp) {
|
||||
|
|
|
@ -51,6 +51,7 @@ import com.ramcosta.composedestinations.annotation.Destination
|
|||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.generated.destinations.AppItemPageDestination
|
||||
import com.ramcosta.composedestinations.utils.toDestinationsNavigator
|
||||
import kotlinx.coroutines.flow.update
|
||||
import li.songe.gkd.MainActivity
|
||||
import li.songe.gkd.data.RawSubscription
|
||||
import li.songe.gkd.data.SubsConfig
|
||||
|
@ -70,6 +71,8 @@ import li.songe.gkd.util.appInfoCacheFlow
|
|||
import li.songe.gkd.util.json
|
||||
import li.songe.gkd.util.launchAsFn
|
||||
import li.songe.gkd.util.launchTry
|
||||
import li.songe.gkd.util.mapHashCode
|
||||
import li.songe.gkd.util.storeFlow
|
||||
import li.songe.gkd.util.throttle
|
||||
import li.songe.gkd.util.toast
|
||||
import li.songe.gkd.util.updateSubscription
|
||||
|
@ -117,16 +120,14 @@ fun SubsPage(
|
|||
val showUninstallApp by vm.showUninstallAppFlow.collectAsState()
|
||||
val sortType by vm.sortTypeFlow.collectAsState()
|
||||
val listState = rememberLazyListState()
|
||||
var isFirstVisit by remember { mutableStateOf(false) }
|
||||
var isFirstVisit by remember { mutableStateOf(true) }
|
||||
LaunchedEffect(
|
||||
appAndConfigs.size,
|
||||
sortType.value,
|
||||
appAndConfigs.fold(0) { acc, t -> 31 * acc + t.t0.id.hashCode() }
|
||||
key1 = appAndConfigs.mapHashCode { it.t0.id }
|
||||
) {
|
||||
if (isFirstVisit) {
|
||||
listState.scrollToItem(0)
|
||||
isFirstVisit = false
|
||||
} else {
|
||||
isFirstVisit = true
|
||||
listState.scrollToItem(0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,11 +198,11 @@ fun SubsPage(
|
|||
RadioButton(
|
||||
selected = sortType == sortOption,
|
||||
onClick = {
|
||||
vm.sortTypeFlow.value = sortOption
|
||||
storeFlow.update { s -> s.copy(subsAppSortType = sortOption.value) }
|
||||
})
|
||||
},
|
||||
onClick = {
|
||||
vm.sortTypeFlow.value = sortOption
|
||||
storeFlow.update { s -> s.copy(subsAppSortType = sortOption.value) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -217,11 +218,11 @@ fun SubsPage(
|
|||
},
|
||||
trailingIcon = {
|
||||
Checkbox(checked = showUninstallApp, onCheckedChange = {
|
||||
vm.showUninstallAppFlow.value = it
|
||||
storeFlow.update { s -> s.copy(subsAppShowUninstallApp = it) }
|
||||
})
|
||||
},
|
||||
onClick = {
|
||||
vm.showUninstallAppFlow.value = !showUninstallApp
|
||||
storeFlow.update { s -> s.copy(subsAppShowUninstallApp = !showUninstallApp) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -252,7 +253,8 @@ fun SubsPage(
|
|||
subsConfig = subsConfig,
|
||||
enableSize = enableSize,
|
||||
onClick = throttle {
|
||||
navController.toDestinationsNavigator().navigate(AppItemPageDestination(subsItemId, appRaw.id))
|
||||
navController.toDestinationsNavigator()
|
||||
.navigate(AppItemPageDestination(subsItemId, appRaw.id))
|
||||
},
|
||||
onValueChange = throttle(fn = vm.viewModelScope.launchAsFn { enable ->
|
||||
val newItem = subsConfig?.copy(
|
||||
|
@ -284,11 +286,13 @@ fun SubsPage(
|
|||
item {
|
||||
Spacer(modifier = Modifier.height(EmptyHeight))
|
||||
if (appAndConfigs.isEmpty()) {
|
||||
EmptyText(text = if (searchStr.isNotEmpty()) {
|
||||
EmptyText(
|
||||
text = if (searchStr.isNotEmpty()) {
|
||||
if (showUninstallApp) "暂无搜索结果" else "暂无搜索结果,请尝试修改筛选条件"
|
||||
} else {
|
||||
"暂无规则"
|
||||
})
|
||||
}
|
||||
)
|
||||
} else if (editable) {
|
||||
Spacer(modifier = Modifier.height(EmptyHeight))
|
||||
}
|
||||
|
|
|
@ -17,8 +17,10 @@ import li.songe.gkd.db.DbSet
|
|||
import li.songe.gkd.util.SortTypeOption
|
||||
import li.songe.gkd.util.appInfoCacheFlow
|
||||
import li.songe.gkd.util.collator
|
||||
import li.songe.gkd.util.findOption
|
||||
import li.songe.gkd.util.getGroupRawEnable
|
||||
import li.songe.gkd.util.map
|
||||
import li.songe.gkd.util.storeFlow
|
||||
import li.songe.gkd.util.subsIdToRawFlow
|
||||
import li.songe.gkd.util.subsItemsFlow
|
||||
|
||||
|
@ -43,9 +45,9 @@ class SubsVm (stateHandle: SavedStateHandle) : ViewModel() {
|
|||
DbSet.clickLogDao.queryLatestUniqueAppIds(args.subsItemId).map { appIds ->
|
||||
appIds.mapIndexed { index, appId -> appId to index }.toMap()
|
||||
}
|
||||
val sortTypeFlow = MutableStateFlow<SortTypeOption>(SortTypeOption.SortByName)
|
||||
val sortTypeFlow = storeFlow.map(viewModelScope) { SortTypeOption.allSubObject.findOption(it.subsAppSortType) }
|
||||
|
||||
val showUninstallAppFlow = MutableStateFlow(false)
|
||||
val showUninstallAppFlow = storeFlow.map(viewModelScope) { it.subsAppShowUninstallApp }
|
||||
private val sortAppsFlow =
|
||||
combine(combine((subsRawFlow.combine(appInfoCacheFlow) { subs, appInfoCache ->
|
||||
(subs?.apps ?: emptyList()).sortedWith { a, b ->
|
||||
|
|
|
@ -69,6 +69,7 @@ import li.songe.gkd.ui.style.appItemPadding
|
|||
import li.songe.gkd.ui.style.menuPadding
|
||||
import li.songe.gkd.util.LocalNavController
|
||||
import li.songe.gkd.util.SortTypeOption
|
||||
import li.songe.gkd.util.mapHashCode
|
||||
import li.songe.gkd.util.ruleSummaryFlow
|
||||
import li.songe.gkd.util.storeFlow
|
||||
import li.songe.gkd.util.throttle
|
||||
|
@ -112,14 +113,16 @@ fun useAppListPage(): ScaffoldExt {
|
|||
})
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
var isFirstVisit by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(key1 = orderedAppInfos, block = {
|
||||
var isFirstVisit by remember { mutableStateOf(true) }
|
||||
LaunchedEffect(
|
||||
key1 = orderedAppInfos.mapHashCode { it.id }
|
||||
) {
|
||||
if (isFirstVisit) {
|
||||
listState.scrollToItem(0)
|
||||
isFirstVisit = false
|
||||
} else {
|
||||
isFirstVisit = true
|
||||
listState.scrollToItem(0)
|
||||
}
|
||||
}
|
||||
})
|
||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||
return ScaffoldExt(
|
||||
navItem = appListNav,
|
||||
|
@ -265,7 +268,9 @@ fun useAppListPage(): ScaffoldExt {
|
|||
Row(
|
||||
modifier = Modifier
|
||||
.clickable(onClick = throttle {
|
||||
navController.toDestinationsNavigator().navigate(AppConfigPageDestination(appInfo.id))
|
||||
navController
|
||||
.toDestinationsNavigator()
|
||||
.navigate(AppConfigPageDestination(appInfo.id))
|
||||
})
|
||||
.height(IntrinsicSize.Min)
|
||||
.appItemPadding(),
|
||||
|
|
5
app/src/main/kotlin/li/songe/gkd/util/Others.kt
Normal file
5
app/src/main/kotlin/li/songe/gkd/util/Others.kt
Normal file
|
@ -0,0 +1,5 @@
|
|||
package li.songe.gkd.util
|
||||
|
||||
inline fun <T, R> Iterable<T>.mapHashCode(transform: (T) -> R): Int {
|
||||
return fold(0) { acc, t -> 31 * acc + transform(t).hashCode() }
|
||||
}
|
|
@ -74,15 +74,21 @@ data class Store(
|
|||
val enableDarkTheme: Boolean? = null,
|
||||
val enableDynamicColor: Boolean = true,
|
||||
val enableAbFloatWindow: Boolean = true,
|
||||
val sortType: Int = SortTypeOption.SortByName.value,
|
||||
val showSystemApp: Boolean = true,
|
||||
val showHiddenApp: Boolean = false,
|
||||
val showSaveSnapshotToast: Boolean = true,
|
||||
val useSystemToast: Boolean = false,
|
||||
val useCustomNotifText: Boolean = false,
|
||||
val customNotifText: String = "\${i}全局/\${k}应用/\${u}规则组/\${n}触发",
|
||||
val enableActivityLog: Boolean = false,
|
||||
val updateChannel: Int = if (META.versionName.contains("beta")) UpdateChannelOption.Beta.value else UpdateChannelOption.Stable.value,
|
||||
val sortType: Int = SortTypeOption.SortByName.value,
|
||||
val showSystemApp: Boolean = true,
|
||||
val showHiddenApp: Boolean = false,
|
||||
val appRuleSortType: Int = RuleSortOption.Default.value,
|
||||
val subsAppSortType: Int = SortTypeOption.SortByName.value,
|
||||
val subsAppShowUninstallApp: Boolean = false,
|
||||
val subsExcludeSortType: Int = SortTypeOption.SortByName.value,
|
||||
val subsExcludeShowSystemApp: Boolean = true,
|
||||
val subsExcludeShowHiddenApp: Boolean = false,
|
||||
)
|
||||
|
||||
val storeFlow by lazy {
|
||||
|
|
Loading…
Reference in New Issue
Block a user