feat: unify email/SMS settings with Bark style (no total toggle)
- Remove emailForwardingEnabled / smsFallbackEnabled total toggles - Email/SMS SectionCards now use headerAction + CompactStopOnFirstSwitch (matching Bark and ntfy) - Sender logic: email/sms participation controlled solely by per-item enabled checkboxes (empty list = channel inactive) - version 1.8.4 -> 1.8.5 (code 21 -> 22) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -120,7 +120,6 @@ object EmailSender {
|
||||
/** 如果电邮转发已启用且收件人非空,异步发邮件(fire-and-forget,不阻塞主流程)。 */
|
||||
fun trySend(app: A2iApp, title: String, body: String, scope: CoroutineScope) {
|
||||
val s = app.settings
|
||||
if (!s.emailForwardingEnabled.value) return
|
||||
val configs = s.emailServers.value.filter { it.enabled && it.to.isNotBlank() && it.host.isNotBlank() }
|
||||
if (configs.isEmpty()) return
|
||||
scope.launch {
|
||||
|
||||
@@ -35,7 +35,6 @@ class SmsFallbackForwarder(private val context: Context) {
|
||||
/** 尝试用短信转发(遍历所有启用的目标号,按 stopOnFirst 决定发通一条止还是全发)。 */
|
||||
fun forward(d: Decision) {
|
||||
scope.launch {
|
||||
if (!settings.smsFallbackEnabled.value) return@launch
|
||||
if (settings.smsSuspended.value) {
|
||||
log(d, "failed", "短信已挂起(余额不足)")
|
||||
return@launch
|
||||
|
||||
@@ -226,68 +226,78 @@ fun SettingsScreen(onOpenLog: () -> Unit) {
|
||||
|
||||
// ===== 电邮 =====
|
||||
item {
|
||||
SectionCard("电邮转发", subtitle = "SMTPS 直发邮件") {
|
||||
SwitchRow(title = "启用电邮转发", subtitle = if (emailEnabled) "已开启" else "已关闭", checked = emailEnabled) { v -> app.appScope.launch { app.settings.setEmailForwarding(v) } }
|
||||
if (emailEnabled) {
|
||||
SwitchRow(title = "发通一条就停", subtitle = if (emailStopFirst) "容错:首个成功止" else "广播:每个都发", checked = emailStopFirst) { v -> app.appScope.launch { app.settings.setEmailStopOnFirst(v) } }
|
||||
if (emailServers.isNotEmpty()) {
|
||||
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,
|
||||
onToggle = { v -> app.appScope.launch { app.settings.setEmailEnabled(s.id, v) } },
|
||||
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测试", "电邮配置测试") } },
|
||||
)
|
||||
}
|
||||
SectionCard(
|
||||
"电邮转发",
|
||||
subtitle = "勾选的邮箱参与转发;开关切换容错/广播",
|
||||
headerAction = {
|
||||
CompactStopOnFirstSwitch(
|
||||
checked = emailStopFirst,
|
||||
onChange = { v -> app.appScope.launch { app.settings.setEmailStopOnFirst(v) } },
|
||||
)
|
||||
},
|
||||
) {
|
||||
if (emailServers.isNotEmpty()) {
|
||||
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,
|
||||
onToggle = { v -> app.appScope.launch { app.settings.setEmailEnabled(s.id, v) } },
|
||||
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测试", "电邮配置测试") } },
|
||||
)
|
||||
}
|
||||
}
|
||||
AddButton { showAddEmail = true }
|
||||
}
|
||||
AddButton { showAddEmail = true }
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 短信兜底 =====
|
||||
item {
|
||||
SectionCard("断网短信兜底", subtitle = "无网络时把验证码/来电用短信发出去") {
|
||||
SwitchRow(title = "启用短信兜底", subtitle = if (smsFallback) "已开启" else "已关闭", checked = smsFallback) { v -> app.appScope.launch { app.settings.setSmsFallback(v) } }
|
||||
if (smsFallback) {
|
||||
SwitchRow(title = "发通一条就停", subtitle = if (smsStopFirst) "容错:首个成功止" else "广播:每个都发", checked = smsStopFirst) { v -> app.appScope.launch { app.settings.setSmsStopOnFirst(v) } }
|
||||
if (!hasSmsPerm) {
|
||||
Spacer(Modifier.size(4.dp))
|
||||
OutlinedButton(onClick = { smsPermLauncher.launch(android.Manifest.permission.SEND_SMS) }, modifier = Modifier.fillMaxWidth()) { Text("授予短信权限") }
|
||||
}
|
||||
if (smsTargets.isNotEmpty()) {
|
||||
Spacer(Modifier.size(8.dp))
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
smsTargets.forEachIndexed { i, s ->
|
||||
ConfigRow(
|
||||
title = s.name.ifBlank { s.number },
|
||||
subtitle = s.number,
|
||||
enabled = s.enabled, isFirst = i == 0, isLast = i == smsTargets.lastIndex,
|
||||
onToggle = { v -> app.appScope.launch { app.settings.setSmsTargetEnabled(s.id, v) } },
|
||||
onEdit = { editingSms = s }, onDelete = { app.appScope.launch { app.settings.deleteSmsTarget(s.id) } },
|
||||
onMoveUp = { app.appScope.launch { app.settings.moveSmsTarget(s.id, true) } },
|
||||
onMoveDown = { app.appScope.launch { app.settings.moveSmsTarget(s.id, false) } },
|
||||
onTest = null,
|
||||
)
|
||||
}
|
||||
SectionCard(
|
||||
"断网短信兜底",
|
||||
subtitle = "勾选的目标号参与转发;开关切换容错/广播",
|
||||
headerAction = {
|
||||
CompactStopOnFirstSwitch(
|
||||
checked = smsStopFirst,
|
||||
onChange = { v -> app.appScope.launch { app.settings.setSmsStopOnFirst(v) } },
|
||||
)
|
||||
},
|
||||
) {
|
||||
if (!hasSmsPerm) {
|
||||
Spacer(Modifier.size(4.dp))
|
||||
OutlinedButton(onClick = { smsPermLauncher.launch(android.Manifest.permission.SEND_SMS) }, modifier = Modifier.fillMaxWidth()) { Text("授予短信权限") }
|
||||
}
|
||||
if (smsTargets.isNotEmpty()) {
|
||||
Spacer(Modifier.size(8.dp))
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
smsTargets.forEachIndexed { i, s ->
|
||||
ConfigRow(
|
||||
title = s.name.ifBlank { s.number },
|
||||
subtitle = s.number,
|
||||
enabled = s.enabled, isFirst = i == 0, isLast = i == smsTargets.lastIndex,
|
||||
onToggle = { v -> app.appScope.launch { app.settings.setSmsTargetEnabled(s.id, v) } },
|
||||
onEdit = { editingSms = s }, onDelete = { app.appScope.launch { app.settings.deleteSmsTarget(s.id) } },
|
||||
onMoveUp = { app.appScope.launch { app.settings.moveSmsTarget(s.id, true) } },
|
||||
onMoveDown = { app.appScope.launch { app.settings.moveSmsTarget(s.id, false) } },
|
||||
onTest = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
AddButton { showAddSms = true }
|
||||
Spacer(Modifier.size(8.dp))
|
||||
val balanceText = when {
|
||||
smsSuspended -> "已挂起(余额不足)"
|
||||
smsBalance < 0 -> "余额未知"
|
||||
else -> "剩余约 $smsBalance 条"
|
||||
}
|
||||
Text("运营商:${detectedCarrier.displayName} · 余额:$balanceText · 手动额度:$smsManualQuota", style = MaterialTheme.typography.bodySmall, color = if (smsSuspended) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
}
|
||||
AddButton { showAddSms = true }
|
||||
Spacer(Modifier.size(8.dp))
|
||||
val balanceText = when {
|
||||
smsSuspended -> "已挂起(余额不足)"
|
||||
smsBalance < 0 -> "余额未知"
|
||||
else -> "剩余约 $smsBalance 条"
|
||||
}
|
||||
Text("运营商:${detectedCarrier.displayName} · 余额:$balanceText · 手动额度:$smsManualQuota", style = MaterialTheme.typography.bodySmall, color = if (smsSuspended) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user