ui: update text + email two-line row
- Replace "转发到 iOS Bark" with "多通道转发" across HomeScreen/FilterScreen - Email config items now use EmailServerRow (two-line layout matching Bark/ntfy): name + test/edit/delete on row 1, host:port + recipient on row 2 with up/down buttons - version 1.8.6 -> 1.8.7 (code 23 -> 24) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,8 +13,8 @@ android {
|
|||||||
applicationId = "com.a2i.forwarder"
|
applicationId = "com.a2i.forwarder"
|
||||||
minSdk = 34
|
minSdk = 34
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 23
|
versionCode = 24
|
||||||
versionName = "1.8.6"
|
versionName = "1.8.7"
|
||||||
}
|
}
|
||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ fun FilterScreen() {
|
|||||||
modifier = Modifier.fillMaxSize().padding(horizontal = 16.dp),
|
modifier = Modifier.fillMaxSize().padding(horizontal = 16.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
) {
|
) {
|
||||||
item { ScreenTitle("过滤与优化", "减少噪音,保留真正需要同步到 iOS 的通知") }
|
item { ScreenTitle("过滤与优化", "减少噪音,保留真正需要转发的通知") }
|
||||||
|
|
||||||
item {
|
item {
|
||||||
SectionCard("功能开关", subtitle = "规则按通知处理流水线顺序执行") {
|
SectionCard("功能开关", subtitle = "规则按通知处理流水线顺序执行") {
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ fun HomeScreen(onOpenLog: () -> Unit, onOpenSettings: () -> Unit) {
|
|||||||
Column(Modifier.weight(1f)) {
|
Column(Modifier.weight(1f)) {
|
||||||
Text(if (granted) "通知监听已开启" else "需要授权通知监听", style = MaterialTheme.typography.titleMedium)
|
Text(if (granted) "通知监听已开启" else "需要授权通知监听", style = MaterialTheme.typography.titleMedium)
|
||||||
Text(
|
Text(
|
||||||
if (granted) "系统通知将按规则处理并推送到 Bark" else "授权后才能读取 Android 通知并转发",
|
if (granted) "系统通知将按规则处理并多通道转发" else "授权后才能读取 Android 通知并转发",
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
@@ -202,7 +202,7 @@ fun HomeScreen(onOpenLog: () -> Unit, onOpenSettings: () -> Unit) {
|
|||||||
SectionCard("运行状态", subtitle = "转发能力、目标服务器和日志入口") {
|
SectionCard("运行状态", subtitle = "转发能力、目标服务器和日志入口") {
|
||||||
SwitchRow(
|
SwitchRow(
|
||||||
title = "通知转发",
|
title = "通知转发",
|
||||||
subtitle = if (forwarding) "新通知会继续转发到 Bark" else "当前已暂停所有转发",
|
subtitle = if (forwarding) "新通知会继续多通道转发" else "当前已暂停所有转发",
|
||||||
checked = forwarding,
|
checked = forwarding,
|
||||||
icon = Icons.Filled.PowerSettingsNew,
|
icon = Icons.Filled.PowerSettingsNew,
|
||||||
tint = if (forwarding) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant,
|
tint = if (forwarding) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
@@ -286,7 +286,7 @@ private fun HomeHero(
|
|||||||
Spacer(Modifier.width(12.dp))
|
Spacer(Modifier.width(12.dp))
|
||||||
Column(Modifier.weight(1f)) {
|
Column(Modifier.weight(1f)) {
|
||||||
Text("a2i", style = MaterialTheme.typography.displaySmall, color = Color.White)
|
Text("a2i", style = MaterialTheme.typography.displaySmall, color = Color.White)
|
||||||
Text("安卓通知转发到 iOS Bark", style = MaterialTheme.typography.bodyMedium, color = Color.White.copy(alpha = 0.82f))
|
Text("安卓通知多通道转发", style = MaterialTheme.typography.bodyMedium, color = Color.White.copy(alpha = 0.82f))
|
||||||
}
|
}
|
||||||
StatusBadge(
|
StatusBadge(
|
||||||
text = if (granted && forwarding) "运行中" else "待处理",
|
text = if (granted && forwarding) "运行中" else "待处理",
|
||||||
@@ -295,7 +295,7 @@ private fun HomeHero(
|
|||||||
}
|
}
|
||||||
Spacer(Modifier.size(14.dp))
|
Spacer(Modifier.size(14.dp))
|
||||||
Text(
|
Text(
|
||||||
if (granted && forwarding) "监听、过滤、提取验证码和推送链路已就绪。" else "完成授权并开启转发后,通知会按规则推送到 iOS。",
|
if (granted && forwarding) "监听、过滤、提取验证码和推送链路已就绪。" else "完成授权并开启转发后,通知会按规则转发到各通道。",
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
color = Color.White.copy(alpha = 0.86f),
|
color = Color.White.copy(alpha = 0.86f),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -240,15 +240,15 @@ fun SettingsScreen(onOpenLog: () -> Unit) {
|
|||||||
Spacer(Modifier.size(8.dp))
|
Spacer(Modifier.size(8.dp))
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
emailServers.forEachIndexed { i, s ->
|
emailServers.forEachIndexed { i, s ->
|
||||||
ConfigRow(
|
EmailServerRow(
|
||||||
title = s.name.ifBlank { s.to },
|
server = s,
|
||||||
subtitle = "${s.host}:${s.port} · ${s.user} → ${s.to}",
|
isFirst = i == 0,
|
||||||
enabled = s.enabled, isFirst = i == 0, isLast = i == emailServers.lastIndex,
|
isLast = i == emailServers.lastIndex,
|
||||||
onToggle = { v -> app.appScope.launch { app.settings.setEmailEnabled(s.id, v) } },
|
onToggle = { v -> app.appScope.launch { app.settings.setEmailEnabled(s.id, v) } },
|
||||||
onEdit = { editingEmail = s }, onDelete = { app.appScope.launch { app.settings.deleteEmail(s.id) } },
|
onEdit = { editingEmail = s },
|
||||||
|
onDelete = { app.appScope.launch { app.settings.deleteEmail(s.id) } },
|
||||||
onMoveUp = { app.appScope.launch { app.settings.moveEmail(s.id, true) } },
|
onMoveUp = { app.appScope.launch { app.settings.moveEmail(s.id, true) } },
|
||||||
onMoveDown = { app.appScope.launch { app.settings.moveEmail(s.id, false) } },
|
onMoveDown = { app.appScope.launch { app.settings.moveEmail(s.id, false) } },
|
||||||
onTest = { app.appScope.launch { EmailSender.send(s.host, s.port, s.user, s.password, s.from, s.to, "a2i测试", "电邮配置测试") } },
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -647,6 +647,52 @@ private fun ServerRow(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun EmailServerRow(
|
||||||
|
server: EmailServer, isFirst: Boolean, isLast: Boolean,
|
||||||
|
onToggle: (Boolean) -> Unit, onEdit: () -> Unit, onDelete: () -> Unit,
|
||||||
|
onMoveUp: () -> Unit, onMoveDown: () -> Unit,
|
||||||
|
) {
|
||||||
|
val app = A2iApp.instance
|
||||||
|
var testing by remember { mutableStateOf(false) }
|
||||||
|
var testMsg by remember { mutableStateOf<String?>(null) }
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(8.dp),
|
||||||
|
color = if (server.enabled) MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.40f) else MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.36f),
|
||||||
|
border = BorderStroke(1.dp, if (server.enabled) MaterialTheme.colorScheme.primary.copy(alpha = 0.30f) else MaterialTheme.colorScheme.outlineVariant),
|
||||||
|
) {
|
||||||
|
Column(Modifier.padding(start = 4.dp, end = 4.dp, top = 2.dp, bottom = 6.dp)) {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Checkbox(checked = server.enabled, onCheckedChange = onToggle)
|
||||||
|
Row(Modifier.weight(1f), verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Text(server.name.ifBlank { server.to.ifBlank { "未命名" } }, style = MaterialTheme.typography.bodyLarge, fontWeight = FontWeight.Medium, maxLines = 1, overflow = TextOverflow.Ellipsis, modifier = Modifier.weight(1f, fill = false))
|
||||||
|
if (!server.enabled) { Spacer(Modifier.width(8.dp)); StatusBadge("停用", MaterialTheme.colorScheme.onSurfaceVariant) }
|
||||||
|
}
|
||||||
|
IconButton(onClick = { if (!testing) app.appScope.launch { testing = true; testMsg = testEmail(server); testing = false } }, enabled = !testing) {
|
||||||
|
if (testing) CircularProgressIndicator(Modifier.size(18.dp), strokeWidth = 2.dp) else Icon(Icons.AutoMirrored.Filled.Send, "测试")
|
||||||
|
}
|
||||||
|
IconButton(onClick = onEdit) { Icon(Icons.Filled.Edit, "编辑") }
|
||||||
|
IconButton(onClick = onDelete) { Icon(Icons.Filled.Delete, "删除") }
|
||||||
|
}
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(top = 2.dp)) {
|
||||||
|
Column(Modifier.weight(1f).padding(start = 36.dp)) {
|
||||||
|
Text("${server.host}:${server.port}", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||||
|
Text(server.to.ifBlank { "收件人未填" }, style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||||
|
}
|
||||||
|
IconButton(onClick = onMoveUp, enabled = !isFirst) { Icon(Icons.Filled.KeyboardArrowUp, "上移") }
|
||||||
|
IconButton(onClick = onMoveDown, enabled = !isLast) { Icon(Icons.Filled.KeyboardArrowDown, "下移") }
|
||||||
|
}
|
||||||
|
testMsg?.let { Text(it, style = MaterialTheme.typography.bodySmall, color = if (it.startsWith("已发送")) MaterialTheme.colorScheme.tertiary else MaterialTheme.colorScheme.error, modifier = Modifier.padding(start = 48.dp, top = 2.dp)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun testEmail(server: EmailServer): String {
|
||||||
|
if (server.host.isBlank() || server.to.isBlank()) return "SMTP 主机或收件人未填"
|
||||||
|
val r = EmailSender.send(server.host, server.port, server.user, server.password, server.from, server.to, "a2i 测试", "电邮配置测试")
|
||||||
|
return if (r.isSuccess) "已发送,请查看邮箱" else "发送失败:${r.exceptionOrNull()?.message}"
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ServerDialog(initial: BarkServer? = null, onDismiss: () -> Unit, onConfirm: (String, String, String) -> Unit) {
|
private fun ServerDialog(initial: BarkServer? = null, onDismiss: () -> Unit, onConfirm: (String, String, String) -> Unit) {
|
||||||
var name by remember { mutableStateOf(initial?.name ?: "") }
|
var name by remember { mutableStateOf(initial?.name ?: "") }
|
||||||
|
|||||||
Reference in New Issue
Block a user