v1.11: pace markers + peak-hours burst icon
Add a pace tick to each usage bar showing where you should be to finish at 100% by reset, color-coded by projected tier, plus a Claude burst icon that lights up during Anthropic peak hours (5-11 AM PT, Mon-Fri). Bars now rendered as bitmaps so the same renderer drives both the widget and the app. New: PaceCalc, PeakHours, BarRenderer, ic_claude_burst drawable. versionCode 12 / versionName 1.11. Includes rebuilt signed release APK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -104,15 +104,38 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
if (!loggedIn || data == null) return
|
||||
|
||||
binding.progressBar.progress = data.progressPercent
|
||||
// ── Peak-hours row ───────────────────────────────────────────────────
|
||||
val peak = PeakHours.state()
|
||||
binding.imgPeak.setColorFilter(if (peak.active) PEAK_ON else PEAK_OFF)
|
||||
binding.tvPeak.setTextColor(if (peak.active) PEAK_ON else 0xFFAAAAAA.toInt())
|
||||
binding.tvPeak.text = if (peak.active)
|
||||
"🔥 Peak hours — ${peak.endsInLabel} · ${peak.windowLabel}"
|
||||
else
|
||||
"Off-peak · ${peak.windowLabel}"
|
||||
|
||||
// ── Session (5-hour) bar + pace ──────────────────────────────────────
|
||||
val sessionPct = data.progressPercent
|
||||
val sessionPace = if (data.fiveHourUtilization >= 0f)
|
||||
PaceCalc.compute(data.fiveHourUtilization, data.utilizationResetAtEpoch, PaceCalc.SESSION_WINDOW_MS)
|
||||
else null
|
||||
binding.barSession.setImageBitmap(
|
||||
BarRenderer.render(sessionPct, sessionPace?.markerPct, SESSION_FILL, sessionPace?.tierColor)
|
||||
)
|
||||
binding.tvSessionPace.text = paceSentence(data.fiveHourUtilization, sessionPace)
|
||||
|
||||
// ── Weekly (7-day) bar + pace ────────────────────────────────────────
|
||||
if (data.weeklyUtilization >= 0f) {
|
||||
val wPct = data.weeklyUtilization.toInt()
|
||||
binding.progressBarWeekly.progress = wPct
|
||||
val weeklyPace = PaceCalc.compute(data.weeklyUtilization, data.weeklyResetAtEpoch, PaceCalc.WEEKLY_WINDOW_MS)
|
||||
binding.barWeekly.setImageBitmap(
|
||||
BarRenderer.render(wPct, weeklyPace?.markerPct, WEEKLY_FILL, weeklyPace?.tierColor)
|
||||
)
|
||||
binding.tvWeeklyUsage.text = "$wPct% this week"
|
||||
binding.tvWeeklyPace.text = paceSentence(data.weeklyUtilization, weeklyPace)
|
||||
} else {
|
||||
binding.progressBarWeekly.progress = 0
|
||||
binding.barWeekly.setImageBitmap(BarRenderer.render(0, null, WEEKLY_FILL, null))
|
||||
binding.tvWeeklyUsage.text = "—"
|
||||
binding.tvWeeklyPace.text = ""
|
||||
}
|
||||
|
||||
binding.tvUsage.text = when {
|
||||
@@ -139,6 +162,12 @@ class MainActivity : AppCompatActivity() {
|
||||
binding.tvError.visibility = if (data.errorMessage.isNotBlank()) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
/** "20% over pace · will likely hit limit" — empty when no projection is available yet. */
|
||||
private fun paceSentence(usedPct: Float, pace: PaceCalc.Pace?): String {
|
||||
if (pace == null) return ""
|
||||
return "${PaceCalc.shortTag(usedPct, pace)} · ${pace.label}"
|
||||
}
|
||||
|
||||
private fun formatReset(epochMs: Long): String {
|
||||
if (epochMs <= 0) return ""
|
||||
val now = System.currentTimeMillis()
|
||||
@@ -151,4 +180,11 @@ class MainActivity : AppCompatActivity() {
|
||||
else -> "Resets ${SimpleDateFormat("EEE", Locale.US).format(Date(epochMs))} $timeStr"
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val SESSION_FILL = 0xFFCC785C.toInt()
|
||||
private const val WEEKLY_FILL = 0xFF7B8FCC.toInt()
|
||||
private const val PEAK_ON = 0xFFCC785C.toInt()
|
||||
private const val PEAK_OFF = 0xFF666666.toInt()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user