Skip to content

Commit

Permalink
feat(widget): using work to refresh widget
Browse files Browse the repository at this point in the history
  • Loading branch information
I-Info committed Oct 25, 2023
1 parent 410107b commit 8837eaf
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 4 deletions.
8 changes: 8 additions & 0 deletions app/src/main/kotlin/com/zjutjh/ijh/widget/ScheduleWidget.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.zjutjh.ijh.widget

import android.content.Context
import android.util.Log
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
Expand All @@ -20,6 +21,7 @@ import androidx.glance.appwidget.cornerRadius
import androidx.glance.appwidget.lazy.LazyColumn
import androidx.glance.appwidget.lazy.items
import androidx.glance.appwidget.provideContent
import androidx.glance.appwidget.updateAll
import androidx.glance.background
import androidx.glance.layout.Alignment
import androidx.glance.layout.Box
Expand Down Expand Up @@ -51,13 +53,16 @@ import java.time.format.DateTimeFormatter
class ScheduleWidget : GlanceAppWidget() {
@OptIn(ExperimentalCoroutinesApi::class)
override suspend fun provideGlance(context: Context, id: GlanceId) {
Log.d("ScheduleWidget", "provideGlance called.")

val entryPoint =
EntryPointAccessors.fromApplication<ScheduleWidgetReceiver.Repositories>(context)

val dayStateFlow = entryPoint.weJhInfoRepository.infoStream
.map { it?.toTermDayState() }
val coursesFlow = dayStateFlow.flatMapLatest {
it ?: return@flatMapLatest flowOf(null)

entryPoint.courseRepository
.getCourses(it.year, it.term, it.week, it.dayOfWeek)
}
Expand All @@ -68,6 +73,9 @@ class ScheduleWidget : GlanceAppWidget() {
DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(it.withZoneSameInstant(ZoneId.systemDefault()))
}

// Force update, or the widget will not show data when it is first created
updateAll(context)

provideContent {
val termDay by dayStateFlow.collectAsState(initial = null)
val courses by coursesFlow.collectAsState(initial = null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import androidx.work.WorkManager
import com.zjutjh.ijh.data.repository.CourseRepository
import com.zjutjh.ijh.data.repository.WeJhInfoRepository
import com.zjutjh.ijh.work.ScheduleWidgetUpdater
import com.zjutjh.ijh.work.WidgetRefreshWorker
import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
Expand Down Expand Up @@ -46,6 +47,8 @@ class ScheduleWidgetReceiver : GlanceAppWidgetReceiver() {
request
)

WidgetRefreshWorker.enqueue<ScheduleWidget>(context)

Log.i("ScheduleWidget", "Updater enqueued.")
}

Expand All @@ -54,6 +57,9 @@ class ScheduleWidgetReceiver : GlanceAppWidgetReceiver() {
// Enter relevant functionality for when the last widget is disabled
val manager = WorkManager.getInstance(context)
manager.cancelUniqueWork(ScheduleWidgetUpdater.PERIODIC_UNIQUE_NAME)

WidgetRefreshWorker.cancel<ScheduleWidget>(context)

Log.i("ScheduleWidget", "Updater canceled.")
}
}
66 changes: 63 additions & 3 deletions app/src/main/kotlin/com/zjutjh/ijh/work/WidgetRefreshWorker.kt
Original file line number Diff line number Diff line change
@@ -1,20 +1,80 @@
package com.zjutjh.ijh.work

import android.content.Context
import android.util.Log
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.updateAll
import androidx.work.Constraints
import androidx.work.CoroutineWorker
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import java.time.Duration

/**
* This worker is used to refresh the widget periodically.
*/
class WidgetRefreshWorker(
private val context: Context,
workerParameters: WorkerParameters
) : CoroutineWorker(context, workerParameters) {

override suspend fun doWork(): Result =
try {
// TODO
companion object {
const val INPUT_CLASS_NAME = "className"

inline fun <reified T : GlanceAppWidget> getUniqueWorkName() =
WidgetRefreshWorker::class.java.simpleName + "_" + T::class.java.simpleName

/**
* Enqueue a [WidgetRefreshWorker] for the given [GlanceAppWidget].
* Do nothing if the worker is already enqueued.
* Default refresh interval is 20 minutes.
*/
inline fun <reified T : GlanceAppWidget> enqueue(context: Context) {
val manager = WorkManager.getInstance(context)
val request = PeriodicWorkRequestBuilder<WidgetRefreshWorker>(
Duration.ofMinutes(20), Duration.ofMinutes(5)
).setConstraints(
Constraints(requiresBatteryNotLow = true, requiresDeviceIdle = true)
).setInputData(
workDataOf(INPUT_CLASS_NAME to T::class.java.name)
).build()

manager.enqueueUniquePeriodicWork(
getUniqueWorkName<T>(),
ExistingPeriodicWorkPolicy.KEEP,
request
)
}

inline fun <reified T : GlanceAppWidget> cancel(context: Context) {
val manager = WorkManager.getInstance(context)
manager.cancelUniqueWork(getUniqueWorkName<T>())
}
}

/**
* Using reflection to get the class name of the widget, then update it.
*/
override suspend fun doWork(): Result {
return try {
val className = inputData.getString(INPUT_CLASS_NAME) ?: return Result.failure()
val clazz = Class.forName(className)

// check if clazz is a subclass of GlanceAppWidget
if (!GlanceAppWidget::class.java.isAssignableFrom(clazz)) return Result.failure()

val obj = clazz.getConstructor().newInstance() as GlanceAppWidget

obj.updateAll(context)

Result.success()
} catch (e: Exception) {
Log.e("WidgetRefreshWorker", "Error when refreshing widget.", e)
Result.failure()
}
}

}
2 changes: 1 addition & 1 deletion app/src/main/res/xml/app_widget_schedule.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
android:resizeMode="horizontal|vertical"
android:targetCellWidth="3"
android:targetCellHeight="3"
android:updatePeriodMillis="1800000"
android:updatePeriodMillis="0"
android:widgetCategory="home_screen"
tools:targetApi="s" />

0 comments on commit 8837eaf

Please sign in to comment.