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)
This commit is contained in:
parent
05008ca20f
commit
5a18ffc02f
22
.commit_msg
Normal file
22
.commit_msg
Normal file
@ -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)
|
||||
@ -28,22 +28,44 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- 桌面小组件 -->
|
||||
<!-- 桌面小组件:后台刷新接收器(主进程,需要 Hilt DI) -->
|
||||
<receiver
|
||||
android:name=".ui.widget.WidgetRefreshReceiver"
|
||||
android:exported="false" />
|
||||
|
||||
<!-- OpenCode Go 小组件(MIUI Widget) -->
|
||||
<receiver
|
||||
android:name=".ui.widget.OpenCodeGoWidgetProvider"
|
||||
android:exported="true">
|
||||
android:exported="true"
|
||||
android:label="@string/widget_name">
|
||||
<intent-filter>
|
||||
<!-- 标准 Android 定时刷新 -->
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
<!-- MIUI 曝光刷新(划到负一屏/桌面时触发) -->
|
||||
<action android:name="miui.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/widget_opencode_go_info" />
|
||||
<!-- MIUI Widget 核心标识 -->
|
||||
<meta-data
|
||||
android:name="miuiWidget"
|
||||
android:value="true" />
|
||||
<!-- MIUI 曝光刷新:用户滑动到小组件所在页面时自动触发 -->
|
||||
<meta-data
|
||||
android:name="miuiWidgetRefresh"
|
||||
android:value="exposure" />
|
||||
<!-- 曝光刷新最小间隔 20 秒(官方最短为 10 秒) -->
|
||||
<meta-data
|
||||
android:name="miuiWidgetRefreshMinInterval"
|
||||
android:value="20000" />
|
||||
</receiver>
|
||||
|
||||
<!-- MIUI Widget 版本号(application 级,升级时递增) -->
|
||||
<meta-data
|
||||
android:name="miuiWidgetVersion"
|
||||
android:value="1" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@ -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(
|
||||
|
||||
@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
@ -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) // 暗色下描边
|
||||
|
||||
@ -88,4 +88,14 @@ fun RainyTokenTheme(
|
||||
typography = RainyTypography,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 暗色模式自适应的文本颜色。
|
||||
* 浅色模式下返回暖黑/暖灰,深色模式下自动切换到浅色。
|
||||
*/
|
||||
@Composable
|
||||
fun inkWarm(): Color = if (isSystemInDarkTheme()) DarkInkWarm else InkWarm
|
||||
|
||||
@Composable
|
||||
fun inkMuted(): Color = if (isSystemInDarkTheme()) DarkInkMuted else InkMuted
|
||||
@ -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(
|
||||
|
||||
BIN
app/src/main/res/drawable-night/ic_deepseek_logo.png
Normal file
BIN
app/src/main/res/drawable-night/ic_deepseek_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.4 KiB |
20
app/src/main/res/drawable-night/ic_opencode_go_logo.xml
Normal file
20
app/src/main/res/drawable-night/ic_opencode_go_logo.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- OpenCode Go logo 深色模式版:深色填充 → 浅色,保证在 #2D1825 背景上可见 -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="54dp"
|
||||
android:height="30dp"
|
||||
android:viewportWidth="54"
|
||||
android:viewportHeight="30">
|
||||
<path
|
||||
android:fillColor="#E8D8D5"
|
||||
android:pathData="M24 30H0V0H24V6H6V24H18V18H12V12H24V30Z"/>
|
||||
<path
|
||||
android:fillColor="#A09098"
|
||||
android:pathData="M12 18H18V24H6V12H12V18Z"/>
|
||||
<path
|
||||
android:fillColor="#A09098"
|
||||
android:pathData="M48 12V24H36V12H48Z"/>
|
||||
<path
|
||||
android:fillColor="#E8D8D5"
|
||||
android:pathData="M54 30H30V0H54V30ZM36 24H48V6H36V24Z"/>
|
||||
</vector>
|
||||
BIN
app/src/main/res/drawable-night/rainytoken_wordmark.png
Normal file
BIN
app/src/main/res/drawable-night/rainytoken_wordmark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
10
app/src/main/res/drawable-night/widget_bg.xml
Normal file
10
app/src/main/res/drawable-night/widget_bg.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- MIUI Widget 深色模式背景 -->
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="16dp" />
|
||||
<solid android:color="#2D1825" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#4D2D3D" />
|
||||
</shape>
|
||||
BIN
app/src/main/res/drawable-nodpi/widget_preview.png
Normal file
BIN
app/src/main/res/drawable-nodpi/widget_preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.7 KiB |
212
app/src/main/res/layout-night/widget_opencode_go.xml
Normal file
212
app/src/main/res/layout-night/widget_opencode_go.xml
Normal file
@ -0,0 +1,212 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- MIUI Widget 深色模式布局 — 文字颜色适配暗色背景 #2D1825 -->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/widget_bg">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/widget_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<!-- 第一行:wordmark + 更新时间 + 刷新按钮 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="2dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/widget_wordmark"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/rainytoken_wordmark"
|
||||
android:contentDescription="RainyToken"
|
||||
android:adjustViewBounds="true" />
|
||||
|
||||
<ProgressBar
|
||||
style="@android:style/Widget.ProgressBar.Horizontal"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_weight="1"
|
||||
android:max="100" android:progress="0"
|
||||
android:progressTint="#00000000"
|
||||
android:progressBackgroundTint="#00000000"
|
||||
android:indeterminate="false" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/widget_updated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="--"
|
||||
android:textColor="#D4C4CC"
|
||||
android:textSize="10sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/widget_refresh"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="↻"
|
||||
android:textColor="#FF85A2"
|
||||
android:textSize="20sp"
|
||||
android:layout_marginStart="6dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- OpenCode Go 标识 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="6dp">
|
||||
<ImageView
|
||||
android:id="@+id/widget_logo"
|
||||
android:layout_width="22dp" android:layout_height="12dp"
|
||||
android:src="@drawable/ic_opencode_go_logo"
|
||||
android:contentDescription="OpenCode Go"
|
||||
android:layout_marginEnd="5dp" />
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="OpenCode Go · 用量"
|
||||
android:textColor="#C4A0AB"
|
||||
android:textSize="11sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 5h -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="3dp">
|
||||
<TextView android:id="@+id/row1_label"
|
||||
android:layout_width="36dp" android:layout_height="wrap_content"
|
||||
android:text="5h" android:textColor="#C4A0AB" android:textSize="11sp" />
|
||||
<TextView android:id="@+id/row1_pct"
|
||||
android:layout_width="38dp" android:layout_height="wrap_content"
|
||||
android:text="--" android:textColor="#FFD1DC" android:textSize="12sp"
|
||||
android:textStyle="bold" android:gravity="end" />
|
||||
<ProgressBar android:id="@+id/row1_bar"
|
||||
style="@android:style/Widget.ProgressBar.Horizontal"
|
||||
android:layout_width="0dp" android:layout_height="6dp"
|
||||
android:layout_weight="1" android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp" android:max="100" android:progress="0"
|
||||
android:progressTint="@color/widget_bar_5h" />
|
||||
<TextView android:id="@+id/row1_reset"
|
||||
android:layout_width="48dp" android:layout_height="wrap_content"
|
||||
android:text="--" android:textColor="#8A7A82" android:textSize="10sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 本周 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="3dp">
|
||||
<TextView android:id="@+id/row2_label"
|
||||
android:layout_width="36dp" android:layout_height="wrap_content"
|
||||
android:text="本周" android:textColor="#C4A0AB" android:textSize="11sp" />
|
||||
<TextView android:id="@+id/row2_pct"
|
||||
android:layout_width="38dp" android:layout_height="wrap_content"
|
||||
android:text="--" android:textColor="#FFD1DC" android:textSize="12sp"
|
||||
android:textStyle="bold" android:gravity="end" />
|
||||
<ProgressBar android:id="@+id/row2_bar"
|
||||
style="@android:style/Widget.ProgressBar.Horizontal"
|
||||
android:layout_width="0dp" android:layout_height="6dp"
|
||||
android:layout_weight="1" android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp" android:max="100" android:progress="0"
|
||||
android:progressTint="@color/widget_bar_weekly" />
|
||||
<TextView android:id="@+id/row2_reset"
|
||||
android:layout_width="48dp" android:layout_height="wrap_content"
|
||||
android:text="--" android:textColor="#8A7A82" android:textSize="10sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 本月 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="3dp">
|
||||
<TextView android:id="@+id/row3_label"
|
||||
android:layout_width="36dp" android:layout_height="wrap_content"
|
||||
android:text="本月" android:textColor="#C4A0AB" android:textSize="11sp" />
|
||||
<TextView android:id="@+id/row3_pct"
|
||||
android:layout_width="38dp" android:layout_height="wrap_content"
|
||||
android:text="--" android:textColor="#FFD1DC" android:textSize="12sp"
|
||||
android:textStyle="bold" android:gravity="end" />
|
||||
<ProgressBar android:id="@+id/row3_bar"
|
||||
style="@android:style/Widget.ProgressBar.Horizontal"
|
||||
android:layout_width="0dp" android:layout_height="6dp"
|
||||
android:layout_weight="1" android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp" android:max="100" android:progress="0"
|
||||
android:progressTint="@color/widget_bar_monthly" />
|
||||
<TextView android:id="@+id/row3_reset"
|
||||
android:layout_width="48dp" android:layout_height="wrap_content"
|
||||
android:text="--" android:textColor="#8A7A82" android:textSize="10sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- DeepSeek 余额 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="2dp">
|
||||
<ImageView
|
||||
android:layout_width="wrap_content" android:layout_height="12dp"
|
||||
android:src="@drawable/ic_deepseek_logo"
|
||||
android:contentDescription="DeepSeek"
|
||||
android:adjustViewBounds="true"
|
||||
android:layout_marginEnd="5dp" />
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="DeepSeek 余额"
|
||||
android:textColor="#C4A0AB"
|
||||
android:textSize="11sp" />
|
||||
<TextView
|
||||
android:id="@+id/widget_ds_amount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="--"
|
||||
android:textColor="#FFD1DC"
|
||||
android:textSize="11sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginStart="4dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 署名 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
<ProgressBar
|
||||
style="@android:style/Widget.ProgressBar.Horizontal"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_weight="1"
|
||||
android:max="100" android:progress="0"
|
||||
android:progressTint="#00000000"
|
||||
android:progressBackgroundTint="#00000000"
|
||||
android:indeterminate="false" />
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="雨晴喵"
|
||||
android:textColor="#8A7A82"
|
||||
android:textSize="9sp" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
@ -1,11 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/widget_root"
|
||||
<!-- MIUI Widget 布局规范:根布局必须为 FrameLayout + @android:id/background -->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/widget_bg"
|
||||
android:orientation="vertical"
|
||||
android:padding="12dp">
|
||||
android:background="@drawable/widget_bg">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/widget_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp"
|
||||
android:gravity="center_vertical">
|
||||
<!-- 注意:保持和原来一样的内部结构,只是套了一层 FrameLayout -->
|
||||
|
||||
<!-- 第一行 -->
|
||||
<LinearLayout
|
||||
@ -59,7 +67,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="10dp">
|
||||
android:layout_marginBottom="6dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/widget_logo"
|
||||
@ -83,7 +91,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="6dp">
|
||||
android:layout_marginBottom="3dp">
|
||||
|
||||
<TextView android:id="@+id/row1_label"
|
||||
android:layout_width="36dp" android:layout_height="wrap_content"
|
||||
@ -112,7 +120,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="6dp">
|
||||
android:layout_marginBottom="3dp">
|
||||
|
||||
<TextView android:id="@+id/row2_label"
|
||||
android:layout_width="36dp" android:layout_height="wrap_content"
|
||||
@ -141,7 +149,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="8dp">
|
||||
android:layout_marginBottom="3dp">
|
||||
|
||||
<TextView android:id="@+id/row3_label"
|
||||
android:layout_width="36dp" android:layout_height="wrap_content"
|
||||
@ -170,7 +178,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="4dp">
|
||||
android:layout_marginBottom="2dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
@ -224,4 +232,6 @@
|
||||
android:textSize="9sp" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
180
app/src/main/res/layout/widget_opencode_go_preview.xml
Normal file
180
app/src/main/res/layout/widget_opencode_go_preview.xml
Normal file
@ -0,0 +1,180 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 小组件预览布局 — 小米小部件中心选择器展示用 -->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/widget_bg">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="12dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<!-- 标题行 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="2dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/rainytoken_wordmark"
|
||||
android:adjustViewBounds="true" />
|
||||
|
||||
<ProgressBar
|
||||
style="@android:style/Widget.ProgressBar.Horizontal"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_weight="1"
|
||||
android:max="100"
|
||||
android:progress="0"
|
||||
android:progressTint="#00000000"
|
||||
android:progressBackgroundTint="#00000000"
|
||||
android:indeterminate="false" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="自动刷新"
|
||||
android:textColor="#C4A0AB"
|
||||
android:textSize="10sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- OpenCode Go 标识 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="10dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="22dp"
|
||||
android:layout_height="12dp"
|
||||
android:src="@drawable/ic_opencode_go_logo"
|
||||
android:layout_marginEnd="5dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="OpenCode Go · 用量"
|
||||
android:textColor="#8A7A82"
|
||||
android:textSize="11sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 5h -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="6dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="36dp" android:layout_height="wrap_content"
|
||||
android:text="5h" android:textColor="#8A7A82" android:textSize="11sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="38dp" android:layout_height="wrap_content"
|
||||
android:text="42%" android:textColor="#3D2C35" android:textSize="12sp"
|
||||
android:textStyle="bold" android:gravity="end" />
|
||||
|
||||
<ProgressBar
|
||||
style="@android:style/Widget.ProgressBar.Horizontal"
|
||||
android:layout_width="0dp" android:layout_height="6dp"
|
||||
android:layout_weight="1" android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp" android:max="100" android:progress="42"
|
||||
android:progressTint="#FF85A2" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 本周 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="6dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="36dp" android:layout_height="wrap_content"
|
||||
android:text="本周" android:textColor="#8A7A82" android:textSize="11sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="38dp" android:layout_height="wrap_content"
|
||||
android:text="68%" android:textColor="#3D2C35" android:textSize="12sp"
|
||||
android:textStyle="bold" android:gravity="end" />
|
||||
|
||||
<ProgressBar
|
||||
style="@android:style/Widget.ProgressBar.Horizontal"
|
||||
android:layout_width="0dp" android:layout_height="6dp"
|
||||
android:layout_weight="1" android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp" android:max="100" android:progress="68"
|
||||
android:progressTint="#FFA726" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 本月 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="8dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="36dp" android:layout_height="wrap_content"
|
||||
android:text="本月" android:textColor="#8A7A82" android:textSize="11sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="38dp" android:layout_height="wrap_content"
|
||||
android:text="35%" android:textColor="#3D2C35" android:textSize="12sp"
|
||||
android:textStyle="bold" android:gravity="end" />
|
||||
|
||||
<ProgressBar
|
||||
style="@android:style/Widget.ProgressBar.Horizontal"
|
||||
android:layout_width="0dp" android:layout_height="6dp"
|
||||
android:layout_weight="1" android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp" android:max="100" android:progress="35"
|
||||
android:progressTint="#FF85A2" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- DeepSeek 余额 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="4dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="12dp"
|
||||
android:src="@drawable/ic_deepseek_logo"
|
||||
android:adjustViewBounds="true"
|
||||
android:layout_marginEnd="5dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="DeepSeek 余额"
|
||||
android:textColor="#8A7A82"
|
||||
android:textSize="11sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="¥18.00"
|
||||
android:textColor="#3D2C35"
|
||||
android:textSize="11sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginStart="4dp" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
@ -1,3 +1,5 @@
|
||||
<resources>
|
||||
<string name="app_name">雨晴Token</string>
|
||||
<string name="widget_name">AI用量面板</string>
|
||||
<string name="widget_description">OpenCode Go 用量 + DeepSeek 余额,自动刷新</string>
|
||||
</resources>
|
||||
@ -1,9 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:minWidth="250dp"
|
||||
android:minWidth="300dp"
|
||||
android:minHeight="110dp"
|
||||
android:initialLayout="@layout/widget_opencode_go"
|
||||
android:previewImage="@mipmap/ic_launcher"
|
||||
android:previewImage="@drawable/widget_preview"
|
||||
android:previewLayout="@layout/widget_opencode_go_preview"
|
||||
android:description="@string/widget_description"
|
||||
android:resizeMode="vertical|horizontal"
|
||||
android:widgetCategory="home_screen"
|
||||
android:widgetCategory="home_screen|keyguard"
|
||||
android:widgetFeatures="reconfigurable"
|
||||
android:updatePeriodMillis="1800000" />
|
||||
|
||||
Loading…
Reference in New Issue
Block a user