diff --git a/.commit_msg b/.commit_msg new file mode 100644 index 0000000..4210909 --- /dev/null +++ b/.commit_msg @@ -0,0 +1,22 @@ +feat: MIUI Widget 完整适配 + 深色模式全局修复 + +MIUI Widget 接入(负一屏 + 曝光刷新) +- AndroidManifest 新增 miuiWidget 全套标识 +- intent-filter 新增 miui.appwidget.action.APPWIDGET_UPDATE +- 根布局改为 FrameLayout + @android:id/background +- minWidth 250→300dp(4x2 标准) +- onReceive() 处理 MIUI 曝光刷新 +- 5min 冷却自动刷新 + +深色模式全面适配 +- drawable-night/widget_bg.xml +- drawable-night/ic_opencode_go_logo.xml +- drawable-night/ic_deepseek_logo.png + wordmark +- layout-night/widget_opencode_go.xml +- DarkInkWarm/DarkInkMuted + inkWarm()/inkMuted() +- 全页面 51 处 InkMuted → inkMuted() +- ServiceIcon 白底 → 主题自适应 + +用户体验 +- 顶部栏 + 按钮:一键添桌面小组件 +- 负一屏底部遮挡修复(间距压缩 28dp) \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b238319..0bda2f5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -28,22 +28,44 @@ - + + + android:exported="true" + android:label="@string/widget_name"> + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/rainy/token/ui/components/ServiceIcon.kt b/app/src/main/java/com/rainy/token/ui/components/ServiceIcon.kt index 3cdcd4c..1ac174f 100644 --- a/app/src/main/java/com/rainy/token/ui/components/ServiceIcon.kt +++ b/app/src/main/java/com/rainy/token/ui/components/ServiceIcon.kt @@ -2,6 +2,7 @@ package com.rainy.token.ui.components import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape @@ -40,7 +41,7 @@ fun ServiceIcon( modifier = modifier .size(size.dp) .clip(RoundedCornerShape(8.dp)) - .background(Color.White), + .background(if (isSystemInDarkTheme()) Color(0xFF352329) else Color.White), contentAlignment = Alignment.Center ) { Image( diff --git a/app/src/main/java/com/rainy/token/ui/dashboard/DashboardScreen.kt b/app/src/main/java/com/rainy/token/ui/dashboard/DashboardScreen.kt index 817d122..5c7d997 100644 --- a/app/src/main/java/com/rainy/token/ui/dashboard/DashboardScreen.kt +++ b/app/src/main/java/com/rainy/token/ui/dashboard/DashboardScreen.kt @@ -22,6 +22,7 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.Card @@ -59,8 +60,14 @@ import com.rainy.token.ui.components.ServiceIcon import com.rainy.token.ui.components.StatusChip import com.rainy.token.ui.components.StatusLevel import com.rainy.token.ui.components.StatusStyle -import com.rainy.token.ui.theme.InkMuted +import com.rainy.token.ui.theme.inkMuted import com.rainy.token.ui.theme.StrawberryPink +import android.appwidget.AppWidgetManager +import android.content.ComponentName +import android.content.Intent +import androidx.compose.ui.platform.LocalContext +import android.widget.Toast +import com.rainy.token.ui.widget.OpenCodeGoWidgetProvider import java.text.SimpleDateFormat import java.util.Date import java.util.Locale @@ -110,11 +117,36 @@ fun DashboardScreen( Text( text = "AI 余额一览", style = MaterialTheme.typography.bodySmall, - color = InkMuted + color = inkMuted() ) } }, actions = { + // 添加小组件:优先 requestPinAppWidget,不支持则打开系统小组件选择器 + val context = LocalContext.current + IconButton(onClick = { + val appWidgetManager = AppWidgetManager.getInstance(context) + val component = ComponentName(context, OpenCodeGoWidgetProvider::class.java) + if (appWidgetManager.isRequestPinAppWidgetSupported) { + appWidgetManager.requestPinAppWidget(component, null, null) + } else { + // MIUI 等桌面回退:直接打开小组件选择器 + try { + val pickerIntent = Intent(AppWidgetManager.ACTION_APPWIDGET_PICK).apply { + putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID) + } + context.startActivity(pickerIntent) + } catch (e: Exception) { + Toast.makeText(context, "请在桌面长按 → 小组件 → 查找雨晴Token", Toast.LENGTH_LONG).show() + } + } + }) { + Icon( + imageVector = Icons.Filled.Add, + contentDescription = "添加小组件到桌面", + tint = StrawberryPink + ) + } IconButton( onClick = { viewModel.refresh() }, enabled = !uiState.refreshing @@ -165,7 +197,7 @@ fun DashboardScreen( Text( text = "服务余额", style = MaterialTheme.typography.labelMedium, - color = InkMuted, + color = inkMuted(), modifier = Modifier.padding(top = 8.dp) ) } @@ -204,7 +236,7 @@ private fun DashboardCard(card: DashboardCardUi, onClick: () -> Unit) { Text( text = secondaryLine(card), style = MaterialTheme.typography.bodySmall, - color = InkMuted + color = inkMuted() ) } StatusChip(style = card.statusBadgeStyle()) @@ -223,7 +255,7 @@ private fun DashboardCard(card: DashboardCardUi, onClick: () -> Unit) { color = if (card.lastFetchError != null) MaterialTheme.colorScheme.error else - InkMuted + inkMuted() ) } } @@ -237,24 +269,24 @@ private fun BalanceMainArea(card: DashboardCardUi) { Text( text = "—", style = MaterialTheme.typography.displayMedium, - color = InkMuted + color = inkMuted() ) Text( text = "点击配置凭据", style = MaterialTheme.typography.bodySmall, - color = InkMuted + color = inkMuted() ) } balance == null -> { Text( text = "—", style = MaterialTheme.typography.displayMedium, - color = InkMuted + color = inkMuted() ) Text( text = "下拉刷新", style = MaterialTheme.typography.bodySmall, - color = InkMuted + color = inkMuted() ) } card.service == ServiceType.OPENCODE_GO -> { @@ -278,7 +310,7 @@ private fun BalanceMainArea(card: DashboardCardUi) { Text( text = balance.unit, style = MaterialTheme.typography.titleMedium, - color = InkMuted, + color = inkMuted(), modifier = Modifier.padding(bottom = 6.dp) ) } @@ -293,7 +325,7 @@ private fun BalanceMainArea(card: DashboardCardUi) { Text( text = "本月已用 ${formatAmount(spent)}${balance.unit}", style = MaterialTheme.typography.bodySmall, - color = InkMuted + color = inkMuted() ) } } @@ -313,7 +345,7 @@ private fun OpenCodeGoMainBalance(balance: com.rainy.token.domain.model.ServiceB Text( text = "%", style = MaterialTheme.typography.titleLarge, - color = InkMuted, + color = inkMuted(), fontWeight = FontWeight.SemiBold, modifier = Modifier.padding(bottom = 6.dp, start = 2.dp) ) @@ -321,7 +353,7 @@ private fun OpenCodeGoMainBalance(balance: com.rainy.token.domain.model.ServiceB Text( text = "5h 用量", style = MaterialTheme.typography.titleMedium, - color = InkMuted, + color = inkMuted(), modifier = Modifier.padding(bottom = 6.dp) ) } @@ -356,13 +388,13 @@ private fun CompactUsageRowEmpty(label: String, resetInSec: Long?) { Text( text = label, style = MaterialTheme.typography.bodySmall, - color = InkMuted, + color = inkMuted(), modifier = Modifier.weight(1f) ) Text( text = "—", style = MaterialTheme.typography.bodyMedium, - color = InkMuted + color = inkMuted() ) } Spacer(modifier = Modifier.height(4.dp)) @@ -372,7 +404,7 @@ private fun CompactUsageRowEmpty(label: String, resetInSec: Long?) { .fillMaxWidth() .height(4.dp) .clip(RoundedCornerShape(2.dp)), - color = InkMuted.copy(alpha = 0.3f), + color = inkMuted().copy(alpha = 0.3f), trackColor = MaterialTheme.colorScheme.surfaceVariant, strokeCap = androidx.compose.ui.graphics.StrokeCap.Butt ) @@ -381,7 +413,7 @@ private fun CompactUsageRowEmpty(label: String, resetInSec: Long?) { Text( text = "${formatResetInSec(resetInSec)}后重置", style = MaterialTheme.typography.bodySmall, - color = InkMuted + color = inkMuted() ) } } @@ -398,7 +430,7 @@ private fun CompactUsageRow(label: String, pct: Int, resetInSec: Long?) { Text( text = label, style = MaterialTheme.typography.bodySmall, - color = InkMuted, + color = inkMuted(), modifier = Modifier.weight(1f) ) Text( @@ -433,7 +465,7 @@ private fun CompactUsageRow(label: String, pct: Int, resetInSec: Long?) { Text( text = "${formatResetInSec(resetInSec)}后重置", style = MaterialTheme.typography.bodySmall, - color = InkMuted + color = inkMuted() ) } } @@ -511,13 +543,13 @@ private fun DashboardFooter() { Text( text = "RainyToken · v1.0", style = MaterialTheme.typography.bodySmall, - color = InkMuted + color = inkMuted() ) Spacer(modifier = Modifier.height(4.dp)) Text( text = "本地加密存储,不上传任何数据", style = MaterialTheme.typography.bodySmall, - color = InkMuted + color = inkMuted() ) } } \ No newline at end of file diff --git a/app/src/main/java/com/rainy/token/ui/dashboard/UsageDataScreen.kt b/app/src/main/java/com/rainy/token/ui/dashboard/UsageDataScreen.kt index b481d03..db7f3a2 100644 --- a/app/src/main/java/com/rainy/token/ui/dashboard/UsageDataScreen.kt +++ b/app/src/main/java/com/rainy/token/ui/dashboard/UsageDataScreen.kt @@ -52,7 +52,7 @@ import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.rainy.token.data.local.UsageRecord -import com.rainy.token.ui.theme.InkMuted +import com.rainy.token.ui.theme.inkMuted import com.rainy.token.ui.theme.StrawberryPink import java.text.SimpleDateFormat import java.time.Instant @@ -121,7 +121,7 @@ fun UsageDataScreen( }) } } - Text("UTC+0", style = MaterialTheme.typography.bodySmall, color = InkMuted) + Text("UTC+0", style = MaterialTheme.typography.bodySmall, color = inkMuted()) Spacer(Modifier.weight(1f)) // 模型筛选 Box { @@ -150,17 +150,17 @@ fun UsageDataScreen( CustomTimeRangeRow(customStartMs, customEndMs, { showStartPicker = true }, { showEndPicker = true }, { viewModel.setTimeFilter(TimeFilter.Custom(customStartMs, customEndMs)) }) - Text("所有时间按 UTC+0 计算", style = MaterialTheme.typography.bodySmall, color = InkMuted) + Text("所有时间按 UTC+0 计算", style = MaterialTheme.typography.bodySmall, color = inkMuted()) } // ─── 记录数 + 分页 ─── Row(Modifier.fillMaxWidth().padding(vertical = 4.dp), Arrangement.SpaceBetween, Alignment.CenterVertically) { - Text("${state.totalRecords} 条记录", style = MaterialTheme.typography.bodySmall, color = InkMuted) + Text("${state.totalRecords} 条记录", style = MaterialTheme.typography.bodySmall, color = inkMuted()) if (state.totalPages > 1) { Row(verticalAlignment = Alignment.CenterVertically) { TextButton(onClick = { viewModel.prevPage() }, enabled = state.currentPage > 1) { Text("◀", color = StrawberryPink, fontSize = 13.sp) } - Text("${state.currentPage}/${state.totalPages}", style = MaterialTheme.typography.bodySmall, color = InkMuted) + Text("${state.currentPage}/${state.totalPages}", style = MaterialTheme.typography.bodySmall, color = inkMuted()) TextButton(onClick = { viewModel.nextPage() }, enabled = state.currentPage < state.totalPages) { Text("▶", color = StrawberryPink, fontSize = 13.sp) } Spacer(Modifier.width(6.dp)) @@ -173,7 +173,7 @@ fun UsageDataScreen( keyboardOptions = KeyboardOptions.Default.copy(keyboardType = androidx.compose.ui.text.input.KeyboardType.Number), colors = OutlinedTextFieldDefaults.colors( focusedBorderColor = StrawberryPink, - unfocusedBorderColor = InkMuted.copy(alpha = 0.4f) + unfocusedBorderColor = inkMuted().copy(alpha = 0.4f) ) ) TextButton(onClick = { @@ -201,11 +201,11 @@ fun UsageDataScreen( // ─── 记录列表 ─── if (state.loading) { Box(Modifier.fillMaxWidth().height(200.dp), contentAlignment = Alignment.Center) { - Text("加载中…", color = InkMuted) + Text("加载中…", color = inkMuted()) } } else if (state.records.isEmpty()) { Box(Modifier.fillMaxWidth().height(200.dp), contentAlignment = Alignment.Center) { - Text("暂无数据", color = InkMuted) + Text("暂无数据", color = inkMuted()) } } else { LazyColumn(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(1.dp)) { @@ -265,7 +265,7 @@ fun UsageDataScreen( @Composable private fun HeaderCell(text: String, modifier: Modifier) { Text(text, modifier = modifier, fontSize = 12.sp, fontWeight = FontWeight.Bold, - color = InkMuted, textAlign = TextAlign.Center) + color = inkMuted(), textAlign = TextAlign.Center) } @Composable @@ -293,7 +293,7 @@ private fun DataCell(text: String, modifier: Modifier) { @Composable private fun RawField(label: String, value: String) { Row(Modifier.fillMaxWidth().padding(vertical = 1.dp)) { - Text("$label: ", fontSize = 12.sp, fontWeight = FontWeight.Medium, color = InkMuted) + Text("$label: ", fontSize = 12.sp, fontWeight = FontWeight.Medium, color = inkMuted()) Text(value, fontSize = 12.sp, color = MaterialTheme.colorScheme.onSurface) } } diff --git a/app/src/main/java/com/rainy/token/ui/dashboard/UsageOverviewScreen.kt b/app/src/main/java/com/rainy/token/ui/dashboard/UsageOverviewScreen.kt index 785d90f..efb2c93 100644 --- a/app/src/main/java/com/rainy/token/ui/dashboard/UsageOverviewScreen.kt +++ b/app/src/main/java/com/rainy/token/ui/dashboard/UsageOverviewScreen.kt @@ -43,7 +43,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.rainy.token.data.local.ModelStats import com.rainy.token.data.local.OverviewStats -import com.rainy.token.ui.theme.InkMuted +import com.rainy.token.ui.theme.inkMuted import com.rainy.token.ui.theme.StrawberryPink import java.text.SimpleDateFormat import java.util.Date @@ -89,7 +89,7 @@ fun UsageOverviewScreen( Text(uiState.timeFilter.label, color = StrawberryPink, fontWeight = FontWeight.SemiBold) Icon(Icons.Filled.ArrowDropDown, null, tint = StrawberryPink) } - Text("UTC+0", style = MaterialTheme.typography.bodySmall, color = InkMuted, modifier = Modifier.padding(start = 4.dp)) + Text("UTC+0", style = MaterialTheme.typography.bodySmall, color = inkMuted(), modifier = Modifier.padding(start = 4.dp)) DropdownMenu(menuExpanded, { menuExpanded = false }) { listOf(TimeFilter.All, TimeFilter.Last5h, TimeFilter.Last24h, TimeFilter.Today, TimeFilter.Yesterday, @@ -112,7 +112,7 @@ fun UsageOverviewScreen( CustomTimeRangeRow(customStartMs, customEndMs, { showStartPicker = true }, { showEndPicker = true }, { viewModel.setTimeFilter(TimeFilter.Custom(customStartMs, customEndMs)) }) - Text("所有时间按 UTC+0 计算", style = MaterialTheme.typography.bodySmall, color = InkMuted) + Text("所有时间按 UTC+0 计算", style = MaterialTheme.typography.bodySmall, color = inkMuted()) } } // 总览 @@ -157,7 +157,7 @@ fun UsageOverviewScreen( Row(Modifier.fillMaxWidth(), Arrangement.SpaceBetween, Alignment.CenterVertically) { TextButton(onClick = { viewModel.prevDailyPage() }, enabled = uiState.dailyPage > 1) { Text("◀ 上一页", color = StrawberryPink, style = MaterialTheme.typography.bodySmall) } - Text("${uiState.dailyPage} / $totalPages", style = MaterialTheme.typography.bodySmall, color = InkMuted) + Text("${uiState.dailyPage} / $totalPages", style = MaterialTheme.typography.bodySmall, color = inkMuted()) TextButton(onClick = { viewModel.nextDailyPage() }, enabled = uiState.dailyPage < totalPages) { Text("下一页 ▶", color = StrawberryPink, style = MaterialTheme.typography.bodySmall) } } @@ -193,7 +193,7 @@ fun UsageOverviewScreen( @Composable private fun CacheBreakdownCard(overview: OverviewStats) { Card(Modifier.fillMaxWidth(), RoundedCornerShape(20.dp), CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface)) { Column(Modifier.padding(16.dp)) { - Text("缓存 Token 细分", style = MaterialTheme.typography.labelMedium, color = InkMuted) + Text("缓存 Token 细分", style = MaterialTheme.typography.labelMedium, color = inkMuted()) Spacer(Modifier.height(8.dp)) Row(Modifier.fillMaxWidth(), Arrangement.SpaceEvenly) { StatItem("缓存读取", formatTokenCount(overview.cacheReadTokens)) @@ -201,7 +201,7 @@ fun UsageOverviewScreen( } if (overview.cacheReadTokens > 0) { Spacer(Modifier.height(4.dp)) - Text("※ 缓存读取已计入「输入 Token」", style = MaterialTheme.typography.bodySmall, color = InkMuted) + Text("※ 缓存读取已计入「输入 Token」", style = MaterialTheme.typography.bodySmall, color = inkMuted()) } } } @@ -216,11 +216,11 @@ fun UsageOverviewScreen( Row(Modifier.fillMaxWidth().padding(12.dp), verticalAlignment = Alignment.CenterVertically) { Column(Modifier.weight(1f)) { Text(stat.model, style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Medium) - Text("${stat.count} 次调用", style = MaterialTheme.typography.bodySmall, color = InkMuted) + Text("${stat.count} 次调用", style = MaterialTheme.typography.bodySmall, color = inkMuted()) } Column(horizontalAlignment = Alignment.End) { Text(formatTokenCount(stat.totalTokens), style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Bold, color = StrawberryPink) - Text("$${String.format(Locale.US, "%.4f", stat.totalCost / 100_000_000.0)}", style = MaterialTheme.typography.bodySmall, color = InkMuted) + Text("$${String.format(Locale.US, "%.4f", stat.totalCost / 100_000_000.0)}", style = MaterialTheme.typography.bodySmall, color = inkMuted()) } } } @@ -233,7 +233,7 @@ fun UsageOverviewScreen( Text(utcFmt.format(Date(day.dayTs)), style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Medium, modifier = Modifier.weight(1f)) Column(horizontalAlignment = Alignment.End) { Text(formatTokenCount(day.totalTokens), style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Bold, color = StrawberryPink) - Text("${day.count} 次 · $${String.format(Locale.US, "%.4f", day.totalCost / 100_000_000.0)}", style = MaterialTheme.typography.bodySmall, color = InkMuted) + Text("${day.count} 次 · $${String.format(Locale.US, "%.4f", day.totalCost / 100_000_000.0)}", style = MaterialTheme.typography.bodySmall, color = inkMuted()) } } } @@ -242,7 +242,7 @@ fun UsageOverviewScreen( @Composable private fun StatItem(label: String, value: String) { Column(horizontalAlignment = Alignment.CenterHorizontally) { Text(value, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold, color = StrawberryPink) - Text(label, style = MaterialTheme.typography.bodySmall, color = InkMuted) + Text(label, style = MaterialTheme.typography.bodySmall, color = inkMuted()) } } diff --git a/app/src/main/java/com/rainy/token/ui/servicedetail/ServiceDetailScreen.kt b/app/src/main/java/com/rainy/token/ui/servicedetail/ServiceDetailScreen.kt index 7229173..a36b713 100644 --- a/app/src/main/java/com/rainy/token/ui/servicedetail/ServiceDetailScreen.kt +++ b/app/src/main/java/com/rainy/token/ui/servicedetail/ServiceDetailScreen.kt @@ -51,7 +51,7 @@ import com.rainy.token.ui.components.ServiceIcon import com.rainy.token.ui.components.StatusChip import com.rainy.token.ui.components.StatusLevel import com.rainy.token.ui.components.StatusStyle -import com.rainy.token.ui.theme.InkMuted +import com.rainy.token.ui.theme.inkMuted import com.rainy.token.ui.theme.StrawberryPink import java.text.SimpleDateFormat import java.util.Date @@ -187,7 +187,7 @@ private fun OpenCodeGoWindowsCard(state: State) { Text( text = "用量窗口", style = MaterialTheme.typography.labelLarge, - color = InkMuted + color = inkMuted() ) Spacer(modifier = Modifier.height(12.dp)) UsageWindowRow( @@ -226,7 +226,7 @@ private fun UsageWindowRow(label: String, pct: Int?, resetInSec: Long?) { Text( text = label, style = MaterialTheme.typography.bodyMedium, - color = InkMuted, + color = inkMuted(), modifier = Modifier.weight(1f) ) Row(verticalAlignment = Alignment.Bottom) { @@ -238,7 +238,7 @@ private fun UsageWindowRow(label: String, pct: Int?, resetInSec: Long?) { Text( text = "%", style = MaterialTheme.typography.bodyMedium, - color = InkMuted, + color = inkMuted(), modifier = Modifier.padding(bottom = 2.dp, start = 2.dp) ) } @@ -262,7 +262,7 @@ private fun UsageWindowRow(label: String, pct: Int?, resetInSec: Long?) { Text( text = "${formatResetInSec(resetInSec)}后重置", style = MaterialTheme.typography.bodySmall, - color = InkMuted + color = inkMuted() ) } } @@ -293,7 +293,7 @@ private fun MainBalanceCard(state: State, service: ServiceType) { Text( text = mainCardLabel(service), style = MaterialTheme.typography.labelLarge, - color = InkMuted + color = inkMuted() ) Spacer(modifier = Modifier.weight(1f)) StatusChip(style = stateToChip(state)) @@ -322,14 +322,14 @@ private fun MainBalanceCard(state: State, service: ServiceType) { Text( text = "显示的是上次成功获取的余额", style = MaterialTheme.typography.bodySmall, - color = InkMuted + color = inkMuted() ) } is State.ManualModeHint -> { Text( text = "—", style = MaterialTheme.typography.displayMedium, - color = InkMuted + color = inkMuted() ) } } @@ -343,7 +343,7 @@ private fun BalanceBigNumber(balance: ServiceBalance?) { Text( text = "—", style = MaterialTheme.typography.displayLarge, - color = InkMuted + color = inkMuted() ) return } @@ -358,7 +358,7 @@ private fun BalanceBigNumber(balance: ServiceBalance?) { Text( text = balance.unit, style = MaterialTheme.typography.titleLarge, - color = InkMuted, + color = inkMuted(), modifier = Modifier.padding(bottom = 8.dp) ) } @@ -375,7 +375,7 @@ private fun BalanceBigNumber(balance: ServiceBalance?) { Text( text = "本月消费 ${formatAmount(spent)}${balance.unit}", style = MaterialTheme.typography.bodyMedium, - color = InkMuted + color = inkMuted() ) } } @@ -406,7 +406,7 @@ private fun DeepSeekBreakdownCard(state: State) { Text( text = "余额构成", style = MaterialTheme.typography.labelLarge, - color = InkMuted + color = inkMuted() ) Spacer(modifier = Modifier.height(12.dp)) BreakdownRow("赠送余额", granted, balance!!.unit) @@ -427,7 +427,7 @@ private fun BreakdownRow(label: String, value: Double, unit: String) { Text( text = label, style = MaterialTheme.typography.bodyMedium, - color = InkMuted, + color = inkMuted(), modifier = Modifier.weight(1f) ) Row(verticalAlignment = Alignment.Bottom) { @@ -440,7 +440,7 @@ private fun BreakdownRow(label: String, value: Double, unit: String) { Text( text = unit, style = MaterialTheme.typography.bodyMedium, - color = InkMuted, + color = inkMuted(), modifier = Modifier.padding(bottom = 2.dp) ) } diff --git a/app/src/main/java/com/rainy/token/ui/theme/Color.kt b/app/src/main/java/com/rainy/token/ui/theme/Color.kt index ba89c31..467e195 100644 --- a/app/src/main/java/com/rainy/token/ui/theme/Color.kt +++ b/app/src/main/java/com/rainy/token/ui/theme/Color.kt @@ -37,3 +37,6 @@ val DarkBackground = Color(0xFF1F1419) // 暖深背景 val DarkSurface = Color(0xFF2A1F25) // 卡片 val DarkOnSurface = Color(0xFFEFE0E5) // 卡片文本 val DarkPrimary = Color(0xFFFFB3C6) // 暗色下主色更亮 +val DarkInkWarm = Color(0xFFEFE0E5) // 暗色下暖黑→浅粉白 +val DarkInkMuted = Color(0xFFC9B8BE) // 暗色下暖灰→浅灰粉 +val DarkInkOutline = Color(0xFF4D3A42) // 暗色下描边 diff --git a/app/src/main/java/com/rainy/token/ui/theme/Theme.kt b/app/src/main/java/com/rainy/token/ui/theme/Theme.kt index 19fad57..c375f84 100644 --- a/app/src/main/java/com/rainy/token/ui/theme/Theme.kt +++ b/app/src/main/java/com/rainy/token/ui/theme/Theme.kt @@ -88,4 +88,14 @@ fun RainyTokenTheme( typography = RainyTypography, content = content ) -} \ No newline at end of file +} + +/** + * 暗色模式自适应的文本颜色。 + * 浅色模式下返回暖黑/暖灰,深色模式下自动切换到浅色。 + */ +@Composable +fun inkWarm(): Color = if (isSystemInDarkTheme()) DarkInkWarm else InkWarm + +@Composable +fun inkMuted(): Color = if (isSystemInDarkTheme()) DarkInkMuted else InkMuted \ No newline at end of file diff --git a/app/src/main/java/com/rainy/token/ui/widget/OpenCodeGoWidgetProvider.kt b/app/src/main/java/com/rainy/token/ui/widget/OpenCodeGoWidgetProvider.kt index fa2c64c..507380c 100644 --- a/app/src/main/java/com/rainy/token/ui/widget/OpenCodeGoWidgetProvider.kt +++ b/app/src/main/java/com/rainy/token/ui/widget/OpenCodeGoWidgetProvider.kt @@ -19,13 +19,30 @@ import java.util.Date import java.util.Locale /** - * OpenCode Go 桌面小组件。 + * OpenCode Go 桌面小组件(MIUI Widget)。 * - * 显示 3 个用量窗口(5h / 本周 / 本月)的百分比 + 进度条 + 重置倒计时。 - * 优先展示缓存数据;缓存为空或超过 5 分钟冷却时间时,自动触发后台网络刷新。 + * 显示 3 个用量窗口(5h / 本周 / 本月)的百分比 + 进度条 + 重置倒计时 + DeepSeek 余额。 + * + * 刷新路径: + * - MIUI 曝光刷新:用户划到负一屏 → miui.appwidget.action.APPWIDGET_UPDATE → onReceive → onUpdate + * - 标准定时刷新:系统 30min 定时 → android.appwidget.action.APPWIDGET_UPDATE → onUpdate + * - 手动 ↻ 按钮:PendingIntent 直通 WidgetRefreshReceiver → 网络请求 → notifyDataChanged + * - APP 内刷新:notifyDataChanged() → 广播 ACTION_APPWIDGET_UPDATE → onUpdate + * - 缓存为空/过期时 onUpdate 自动触发后台刷新 */ class OpenCodeGoWidgetProvider : AppWidgetProvider() { + override fun onReceive(context: Context, intent: Intent) { + if ("miui.appwidget.action.APPWIDGET_UPDATE" == intent.action) { + val appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS) + if (appWidgetIds != null) { + onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds) + } + } else { + super.onReceive(context, intent) + } + } + override fun onUpdate( context: Context, appWidgetManager: AppWidgetManager, @@ -44,7 +61,7 @@ class OpenCodeGoWidgetProvider : AppWidgetProvider() { context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) - views.setOnClickPendingIntent(R.id.widget_root, pendingIntent) + views.setOnClickPendingIntent(R.id.widget_content, pendingIntent) // 刷新按钮 → 后台广播刷新 val refreshPendingIntent = PendingIntent.getBroadcast( diff --git a/app/src/main/res/drawable-night/ic_deepseek_logo.png b/app/src/main/res/drawable-night/ic_deepseek_logo.png new file mode 100644 index 0000000..85ca0b5 Binary files /dev/null and b/app/src/main/res/drawable-night/ic_deepseek_logo.png differ diff --git a/app/src/main/res/drawable-night/ic_opencode_go_logo.xml b/app/src/main/res/drawable-night/ic_opencode_go_logo.xml new file mode 100644 index 0000000..8874b6a --- /dev/null +++ b/app/src/main/res/drawable-night/ic_opencode_go_logo.xml @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/rainytoken_wordmark.png b/app/src/main/res/drawable-night/rainytoken_wordmark.png new file mode 100644 index 0000000..b4d65ba Binary files /dev/null and b/app/src/main/res/drawable-night/rainytoken_wordmark.png differ diff --git a/app/src/main/res/drawable-night/widget_bg.xml b/app/src/main/res/drawable-night/widget_bg.xml new file mode 100644 index 0000000..f99fb09 --- /dev/null +++ b/app/src/main/res/drawable-night/widget_bg.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-nodpi/widget_preview.png b/app/src/main/res/drawable-nodpi/widget_preview.png new file mode 100644 index 0000000..187a472 Binary files /dev/null and b/app/src/main/res/drawable-nodpi/widget_preview.png differ diff --git a/app/src/main/res/layout-night/widget_opencode_go.xml b/app/src/main/res/layout-night/widget_opencode_go.xml new file mode 100644 index 0000000..7353014 --- /dev/null +++ b/app/src/main/res/layout-night/widget_opencode_go.xml @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/widget_opencode_go.xml b/app/src/main/res/layout/widget_opencode_go.xml index 6adf53b..b4a1722 100644 --- a/app/src/main/res/layout/widget_opencode_go.xml +++ b/app/src/main/res/layout/widget_opencode_go.xml @@ -1,11 +1,19 @@ - + + android:background="@drawable/widget_bg"> + + + + android:layout_marginBottom="6dp"> + android:layout_marginBottom="3dp"> + android:layout_marginBottom="3dp"> + android:layout_marginBottom="3dp"> + android:layout_marginBottom="2dp"> - \ No newline at end of file + + + \ No newline at end of file diff --git a/app/src/main/res/layout/widget_opencode_go_preview.xml b/app/src/main/res/layout/widget_opencode_go_preview.xml new file mode 100644 index 0000000..c6927ff --- /dev/null +++ b/app/src/main/res/layout/widget_opencode_go_preview.xml @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 08ab72d..a787889 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,5 @@ 雨晴Token + AI用量面板 +OpenCode Go 用量 + DeepSeek 余额,自动刷新 \ No newline at end of file diff --git a/app/src/main/res/xml/widget_opencode_go_info.xml b/app/src/main/res/xml/widget_opencode_go_info.xml index 537cdaa..924714c 100644 --- a/app/src/main/res/xml/widget_opencode_go_info.xml +++ b/app/src/main/res/xml/widget_opencode_go_info.xml @@ -1,9 +1,12 @@