feat: brand logos for Bark/ntfy channel marks + icons for email/SMS

- Bark: official logo from Finb/Bark repo (ic_bark_logo.png)
- ntfy: official logo from binwiederhier/ntfy repo (ic_ntfy_logo.png)
- Meow: Material Pets icon (paw) — no official logo found
- Email: Material Email icon
- SMS: Material Sms icon
- ChannelMark now supports both ImageVector and Painter (PNG logo)
- SectionCard gains optional titleIcon parameter
- version 1.8.9 -> 1.8.10 (code 26 -> 27)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-10 13:23:05 +08:00
parent 22ac5a31ff
commit 191a04a68b
5 changed files with 54 additions and 23 deletions
+2 -2
View File
@@ -13,8 +13,8 @@ android {
applicationId = "com.a2i.forwarder"
minSdk = 34
targetSdk = 36
versionCode = 26
versionName = "1.8.9"
versionCode = 27
versionName = "1.8.10"
}
signingConfigs {
@@ -7,6 +7,7 @@ import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -68,6 +69,8 @@ fun SectionCard(
title: String? = null,
modifier: Modifier = Modifier,
subtitle: String? = null,
titleIcon: ImageVector? = null,
titleIconTint: Color = MaterialTheme.colorScheme.primary,
headerAction: (@Composable () -> Unit)? = null,
content: @Composable ColumnScope.() -> Unit,
) {
@@ -81,12 +84,18 @@ fun SectionCard(
Column(Modifier.padding(16.dp)) {
if (title != null) {
if (headerAction == null) {
Text(
title,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
modifier = Modifier.padding(bottom = if (subtitle == null) 10.dp else 2.dp),
)
Row(verticalAlignment = Alignment.CenterVertically) {
if (titleIcon != null) {
Icon(titleIcon, null, tint = titleIconTint, modifier = Modifier.size(22.dp))
Spacer(Modifier.width(8.dp))
}
Text(
title,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
modifier = Modifier.padding(bottom = if (subtitle == null) 10.dp else 2.dp),
)
}
if (subtitle != null) {
Text(
subtitle,
@@ -103,12 +112,18 @@ fun SectionCard(
verticalAlignment = Alignment.Top,
) {
Column(Modifier.weight(1f)) {
Text(
title,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
modifier = Modifier.padding(bottom = if (subtitle == null) 0.dp else 2.dp),
)
Row(verticalAlignment = Alignment.CenterVertically) {
if (titleIcon != null) {
Icon(titleIcon, null, tint = titleIconTint, modifier = Modifier.size(22.dp))
Spacer(Modifier.width(8.dp))
}
Text(
title,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
modifier = Modifier.padding(bottom = if (subtitle == null) 0.dp else 2.dp),
)
}
if (subtitle != null) {
Text(
subtitle,
@@ -6,6 +6,7 @@ import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
@@ -26,11 +27,15 @@ import androidx.compose.material.icons.automirrored.filled.Send
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Email
import androidx.compose.material.icons.filled.History
import androidx.compose.material.icons.filled.Info
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.KeyboardArrowUp
import androidx.compose.material.icons.filled.Notifications
import androidx.compose.material.icons.filled.Pets
import androidx.compose.material.icons.filled.RestartAlt
import androidx.compose.material.icons.filled.Sms
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.SystemUpdateAlt
import androidx.compose.material.icons.filled.Update
@@ -57,12 +62,16 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.documentfile.provider.DocumentFile
import com.a2i.forwarder.A2iApp
import com.a2i.forwarder.R
import com.a2i.forwarder.core.BarkClient
import com.a2i.forwarder.core.EmailSender
import com.a2i.forwarder.core.IconExporter
@@ -167,6 +176,7 @@ fun SettingsScreen(onOpenLog: () -> Unit) {
summary = "iPhone / iPad 推送目标",
mark = "B",
markColor = Color(0xFFFF9800),
markLogo = painterResource(R.drawable.ic_bark_logo),
platforms = listOf("iOS"),
stopFirst = barkStopFirst,
onStopFirstChange = { v -> app.appScope.launch { app.settings.setBarkStopOnFirst(v) } },
@@ -199,6 +209,7 @@ fun SettingsScreen(onOpenLog: () -> Unit) {
summary = "Android / iOS topic 推送",
mark = "n",
markColor = Color(0xFF43A047),
markLogo = painterResource(R.drawable.ic_ntfy_logo),
platforms = listOf("Android", "iOS"),
stopFirst = ntfyStopFirst,
onStopFirstChange = { v -> app.appScope.launch { app.settings.setNtfyStopOnFirst(v) } },
@@ -234,6 +245,7 @@ fun SettingsScreen(onOpenLog: () -> Unit) {
summary = "鸿蒙手机推送目标",
mark = "M",
markColor = Color(0xFF7E57C2),
markIcon = Icons.Filled.Pets,
platforms = listOf("HarmonyOS"),
stopFirst = meowStopFirst,
onStopFirstChange = { v -> app.appScope.launch { app.settings.setMeowStopOnFirst(v) } },
@@ -269,6 +281,7 @@ fun SettingsScreen(onOpenLog: () -> Unit) {
summary = "SMTP 邮箱推送目标",
mark = "@",
markColor = Color(0xFF1976D2),
markIcon = Icons.Filled.Email,
platforms = listOf("Email"),
stopFirst = emailStopFirst,
onStopFirstChange = { v -> app.appScope.launch { app.settings.setEmailStopOnFirst(v) } },
@@ -302,6 +315,7 @@ fun SettingsScreen(onOpenLog: () -> Unit) {
summary = "无网络时发送关键通知",
mark = "S",
markColor = Color(0xFF00897B),
markIcon = Icons.Filled.Sms,
platforms = listOf("SMS"),
stopFirst = smsStopFirst,
onStopFirstChange = { v -> app.appScope.launch { app.settings.setSmsStopOnFirst(v) } },
@@ -421,6 +435,8 @@ private fun ChannelSectionHeader(
summary: String,
mark: String,
markColor: Color,
markIcon: ImageVector? = null,
markLogo: Painter? = null,
platforms: List<String>,
stopFirst: Boolean,
onStopFirstChange: (Boolean) -> Unit,
@@ -435,7 +451,7 @@ private fun ChannelSectionHeader(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
ChannelMark(mark = mark, tint = markColor)
ChannelMark(mark = mark, tint = markColor, icon = markIcon, logoPainter = markLogo)
Spacer(Modifier.width(10.dp))
Row(
modifier = Modifier.weight(1f),
@@ -492,7 +508,7 @@ private fun ChannelSectionHeader(
}
@Composable
private fun ChannelMark(mark: String, tint: Color) {
private fun ChannelMark(mark: String, tint: Color, icon: ImageVector? = null, logoPainter: Painter? = null) {
Surface(
modifier = Modifier.size(36.dp),
shape = RoundedCornerShape(10.dp),
@@ -504,13 +520,13 @@ private fun ChannelMark(mark: String, tint: Color) {
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
mark,
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.Bold,
color = tint,
maxLines = 1,
)
if (logoPainter != null) {
Image(logoPainter, null, modifier = Modifier.size(24.dp))
} else if (icon != null) {
Icon(icon, null, tint = tint, modifier = Modifier.size(20.dp))
} else {
Text(mark, style = MaterialTheme.typography.titleSmall, fontWeight = FontWeight.Bold, color = tint, maxLines = 1)
}
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB