diff --git a/app/src/main/java/com/rainy/token/ui/components/FormatUtils.kt b/app/src/main/java/com/rainy/token/ui/components/FormatUtils.kt new file mode 100644 index 0000000..5e98ff9 --- /dev/null +++ b/app/src/main/java/com/rainy/token/ui/components/FormatUtils.kt @@ -0,0 +1,47 @@ +package com.rainy.token.ui.components + +import java.util.Locale + +/** + * 共享格式化工具函数。 + * + * 从 DashboardScreen / ServiceDetailScreen / WidgetProvider 提取,消除三处重复。 + */ + +/** 格式化金额:整数无小数点,带小数保留 2 位 */ +fun formatAmount(value: Double): String { + return if (value % 1.0 == 0.0) value.toInt().toString() + else String.format(Locale.US, "%.2f", value) +} + +/** 格式化重置倒计时(秒 → "X 天 Y 小时" / "X 小时 Y 分" / "Y 分"),无效返回 "—" */ +fun formatResetInSec(sec: Long): String { + if (sec <= 0) return "—" + val days = sec / 86400 + val hours = (sec % 86400) / 3600 + val minutes = (sec % 3600) / 60 + return when { + days > 0 -> "$days 天 $hours 小时" + hours > 0 -> "$hours 小时 $minutes 分" + else -> "$minutes 分" + } +} + +/** Widget 版本:逻辑相同但空值返回空串(非 "—"),适配 RemoteViews 布局 */ +fun formatResetForWidget(sec: Long): String { + if (sec <= 0) return "" + val days = sec / 86400 + val hours = (sec % 86400) / 3600 + val minutes = (sec % 3600) / 60 + return when { + days > 0 -> "$days 天 $hours 小时" + hours > 0 -> "$hours 小时 $minutes 分" + else -> "$minutes 分" + } +} + +/** 标准化窗口标签:weekly → 每周,其他原样返回 */ +fun normalizeWindowLabel(label: String): String = when (label.lowercase()) { + "weekly" -> "每周" + else -> label +} \ No newline at end of file diff --git a/app/src/main/java/com/rainy/token/ui/dashboard/ServiceBalanceCards.kt b/app/src/main/java/com/rainy/token/ui/dashboard/ServiceBalanceCards.kt index ae1d0e5..0376276 100644 --- a/app/src/main/java/com/rainy/token/ui/dashboard/ServiceBalanceCards.kt +++ b/app/src/main/java/com/rainy/token/ui/dashboard/ServiceBalanceCards.kt @@ -24,6 +24,9 @@ import com.rainy.token.domain.model.ServiceBalance import com.rainy.token.domain.service.ServiceType import com.rainy.token.ui.components.StatusStyle import com.rainy.token.ui.components.StatusLevel +import com.rainy.token.ui.components.formatAmount +import com.rainy.token.ui.components.formatResetInSec +import com.rainy.token.ui.components.normalizeWindowLabel import com.rainy.token.ui.theme.StrawberryPink import com.rainy.token.ui.theme.StatusOrange import com.rainy.token.ui.theme.inkMuted @@ -353,11 +356,6 @@ internal fun OllamaUsageWindows(balance: ServiceBalance) { // ── Shared UI components ── -internal fun normalizeWindowLabel(label: String): String = when (label.lowercase()) { - "weekly" -> "每周" - else -> label -} - @Composable internal fun CompactUsageRowEmpty(label: String, resetInSec: Long?) { Column { @@ -452,18 +450,6 @@ internal fun CompactUsageRow(label: String, pct: Int, resetInSec: Long?) { // ── Dashboard utility functions ── -internal fun formatResetInSec(sec: Long): String { - if (sec <= 0) return "—" - val days = sec / 86400 - val hours = (sec % 86400) / 3600 - val minutes = (sec % 3600) / 60 - return when { - days > 0 -> "$days 天 $hours 小时" - hours > 0 -> "$hours 小时 $minutes 分" - else -> "$minutes 分" - } -} - internal fun DashboardCardUi.statusBadgeStyle(): StatusStyle = when { credentialState == CredentialStatus.State.NOT_CONFIGURED -> StatusStyle("未配置", StatusLevel.WARNING) @@ -503,9 +489,4 @@ internal fun footerText(card: DashboardCardUi): String { diffMin < 1440 -> "${diffMin / 60} 小时前更新" else -> "$timeStr 更新" } -} - -internal fun formatAmount(value: Double): String { - return if (value % 1.0 == 0.0) value.toInt().toString() - else String.format(Locale.US, "%.2f", value) } \ No newline at end of file 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 460a519..3b051cf 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,6 +51,8 @@ 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.components.formatAmount +import com.rainy.token.ui.components.formatResetInSec import com.rainy.token.ui.theme.inkMuted import com.rainy.token.ui.theme.StrawberryPink import java.text.SimpleDateFormat @@ -388,18 +390,6 @@ private fun UsageWindowRow(label: String, pct: Int?, resetInSec: Long?) { } } -private fun formatResetInSec(sec: Long): String { - if (sec <= 0) return "—" - val days = sec / 86400 - val hours = (sec % 86400) / 3600 - val minutes = (sec % 3600) / 60 - return when { - days > 0 -> "$days 天 $hours 小时" - hours > 0 -> "$hours 小时 $minutes 分" - else -> "$minutes 分" - } -} - /** * Ollama Pro 专属:5h + 每周用量窗口卡。 */ @@ -721,9 +711,4 @@ private fun stateToChip(state: State): StatusStyle = when (state) { private fun formatTime(epochMillis: Long): String { val sdf = SimpleDateFormat("MM-dd HH:mm", Locale.getDefault()) return sdf.format(Date(epochMillis)) -} - -private fun formatAmount(value: Double): String { - return if (value % 1.0 == 0.0) value.toInt().toString() - else String.format(Locale.US, "%.2f", value) } \ 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 ff8d35e..6790ab5 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 @@ -14,6 +14,7 @@ import com.rainy.token.R import com.rainy.token.data.cache.BalanceCache import com.rainy.token.data.cache.balanceCacheDataStore import com.rainy.token.domain.service.ServiceType +import com.rainy.token.ui.components.normalizeWindowLabel import kotlinx.coroutines.runBlocking import java.text.SimpleDateFormat import java.util.Date @@ -218,11 +219,6 @@ class OpenCodeGoWidgetProvider : AppWidgetProvider() { views.setTextViewText(R.id.row3_label, third) } - private fun normalizeWindowLabel(label: String): String = when (label.lowercase()) { - "weekly" -> "每周" - else -> label - } - private fun populateRow( views: RemoteViews, pctViewId: Int,