diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a6f1604..422ba6f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -11,8 +11,8 @@ android { applicationId = "me.khodak.claudeusage" minSdk = 26 targetSdk = 34 - versionCode = 2 - versionName = "1.1" + versionCode = 3 + versionName = "1.2" } signingConfigs { diff --git a/app/src/main/java/me/khodak/claudeusage/ClaudeUsageWidget.kt b/app/src/main/java/me/khodak/claudeusage/ClaudeUsageWidget.kt index fca8017..2d41d58 100644 --- a/app/src/main/java/me/khodak/claudeusage/ClaudeUsageWidget.kt +++ b/app/src/main/java/me/khodak/claudeusage/ClaudeUsageWidget.kt @@ -149,6 +149,7 @@ class ClaudeUsageWidget : AppWidgetProvider() { if (status.isNotBlank()) status else if (updatedMs > 0) formatTime(updatedMs) else "") v.setInt(R.id.btn_refresh, "setColorFilter", if (isRefreshing) 0xFFCC785C.toInt() else 0xFF999999.toInt()) + v.setFloat(R.id.btn_refresh, "setRotation", 0f) return v } @@ -230,6 +231,7 @@ class ClaudeUsageWidget : AppWidgetProvider() { ) v.setInt(R.id.btn_refresh, "setColorFilter", if (isRefreshing) 0xFFCC785C.toInt() else 0xFF999999.toInt()) + v.setFloat(R.id.btn_refresh, "setRotation", 0f) return v } diff --git a/app/src/main/java/me/khodak/claudeusage/UsageUpdateWorker.kt b/app/src/main/java/me/khodak/claudeusage/UsageUpdateWorker.kt index aa82096..2961858 100644 --- a/app/src/main/java/me/khodak/claudeusage/UsageUpdateWorker.kt +++ b/app/src/main/java/me/khodak/claudeusage/UsageUpdateWorker.kt @@ -6,8 +6,14 @@ import android.appwidget.AppWidgetManager import android.content.ComponentName import android.content.Context import android.content.Intent +import android.os.Build import android.os.SystemClock +import android.util.SizeF +import android.widget.RemoteViews import androidx.work.* +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import me.khodak.claudeusage.data.PreferencesManager import java.util.concurrent.TimeUnit @@ -22,19 +28,44 @@ class UsageUpdateWorker( prefs.markTodayActive() - // Try API — save result (even on failure, local data is preserved) - try { - val data = UsageRepository(prefs).fetchUsage() - prefs.saveUsageData(data) - } catch (_: Exception) { - // API failed — don't save, keep existing cached data + coroutineScope { + val animJob = launch { rotateRefreshIcon() } + try { + val data = UsageRepository(prefs).fetchUsage() + prefs.saveUsageData(data) + } catch (_: Exception) {} + animJob.cancel() } - // Always push widget update using latest prefs + cached api data pushWidgetUpdate() return Result.success() } + private suspend fun rotateRefreshIcon() { + val manager = AppWidgetManager.getInstance(context) + val ids = manager.getAppWidgetIds(ComponentName(context, ClaudeUsageWidget::class.java)) + var angle = 0f + while (true) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val small = RemoteViews(context.packageName, R.layout.widget_layout_small) + small.setFloat(R.id.btn_refresh, "setRotation", angle) + val large = RemoteViews(context.packageName, R.layout.widget_layout) + large.setFloat(R.id.btn_refresh, "setRotation", angle) + val rv = RemoteViews(mapOf( + SizeF(50f, 50f) to small, + SizeF(50f, 120f) to large + )) + ids.forEach { id -> manager.partiallyUpdateAppWidget(id, rv) } + } else { + val rv = RemoteViews(context.packageName, R.layout.widget_layout) + rv.setFloat(R.id.btn_refresh, "setRotation", angle) + ids.forEach { id -> manager.partiallyUpdateAppWidget(id, rv) } + } + angle = (angle + 30f) % 360f + delay(83) // ~12 fps, one full rotation per second + } + } + private fun pushWidgetUpdate() { ClaudeUsageWidget.isRefreshing = false val manager = AppWidgetManager.getInstance(context) @@ -57,8 +88,7 @@ class UsageUpdateWorker( } fun cancelPeriodicRefresh(context: Context) = - (context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) - .cancel(alarmIntent(context)) + (context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) .cancel(alarmIntent(context)) fun triggerImmediateRefresh(context: Context) { WorkManager.getInstance(context).enqueueUniqueWork(