refactor: extract duplicate format functions to FormatUtils

- New: ui/components/FormatUtils.kt — formatAmount/formatResetInSec/formatResetForWidget/normalizeWindowLabel
- ServiceBalanceCards.kt: remove internal definitions, import from FormatUtils
- ServiceDetailScreen.kt: remove private formatResetInSec/formatAmount, import from FormatUtils
- OpenCodeGoWidgetProvider.kt: remove private normalizeWindowLabel, import from FormatUtils
  (formatReset kept — widget uses short d/h/m format, not Chinese)
This commit is contained in:
WaterRain 2026-07-06 23:47:20 +00:00
parent ec5f2dcb5c
commit f2e5eedaa4
No known key found for this signature in database
4 changed files with 53 additions and 44 deletions

View File

@ -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
}

View File

@ -24,6 +24,9 @@ import com.rainy.token.domain.model.ServiceBalance
import com.rainy.token.domain.service.ServiceType import com.rainy.token.domain.service.ServiceType
import com.rainy.token.ui.components.StatusStyle import com.rainy.token.ui.components.StatusStyle
import com.rainy.token.ui.components.StatusLevel 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.StrawberryPink
import com.rainy.token.ui.theme.StatusOrange import com.rainy.token.ui.theme.StatusOrange
import com.rainy.token.ui.theme.inkMuted import com.rainy.token.ui.theme.inkMuted
@ -353,11 +356,6 @@ internal fun OllamaUsageWindows(balance: ServiceBalance) {
// ── Shared UI components ── // ── Shared UI components ──
internal fun normalizeWindowLabel(label: String): String = when (label.lowercase()) {
"weekly" -> "每周"
else -> label
}
@Composable @Composable
internal fun CompactUsageRowEmpty(label: String, resetInSec: Long?) { internal fun CompactUsageRowEmpty(label: String, resetInSec: Long?) {
Column { Column {
@ -452,18 +450,6 @@ internal fun CompactUsageRow(label: String, pct: Int, resetInSec: Long?) {
// ── Dashboard utility functions ── // ── 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 { internal fun DashboardCardUi.statusBadgeStyle(): StatusStyle = when {
credentialState == CredentialStatus.State.NOT_CONFIGURED -> credentialState == CredentialStatus.State.NOT_CONFIGURED ->
StatusStyle("未配置", StatusLevel.WARNING) StatusStyle("未配置", StatusLevel.WARNING)
@ -504,8 +490,3 @@ internal fun footerText(card: DashboardCardUi): String {
else -> "$timeStr 更新" 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)
}

View File

@ -51,6 +51,8 @@ import com.rainy.token.ui.components.ServiceIcon
import com.rainy.token.ui.components.StatusChip import com.rainy.token.ui.components.StatusChip
import com.rainy.token.ui.components.StatusLevel import com.rainy.token.ui.components.StatusLevel
import com.rainy.token.ui.components.StatusStyle 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.inkMuted
import com.rainy.token.ui.theme.StrawberryPink import com.rainy.token.ui.theme.StrawberryPink
import java.text.SimpleDateFormat 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 + 每周用量窗口卡。 * Ollama Pro 专属:5h + 每周用量窗口卡。
*/ */
@ -722,8 +712,3 @@ private fun formatTime(epochMillis: Long): String {
val sdf = SimpleDateFormat("MM-dd HH:mm", Locale.getDefault()) val sdf = SimpleDateFormat("MM-dd HH:mm", Locale.getDefault())
return sdf.format(Date(epochMillis)) 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)
}

View File

@ -14,6 +14,7 @@ import com.rainy.token.R
import com.rainy.token.data.cache.BalanceCache import com.rainy.token.data.cache.BalanceCache
import com.rainy.token.data.cache.balanceCacheDataStore import com.rainy.token.data.cache.balanceCacheDataStore
import com.rainy.token.domain.service.ServiceType import com.rainy.token.domain.service.ServiceType
import com.rainy.token.ui.components.normalizeWindowLabel
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
@ -218,11 +219,6 @@ class OpenCodeGoWidgetProvider : AppWidgetProvider() {
views.setTextViewText(R.id.row3_label, third) views.setTextViewText(R.id.row3_label, third)
} }
private fun normalizeWindowLabel(label: String): String = when (label.lowercase()) {
"weekly" -> "每周"
else -> label
}
private fun populateRow( private fun populateRow(
views: RemoteViews, views: RemoteViews,
pctViewId: Int, pctViewId: Int,