diff options
Diffstat (limited to 'android/app/src/main/java/org')
4 files changed, 62 insertions, 8 deletions
diff --git a/android/app/src/main/java/org/terst/doot/widget/data/DataStore.kt b/android/app/src/main/java/org/terst/doot/widget/data/DataStore.kt index 164e3a2..d2f2cbb 100644 --- a/android/app/src/main/java/org/terst/doot/widget/data/DataStore.kt +++ b/android/app/src/main/java/org/terst/doot/widget/data/DataStore.kt @@ -3,6 +3,7 @@ package org.terst.doot.widget.data import android.content.Context import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.longPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore @@ -15,4 +16,5 @@ object Keys { val ITEMS_JSON = stringPreferencesKey("items_json") val NOW = stringPreferencesKey("now") val LAST_UPDATED = longPreferencesKey("last_updated") + val IS_REFRESHING = booleanPreferencesKey("is_refreshing") } diff --git a/android/app/src/main/java/org/terst/doot/widget/ui/Actions.kt b/android/app/src/main/java/org/terst/doot/widget/ui/Actions.kt index 27a49e6..3a813fd 100644 --- a/android/app/src/main/java/org/terst/doot/widget/ui/Actions.kt +++ b/android/app/src/main/java/org/terst/doot/widget/ui/Actions.kt @@ -1,10 +1,15 @@ package org.terst.doot.widget.ui import android.content.Context +import androidx.datastore.preferences.core.edit import androidx.glance.GlanceId import androidx.glance.action.ActionParameters import androidx.glance.appwidget.action.ActionCallback +import androidx.glance.appwidget.updateAll +import org.terst.doot.widget.data.Keys +import org.terst.doot.widget.data.dataStore import org.terst.doot.widget.work.CompleteWorker +import org.terst.doot.widget.work.RefreshWorker class CompleteTaskAction : ActionCallback { override suspend fun onAction( @@ -22,3 +27,19 @@ class CompleteTaskAction : ActionCallback { val sourceKey = ActionParameters.Key<String>("item_source") } } + +// Sets IS_REFRESHING synchronously (before the network round trip) so the +// widget's icon flips to the loading state on the spot -- RefreshWorker +// clears the flag when it finishes, regardless of outcome, so the icon +// never gets stuck. +class RefreshTaskAction : ActionCallback { + override suspend fun onAction( + context: Context, + glanceId: GlanceId, + parameters: ActionParameters + ) { + context.dataStore.edit { it[Keys.IS_REFRESHING] = true } + DootWidget().updateAll(context) + RefreshWorker.runOnce(context) + } +} diff --git a/android/app/src/main/java/org/terst/doot/widget/ui/DootWidget.kt b/android/app/src/main/java/org/terst/doot/widget/ui/DootWidget.kt index 7674ddb..7c84168 100644 --- a/android/app/src/main/java/org/terst/doot/widget/ui/DootWidget.kt +++ b/android/app/src/main/java/org/terst/doot/widget/ui/DootWidget.kt @@ -47,9 +47,10 @@ class DootWidget : GlanceAppWidget() { val items = parseItems(prefs) val now = prefs[Keys.NOW]?.let { runCatching { Instant.parse(it) }.getOrNull() } ?: Instant.now() + val isRefreshing = prefs[Keys.IS_REFRESHING] ?: false provideContent { - WidgetRoot(items, now) + WidgetRoot(items, now, isRefreshing) } } @@ -60,7 +61,7 @@ class DootWidget : GlanceAppWidget() { } @Composable -fun WidgetRoot(items: List<WidgetItem>, now: Instant) { +fun WidgetRoot(items: List<WidgetItem>, now: Instant, isRefreshing: Boolean) { val zone = ZoneId.systemDefault() val nowZoned: ZonedDateTime = now.atZone(zone) val todayStart = nowZoned.toLocalDate().atStartOfDay(zone).toInstant() @@ -107,15 +108,20 @@ fun WidgetRoot(items: List<WidgetItem>, now: Instant) { .background(Color.Transparent) .padding(horizontal = 8.dp, vertical = 4.dp) ) { - Row(modifier = GlanceModifier.fillMaxWidth().padding(bottom = 4.dp)) { + Row( + modifier = GlanceModifier.fillMaxWidth().padding(bottom = 4.dp), + verticalAlignment = Alignment.CenterVertically + ) { Text( "TODAY", style = TextStyle( color = ColorProvider(Color(0x66FFFFFF)), fontSize = 11.sp, fontWeight = FontWeight.Bold - ) + ), + modifier = GlanceModifier.defaultWeight() ) + RefreshButton(isRefreshing) } todayAllDay.forEach { AllDayRow(it) } @@ -151,6 +157,26 @@ fun AllDayRow(event: WidgetItem) { } @Composable +fun RefreshButton(isRefreshing: Boolean) { + Box( + modifier = GlanceModifier + .size(24.dp) + .clickable(actionRunCallback<RefreshTaskAction>()), + contentAlignment = Alignment.Center + ) { + Image( + provider = ImageProvider( + if (isRefreshing) org.terst.doot.widget.R.drawable.ic_refresh_loading + else org.terst.doot.widget.R.drawable.ic_refresh + ), + contentDescription = if (isRefreshing) "Refreshing" else "Refresh", + colorFilter = ColorFilter.tint(ColorProvider(Color(0x99FFFFFF))), + modifier = GlanceModifier.size(14.dp) + ) + } +} + +@Composable fun HourRow( hour: Int, nowZoned: ZonedDateTime, diff --git a/android/app/src/main/java/org/terst/doot/widget/work/RefreshWorker.kt b/android/app/src/main/java/org/terst/doot/widget/work/RefreshWorker.kt index 5c0e89c..3bb8bdd 100644 --- a/android/app/src/main/java/org/terst/doot/widget/work/RefreshWorker.kt +++ b/android/app/src/main/java/org/terst/doot/widget/work/RefreshWorker.kt @@ -1,6 +1,7 @@ package org.terst.doot.widget.work import android.content.Context +import androidx.datastore.preferences.core.edit import androidx.glance.appwidget.updateAll import androidx.work.* import kotlinx.coroutines.flow.first @@ -14,16 +15,20 @@ class RefreshWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { override suspend fun doWork(): Result { + val result = performRefresh() + applicationContext.dataStore.edit { it[Keys.IS_REFRESHING] = false } + DootWidget().updateAll(applicationContext) + return result + } + + private suspend fun performRefresh(): Result { val prefs = applicationContext.dataStore.data.first() val url = prefs[Keys.SERVER_URL]?.takeIf { it.isNotBlank() } ?: return Result.failure() val token = prefs[Keys.TOKEN]?.takeIf { it.isNotBlank() } ?: return Result.failure() val repo = WidgetRepository(okhttp3.OkHttpClient(), url, token) return repo.fetchAndPersist(applicationContext).fold( - onSuccess = { - DootWidget().updateAll(applicationContext) - Result.success() - }, + onSuccess = { Result.success() }, onFailure = { Result.retry() } ) } |
