From 8588da1f6b6ca68dd1e1d499700e9ef0acf38a38 Mon Sep 17 00:00:00 2001 From: Song <168455@qq.com> Date: Fri, 10 Jul 2026 09:31:20 +0800 Subject: [PATCH] ui: update text + email two-line row MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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) --- app/build.gradle.kts | 4 +- .../a2i/forwarder/ui/screens/FilterScreen.kt | 2 +- .../a2i/forwarder/ui/screens/HomeScreen.kt | 8 +-- .../forwarder/ui/screens/SettingsScreen.kt | 58 +++++++++++++++++-- 4 files changed, 59 insertions(+), 13 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index fae179b..4e7301e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -13,8 +13,8 @@ android { applicationId = "com.a2i.forwarder" minSdk = 34 targetSdk = 36 - versionCode = 23 - versionName = "1.8.6" + versionCode = 24 + versionName = "1.8.7" } signingConfigs { diff --git a/app/src/main/java/com/a2i/forwarder/ui/screens/FilterScreen.kt b/app/src/main/java/com/a2i/forwarder/ui/screens/FilterScreen.kt index 693b26f..931d8e1 100644 --- a/app/src/main/java/com/a2i/forwarder/ui/screens/FilterScreen.kt +++ b/app/src/main/java/com/a2i/forwarder/ui/screens/FilterScreen.kt @@ -61,7 +61,7 @@ fun FilterScreen() { modifier = Modifier.fillMaxSize().padding(horizontal = 16.dp), verticalArrangement = Arrangement.spacedBy(12.dp), ) { - item { ScreenTitle("过滤与优化", "减少噪音,保留真正需要同步到 iOS 的通知") } + item { ScreenTitle("过滤与优化", "减少噪音,保留真正需要转发的通知") } item { SectionCard("功能开关", subtitle = "规则按通知处理流水线顺序执行") { diff --git a/app/src/main/java/com/a2i/forwarder/ui/screens/HomeScreen.kt b/app/src/main/java/com/a2i/forwarder/ui/screens/HomeScreen.kt index 21614f5..4177d23 100644 --- a/app/src/main/java/com/a2i/forwarder/ui/screens/HomeScreen.kt +++ b/app/src/main/java/com/a2i/forwarder/ui/screens/HomeScreen.kt @@ -178,7 +178,7 @@ fun HomeScreen(onOpenLog: () -> Unit, onOpenSettings: () -> Unit) { Column(Modifier.weight(1f)) { Text(if (granted) "通知监听已开启" else "需要授权通知监听", style = MaterialTheme.typography.titleMedium) Text( - if (granted) "系统通知将按规则处理并推送到 Bark" else "授权后才能读取 Android 通知并转发", + if (granted) "系统通知将按规则处理并多通道转发" else "授权后才能读取 Android 通知并转发", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant, ) @@ -202,7 +202,7 @@ fun HomeScreen(onOpenLog: () -> Unit, onOpenSettings: () -> Unit) { SectionCard("运行状态", subtitle = "转发能力、目标服务器和日志入口") { SwitchRow( title = "通知转发", - subtitle = if (forwarding) "新通知会继续转发到 Bark" else "当前已暂停所有转发", + subtitle = if (forwarding) "新通知会继续多通道转发" else "当前已暂停所有转发", checked = forwarding, icon = Icons.Filled.PowerSettingsNew, tint = if (forwarding) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant, @@ -286,7 +286,7 @@ private fun HomeHero( Spacer(Modifier.width(12.dp)) Column(Modifier.weight(1f)) { 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( text = if (granted && forwarding) "运行中" else "待处理", @@ -295,7 +295,7 @@ private fun HomeHero( } Spacer(Modifier.size(14.dp)) Text( - if (granted && forwarding) "监听、过滤、提取验证码和推送链路已就绪。" else "完成授权并开启转发后,通知会按规则推送到 iOS。", + if (granted && forwarding) "监听、过滤、提取验证码和推送链路已就绪。" else "完成授权并开启转发后,通知会按规则转发到各通道。", style = MaterialTheme.typography.bodyMedium, color = Color.White.copy(alpha = 0.86f), ) diff --git a/app/src/main/java/com/a2i/forwarder/ui/screens/SettingsScreen.kt b/app/src/main/java/com/a2i/forwarder/ui/screens/SettingsScreen.kt index b8f07ec..82f8729 100644 --- a/app/src/main/java/com/a2i/forwarder/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/com/a2i/forwarder/ui/screens/SettingsScreen.kt @@ -240,15 +240,15 @@ fun SettingsScreen(onOpenLog: () -> Unit) { Spacer(Modifier.size(8.dp)) Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { emailServers.forEachIndexed { i, s -> - ConfigRow( - title = s.name.ifBlank { s.to }, - subtitle = "${s.host}:${s.port} · ${s.user} → ${s.to}", - enabled = s.enabled, isFirst = i == 0, isLast = i == emailServers.lastIndex, + EmailServerRow( + server = s, + isFirst = i == 0, + isLast = i == emailServers.lastIndex, 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) } }, 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(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 private fun ServerDialog(initial: BarkServer? = null, onDismiss: () -> Unit, onConfirm: (String, String, String) -> Unit) { var name by remember { mutableStateOf(initial?.name ?: "") }