Compare commits

..

3 Commits

3 changed files with 44 additions and 10 deletions
+2 -2
View File
@@ -11,8 +11,8 @@ android {
applicationId = "me.khodak.claudeusage" applicationId = "me.khodak.claudeusage"
minSdk = 26 minSdk = 26
targetSdk = 34 targetSdk = 34
versionCode = 2 versionCode = 4
versionName = "1.1" versionName = "1.3"
} }
signingConfigs { signingConfigs {
@@ -39,6 +39,7 @@ class ClaudeUsageWidget : AppWidgetProvider() {
companion object { companion object {
const val ACTION_REFRESH = "me.khodak.claudeusage.ACTION_REFRESH" const val ACTION_REFRESH = "me.khodak.claudeusage.ACTION_REFRESH"
@Volatile internal var isRefreshing = false @Volatile internal var isRefreshing = false
@Volatile internal var currentRotation = 0f
fun updateWidget(context: Context, manager: AppWidgetManager, widgetId: Int) { fun updateWidget(context: Context, manager: AppWidgetManager, widgetId: Int) {
val prefs = PreferencesManager(context) val prefs = PreferencesManager(context)
@@ -149,6 +150,7 @@ class ClaudeUsageWidget : AppWidgetProvider() {
if (status.isNotBlank()) status else if (updatedMs > 0) formatTime(updatedMs) else "") if (status.isNotBlank()) status else if (updatedMs > 0) formatTime(updatedMs) else "")
v.setInt(R.id.btn_refresh, "setColorFilter", v.setInt(R.id.btn_refresh, "setColorFilter",
if (isRefreshing) 0xFFCC785C.toInt() else 0xFF999999.toInt()) if (isRefreshing) 0xFFCC785C.toInt() else 0xFF999999.toInt())
v.setFloat(R.id.btn_refresh, "setRotation", currentRotation)
return v return v
} }
@@ -230,6 +232,7 @@ class ClaudeUsageWidget : AppWidgetProvider() {
) )
v.setInt(R.id.btn_refresh, "setColorFilter", v.setInt(R.id.btn_refresh, "setColorFilter",
if (isRefreshing) 0xFFCC785C.toInt() else 0xFF999999.toInt()) if (isRefreshing) 0xFFCC785C.toInt() else 0xFF999999.toInt())
v.setFloat(R.id.btn_refresh, "setRotation", currentRotation)
return v return v
} }
@@ -8,8 +8,12 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.SystemClock import android.os.SystemClock
import androidx.work.* import androidx.work.*
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.khodak.claudeusage.data.PreferencesManager import me.khodak.claudeusage.data.PreferencesManager
import java.util.concurrent.TimeUnit
class UsageUpdateWorker( class UsageUpdateWorker(
private val context: Context, private val context: Context,
@@ -22,21 +26,48 @@ class UsageUpdateWorker(
prefs.markTodayActive() prefs.markTodayActive()
// Try API — save result (even on failure, local data is preserved) coroutineScope {
val animJob = launch { rotateRefreshIcon() }
try { try {
val data = UsageRepository(prefs).fetchUsage() val data = UsageRepository(prefs).fetchUsage()
prefs.saveUsageData(data) prefs.saveUsageData(data)
} catch (_: Exception) { } catch (_: Exception) {}
// API failed — don't save, keep existing cached data animJob.cancel()
animJob.join() // wait for the minimum-rotation finally block to finish
} }
// Always push widget update using latest prefs + cached api data
pushWidgetUpdate() pushWidgetUpdate()
return Result.success() return Result.success()
} }
private suspend fun rotateRefreshIcon() {
val manager = AppWidgetManager.getInstance(context)
val ids = manager.getAppWidgetIds(ComponentName(context, ClaudeUsageWidget::class.java))
var totalDegrees = 0f
try {
while (true) {
ClaudeUsageWidget.currentRotation = (ClaudeUsageWidget.currentRotation + 6f) % 360f
totalDegrees += 6f
ids.forEach { id -> ClaudeUsageWidget.updateWidget(context, manager, id) }
delay(33) // 30 fps, one full rotation per second
}
} finally {
// Even if the fetch finishes early, complete at least one full 360°
withContext(NonCancellable) {
while (totalDegrees < 360f) {
ClaudeUsageWidget.currentRotation = (ClaudeUsageWidget.currentRotation + 6f) % 360f
totalDegrees += 6f
ids.forEach { id -> ClaudeUsageWidget.updateWidget(context, manager, id) }
delay(33)
}
}
}
}
private fun pushWidgetUpdate() { private fun pushWidgetUpdate() {
ClaudeUsageWidget.isRefreshing = false ClaudeUsageWidget.isRefreshing = false
ClaudeUsageWidget.currentRotation = 0f
val manager = AppWidgetManager.getInstance(context) val manager = AppWidgetManager.getInstance(context)
val ids = manager.getAppWidgetIds(ComponentName(context, ClaudeUsageWidget::class.java)) val ids = manager.getAppWidgetIds(ComponentName(context, ClaudeUsageWidget::class.java))
ids.forEach { id -> ClaudeUsageWidget.updateWidget(context, manager, id) } ids.forEach { id -> ClaudeUsageWidget.updateWidget(context, manager, id) }