mirror of
https://github.com/gkd-kit/gkd.git
synced 2024-11-16 11:42:22 +08:00
Compare commits
4 Commits
9e79f1eab6
...
e5a2f44521
Author | SHA1 | Date | |
---|---|---|---|
|
e5a2f44521 | ||
|
0ad0e1d9e7 | ||
|
eaa7ee0ee1 | ||
|
6f2d47ea04 |
|
@ -1,6 +1,7 @@
|
|||
package li.songe.gkd.ui
|
||||
|
||||
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
|
||||
|
@ -19,6 +20,7 @@ import androidx.compose.material.icons.Icons
|
|||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.Sort
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
|
@ -30,6 +32,7 @@ import androidx.compose.material3.RadioButton
|
|||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
|
@ -177,6 +180,7 @@ fun AppConfigPage(appId: String) {
|
|||
}
|
||||
val checked = getChecked(excludeData, g.group, appId, appInfo)
|
||||
AppGroupCard(
|
||||
vm = vm,
|
||||
group = g.group,
|
||||
checked = checked,
|
||||
onClick = {
|
||||
|
@ -211,15 +215,20 @@ fun AppConfigPage(appId: String) {
|
|||
}
|
||||
}
|
||||
items(appGroups) { g ->
|
||||
AppGroupCard(g.group, g.enable, onClick = {
|
||||
navController.navigate(
|
||||
AppItemPageDestination(
|
||||
g.subsItem.id,
|
||||
appId,
|
||||
g.group.key,
|
||||
AppGroupCard(
|
||||
vm = vm,
|
||||
group = g.group,
|
||||
checked = g.enable,
|
||||
onClick = {
|
||||
navController.navigate(
|
||||
AppItemPageDestination(
|
||||
g.subsItem.id,
|
||||
appId,
|
||||
g.group.key,
|
||||
)
|
||||
)
|
||||
)
|
||||
}) {
|
||||
}
|
||||
) {
|
||||
vm.viewModelScope.launchTry {
|
||||
DbSet.subsConfigDao.insert(
|
||||
g.config?.copy(enable = it) ?: SubsConfig(
|
||||
|
@ -248,10 +257,28 @@ fun AppConfigPage(appId: String) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
val innerDisabledDlg by vm.innerDisabledDlgFlow.collectAsState()
|
||||
if (innerDisabledDlg) {
|
||||
AlertDialog(
|
||||
title = { Text(text = "内置禁用") },
|
||||
text = {
|
||||
Text(text = "此规则组已经在其 apps 字段中配置对当前应用的禁用, 因此无法手动开启规则组\n\n提示: 这种情况一般在此全局规则无法适配/跳过适配当前应用时出现")
|
||||
},
|
||||
onDismissRequest = { vm.innerDisabledDlgFlow.value = false },
|
||||
confirmButton = {
|
||||
TextButton(onClick = { vm.innerDisabledDlgFlow.value = false }) {
|
||||
Text(text = "我知道了")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AppGroupCard(
|
||||
vm: AppConfigVm,
|
||||
group: RawSubscription.RawGroupProps,
|
||||
checked: Boolean?,
|
||||
onClick: () -> Unit,
|
||||
|
@ -313,7 +340,13 @@ private fun AppGroupCard(
|
|||
Switch(
|
||||
checked = false,
|
||||
enabled = false,
|
||||
onCheckedChange = onCheckedChange
|
||||
onCheckedChange = null,
|
||||
modifier = Modifier.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null,
|
||||
) {
|
||||
vm.innerDisabledDlgFlow.value = true
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,5 +74,7 @@ class AppConfigVm @Inject constructor(stateHandle: SavedStateHandle) : ViewModel
|
|||
}
|
||||
}.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
val innerDisabledDlgFlow = MutableStateFlow(false)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ import li.songe.gkd.data.RawSubscription
|
|||
import li.songe.gkd.data.SubsConfig
|
||||
import li.songe.gkd.data.stringify
|
||||
import li.songe.gkd.db.DbSet
|
||||
import li.songe.gkd.ui.component.TowLineText
|
||||
import li.songe.gkd.ui.component.getDialogResult
|
||||
import li.songe.gkd.ui.destinations.GroupItemPageDestination
|
||||
import li.songe.gkd.ui.style.itemPadding
|
||||
|
@ -132,19 +133,10 @@ fun AppItemPage(
|
|||
)
|
||||
}
|
||||
}, title = {
|
||||
Column {
|
||||
Text(
|
||||
text = subsRaw?.name ?: subsItemId.toString(),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Text(
|
||||
text = appInfoCache[appId]?.name ?: appRaw.name ?: appId,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
}
|
||||
TowLineText(
|
||||
title = subsRaw?.name ?: subsItemId.toString(),
|
||||
subTitle = appInfoCache[appId]?.name ?: appRaw.name ?: appId
|
||||
)
|
||||
}, actions = {})
|
||||
}, floatingActionButton = {
|
||||
if (editable) {
|
||||
|
|
|
@ -40,7 +40,6 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
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
|
||||
import androidx.lifecycle.viewModelScope
|
||||
|
@ -50,6 +49,7 @@ import kotlinx.coroutines.Dispatchers
|
|||
import li.songe.gkd.data.CategoryConfig
|
||||
import li.songe.gkd.data.RawSubscription
|
||||
import li.songe.gkd.db.DbSet
|
||||
import li.songe.gkd.ui.component.TowLineText
|
||||
import li.songe.gkd.ui.component.getDialogResult
|
||||
import li.songe.gkd.ui.style.itemPadding
|
||||
import li.songe.gkd.util.EnableGroupOption
|
||||
|
@ -94,14 +94,10 @@ fun CategoryPage(subsItemId: Long) {
|
|||
)
|
||||
}
|
||||
}, title = {
|
||||
Column {
|
||||
Text(
|
||||
text = subsRaw?.name ?: subsItemId.toString(),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Text(text = "规则类别", style = MaterialTheme.typography.bodyMedium)
|
||||
}
|
||||
TowLineText(
|
||||
title = subsRaw?.name ?: subsItemId.toString(),
|
||||
subTitle = "规则类别"
|
||||
)
|
||||
}, actions = {})
|
||||
}, floatingActionButton = {
|
||||
if (editable) {
|
||||
|
|
|
@ -69,6 +69,7 @@ import li.songe.gkd.data.stringify
|
|||
import li.songe.gkd.db.DbSet
|
||||
import li.songe.gkd.service.launcherAppId
|
||||
import li.songe.gkd.ui.component.AppBarTextField
|
||||
import li.songe.gkd.ui.component.TowLineText
|
||||
import li.songe.gkd.ui.style.appItemPadding
|
||||
import li.songe.gkd.ui.style.menuPadding
|
||||
import li.songe.gkd.util.LocalNavController
|
||||
|
@ -129,19 +130,10 @@ fun GlobalRuleExcludePage(subsItemId: Long, groupKey: Int) {
|
|||
modifier = Modifier.focusRequester(focusRequester)
|
||||
)
|
||||
} else {
|
||||
Column {
|
||||
Text(
|
||||
text = rawSubs?.name ?: subsItemId.toString(),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Text(
|
||||
text = (group?.name ?: groupKey.toString()),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
TowLineText(
|
||||
title = rawSubs?.name ?: subsItemId.toString(),
|
||||
subTitle = (group?.name ?: groupKey.toString())
|
||||
)
|
||||
}
|
||||
}, actions = {
|
||||
if (showSearchBar) {
|
||||
|
|
|
@ -63,6 +63,7 @@ import kotlinx.coroutines.Dispatchers
|
|||
import li.songe.gkd.data.RawSubscription
|
||||
import li.songe.gkd.data.SubsConfig
|
||||
import li.songe.gkd.db.DbSet
|
||||
import li.songe.gkd.ui.component.TowLineText
|
||||
import li.songe.gkd.ui.component.getDialogResult
|
||||
import li.songe.gkd.ui.destinations.GlobalRuleExcludePageDestination
|
||||
import li.songe.gkd.ui.destinations.GroupItemPageDestination
|
||||
|
@ -116,14 +117,10 @@ fun GlobalRulePage(subsItemId: Long, focusGroupKey: Int? = null) {
|
|||
)
|
||||
}
|
||||
}, title = {
|
||||
Column {
|
||||
Text(
|
||||
text = rawSubs?.name ?: subsItemId.toString(),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Text(text = "全局规则", style = MaterialTheme.typography.bodyMedium)
|
||||
}
|
||||
TowLineText(
|
||||
title = rawSubs?.name ?: subsItemId.toString(),
|
||||
subTitle = "全局规则"
|
||||
)
|
||||
})
|
||||
},
|
||||
floatingActionButton = {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package li.songe.gkd.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
|
@ -46,7 +45,6 @@ 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
|
||||
import androidx.lifecycle.viewModelScope
|
||||
|
@ -58,6 +56,7 @@ import li.songe.gkd.data.SubsConfig
|
|||
import li.songe.gkd.db.DbSet
|
||||
import li.songe.gkd.ui.component.AppBarTextField
|
||||
import li.songe.gkd.ui.component.SubsAppCard
|
||||
import li.songe.gkd.ui.component.TowLineText
|
||||
import li.songe.gkd.ui.component.getDialogResult
|
||||
import li.songe.gkd.ui.destinations.AppItemPageDestination
|
||||
import li.songe.gkd.ui.style.menuPadding
|
||||
|
@ -150,14 +149,10 @@ fun SubsPage(
|
|||
modifier = Modifier.focusRequester(focusRequester)
|
||||
)
|
||||
} else {
|
||||
Column {
|
||||
Text(
|
||||
text = subsRaw?.name ?: subsItemId.toString(),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Text(text = "应用规则", style = MaterialTheme.typography.bodyMedium)
|
||||
}
|
||||
TowLineText(
|
||||
title = subsRaw?.name ?: subsItemId.toString(),
|
||||
subTitle = "应用规则",
|
||||
)
|
||||
}
|
||||
}, actions = {
|
||||
if (showSearchBar) {
|
||||
|
|
|
@ -7,14 +7,12 @@ import androidx.compose.foundation.layout.heightIn
|
|||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.material3.TextFieldDefaults.indicatorLine
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
|
@ -22,9 +20,7 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.graphics.takeOrElse
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
@ -42,19 +38,17 @@ fun AppBarTextField(
|
|||
keyboardActions: KeyboardActions = KeyboardActions.Default,
|
||||
) {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val textStyle = LocalTextStyle.current
|
||||
|
||||
// make sure there is no background color in the decoration box
|
||||
val colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = Color.Unspecified,
|
||||
unfocusedContainerColor = Color.Unspecified,
|
||||
disabledContainerColor = Color.Unspecified,
|
||||
focusedContainerColor = Color.Transparent,
|
||||
unfocusedContainerColor = Color.Transparent,
|
||||
disabledContainerColor = Color.Transparent,
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
)
|
||||
|
||||
// If color is not provided via the text style, use content color as a default
|
||||
val textColor = textStyle.color.takeOrElse {
|
||||
MaterialTheme.colorScheme.onSurface
|
||||
}
|
||||
val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
|
||||
val mergedTextStyle = LocalTextStyle.current.merge(MaterialTheme.typography.titleMedium)
|
||||
|
||||
// request focus when this composable is first initialized
|
||||
// val focusRequester = FocusRequester()
|
||||
|
@ -68,47 +62,43 @@ fun AppBarTextField(
|
|||
}
|
||||
textFieldValue = textFieldValue.copy(text = value) // make sure to keep the value updated
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalTextSelectionColors provides LocalTextSelectionColors.current
|
||||
) {
|
||||
BasicTextField(
|
||||
value = textFieldValue,
|
||||
onValueChange = {
|
||||
textFieldValue = it
|
||||
// remove newlines to avoid strange layout issues, and also because singleLine=true
|
||||
onValueChange(it.text.replace("\n", ""))
|
||||
},
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(32.dp)
|
||||
.indicatorLine(
|
||||
enabled = true,
|
||||
isError = false,
|
||||
interactionSource = interactionSource,
|
||||
colors = colors
|
||||
),
|
||||
BasicTextField(
|
||||
value = textFieldValue,
|
||||
onValueChange = {
|
||||
textFieldValue = it
|
||||
// remove newlines to avoid strange layout issues, and also because singleLine=true
|
||||
onValueChange(it.text.replace("\n", ""))
|
||||
},
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(32.dp)
|
||||
.indicatorLine(
|
||||
enabled = true,
|
||||
isError = false,
|
||||
interactionSource = interactionSource,
|
||||
colors = colors
|
||||
),
|
||||
// .focusRequester(focusRequester),
|
||||
textStyle = mergedTextStyle,
|
||||
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
|
||||
keyboardOptions = keyboardOptions,
|
||||
keyboardActions = keyboardActions,
|
||||
interactionSource = interactionSource,
|
||||
singleLine = true,
|
||||
decorationBox = { innerTextField ->
|
||||
// places text field with placeholder and appropriate bottom padding
|
||||
TextFieldDefaults.DecorationBox(
|
||||
value = value,
|
||||
innerTextField = innerTextField,
|
||||
enabled = true,
|
||||
singleLine = true,
|
||||
visualTransformation = VisualTransformation.None,
|
||||
interactionSource = interactionSource,
|
||||
isError = false,
|
||||
placeholder = { Text(text = hint) },
|
||||
colors = colors,
|
||||
contentPadding = PaddingValues(bottom = 4.dp),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
textStyle = mergedTextStyle,
|
||||
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
|
||||
keyboardOptions = keyboardOptions,
|
||||
keyboardActions = keyboardActions,
|
||||
interactionSource = interactionSource,
|
||||
singleLine = true,
|
||||
decorationBox = { innerTextField ->
|
||||
// places text field with placeholder and appropriate bottom padding
|
||||
TextFieldDefaults.DecorationBox(
|
||||
value = value,
|
||||
innerTextField = innerTextField,
|
||||
enabled = true,
|
||||
singleLine = true,
|
||||
visualTransformation = VisualTransformation.None,
|
||||
interactionSource = interactionSource,
|
||||
isError = false,
|
||||
placeholder = { Text(text = hint) },
|
||||
colors = colors,
|
||||
contentPadding = PaddingValues(bottom = 4.dp),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
28
app/src/main/kotlin/li/songe/gkd/ui/component/TowLineText.kt
Normal file
28
app/src/main/kotlin/li/songe/gkd/ui/component/TowLineText.kt
Normal file
|
@ -0,0 +1,28 @@
|
|||
package li.songe.gkd.ui.component
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
|
||||
@Composable
|
||||
fun TowLineText(
|
||||
title: String,
|
||||
subTitle: String
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
text = title,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
Text(
|
||||
text = subTitle,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.titleSmall
|
||||
)
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ import androidx.compose.ui.unit.dp
|
|||
|
||||
fun Modifier.itemPadding() = this then padding(16.dp, 12.dp)
|
||||
|
||||
fun Modifier.appItemPadding() = this then padding(10.dp, 8.dp)
|
||||
fun Modifier.appItemPadding() = this then padding(10.dp, 10.dp)
|
||||
|
||||
fun Modifier.menuPadding() =
|
||||
this then padding(MenuDefaults.DropdownMenuItemContentPadding).padding(vertical = 8.dp)
|
||||
|
|
|
@ -189,8 +189,8 @@ val ruleSummaryFlow by lazy {
|
|||
val subGlobalGroupToRules =
|
||||
mutableMapOf<RawSubscription.RawGlobalGroup, List<GlobalRule>>()
|
||||
rawSubs.globalGroups.filter { g ->
|
||||
g.valid && (subGlobalSubsConfigs.find { c -> c.groupKey == g.key }?.enable
|
||||
?: g.enable ?: true)
|
||||
(subGlobalSubsConfigs.find { c -> c.groupKey == g.key }?.enable
|
||||
?: g.enable ?: true) && g.valid
|
||||
}.forEach { groupRaw ->
|
||||
val config = subGlobalSubsConfigs.find { c -> c.groupKey == groupRaw.key }
|
||||
val g = ResolvedGlobalGroup(
|
||||
|
|
|
@ -6,7 +6,7 @@ targetSdk = "34"
|
|||
buildToolsVersion = "34.0.0"
|
||||
minSdk = "26"
|
||||
android = "8.4.2"
|
||||
compose = "1.6.7"
|
||||
compose = "1.6.8"
|
||||
rikka = "4.4.0"
|
||||
room = "2.6.1"
|
||||
paging = "3.3.0"
|
||||
|
@ -39,7 +39,7 @@ others_jankson = { module = "blue.endless:jankson", version = "1.2.3" }
|
|||
others_floating_bubble_view = { module = "io.github.torrydo:floating-bubble-view", version = "0.6.5" }
|
||||
androidx_appcompat = { module = "androidx.appcompat:appcompat", version = "1.7.0" }
|
||||
androidx_core_ktx = { module = "androidx.core:core-ktx", version = "1.13.1" }
|
||||
androidx_lifecycle_runtime_ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version = "2.8.1" }
|
||||
androidx_lifecycle_runtime_ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version = "2.8.2" }
|
||||
androidx_junit = { module = "androidx.test.ext:junit", version = "1.1.5" }
|
||||
androidx_espresso = { module = "androidx.test.espresso:espresso-core", version = "3.5.1" }
|
||||
androidx_room_runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
||||
|
|
Loading…
Reference in New Issue
Block a user