diff --git a/android/build.gradle.kts b/android/build.gradle.kts
index 9321a515..f28c58b4 100644
--- a/android/build.gradle.kts
+++ b/android/build.gradle.kts
@@ -20,6 +20,7 @@ android {
dependencies {
kapt(libs.dagger.hilt.compiler)
+ kapt(libs.androidx.hilt.compiler)
implementation(projects.api)
implementation(projects.common)
implementation(projects.database)
@@ -30,6 +31,7 @@ dependencies {
implementation(libs.androidx.appcompat)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.core.splashscreen)
+ implementation(libs.androidx.hilt.work)
implementation(libs.androidx.lifecycle.compose)
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.paging.compose)
@@ -39,4 +41,5 @@ dependencies {
implementation(libs.kotlin.coroutines.core)
implementation(libs.kotlinx.serialization.json)
implementation(libs.retrofit.kotlinxSerializationConverter) { isTransitive = false }
+ implementation(libs.androidx.work.runtime.ktx)
}
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
index 387aef8d..a9953ce5 100644
--- a/android/src/main/AndroidManifest.xml
+++ b/android/src/main/AndroidManifest.xml
@@ -1,5 +1,6 @@
@@ -56,5 +57,15 @@
android:pathPattern="/s/....../...*"/>
+
+
+
diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/ClawApplication.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/ClawApplication.kt
index b30d1319..6038eac6 100644
--- a/android/src/main/kotlin/dev/msfjarvis/claw/android/ClawApplication.kt
+++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/ClawApplication.kt
@@ -2,10 +2,16 @@ package dev.msfjarvis.claw.android
import android.app.Application
import android.os.StrictMode
+import android.util.Log
+import androidx.hilt.work.HiltWorkerFactory
+import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
+import javax.inject.Inject
@HiltAndroidApp
-class ClawApplication : Application() {
+class ClawApplication : Application(), Configuration.Provider {
+ @Inject lateinit var workerFactory: HiltWorkerFactory
+
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
@@ -13,4 +19,11 @@ class ClawApplication : Application() {
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build())
}
}
+
+ override fun getWorkManagerConfiguration(): Configuration {
+ return Configuration.Builder()
+ .setMinimumLoggingLevel(Log.DEBUG)
+ .setWorkerFactory(workerFactory)
+ .build()
+ }
}
diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/MainActivity.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/MainActivity.kt
index 508f7278..25ac08c2 100644
--- a/android/src/main/kotlin/dev/msfjarvis/claw/android/MainActivity.kt
+++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/MainActivity.kt
@@ -6,10 +6,18 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
+import androidx.lifecycle.lifecycleScope
+import androidx.work.Constraints
+import androidx.work.ExistingPeriodicWorkPolicy
+import androidx.work.NetworkType
+import androidx.work.PeriodicWorkRequestBuilder
+import androidx.work.WorkManager
import dagger.hilt.android.AndroidEntryPoint
import dev.msfjarvis.claw.android.ui.LobstersApp
+import dev.msfjarvis.claw.android.work.SavedPostUpdaterWorker
import dev.msfjarvis.claw.common.comments.HTMLConverter
import dev.msfjarvis.claw.common.urllauncher.UrlLauncher
+import java.util.concurrent.TimeUnit
import javax.inject.Inject
@AndroidEntryPoint
@@ -23,10 +31,26 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState)
installSplashScreen()
setContent {
- LobstersApp(
- urlLauncher = urlLauncher,
- htmlConverter = htmlConverter,
- ) { url -> webUri = url }
+ LobstersApp(urlLauncher = urlLauncher, htmlConverter = htmlConverter) { url -> webUri = url }
+ }
+ lifecycleScope.launchWhenCreated {
+ val postUpdateWorkRequest =
+ PeriodicWorkRequestBuilder(24, TimeUnit.HOURS)
+ .setConstraints(
+ Constraints.Builder()
+ .setRequiresCharging(false)
+ .setRequiresBatteryNotLow(true)
+ .setRequiredNetworkType(NetworkType.CONNECTED)
+ .setRequiresDeviceIdle(true)
+ .build()
+ )
+ .build()
+ WorkManager.getInstance(this@MainActivity)
+ .enqueueUniquePeriodicWork(
+ "updateSavedPosts",
+ ExistingPeriodicWorkPolicy.KEEP,
+ postUpdateWorkRequest,
+ )
}
}
diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/work/SavedPostUpdaterWorker.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/work/SavedPostUpdaterWorker.kt
new file mode 100644
index 00000000..458f2c18
--- /dev/null
+++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/work/SavedPostUpdaterWorker.kt
@@ -0,0 +1,40 @@
+package dev.msfjarvis.claw.android.work
+
+import android.content.Context
+import androidx.hilt.work.HiltWorker
+import androidx.work.CoroutineWorker
+import androidx.work.WorkerParameters
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dev.msfjarvis.claw.android.viewmodel.SavedPostsRepository
+import dev.msfjarvis.claw.api.LobstersApi
+import dev.msfjarvis.claw.common.posts.toDbModel
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.first
+
+/**
+ * WorkManager-backed [CoroutineWorker] that gets all the posts from [SavedPostsRepository], fetches
+ * their newest state using the [LobstersApi] and then writes them back to the database. This allows
+ * saved posts that were saved before comment counts were added to be able to show a comment count
+ * and for new-enough posts that are still getting comments to have an accurate one.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@HiltWorker
+class SavedPostUpdaterWorker
+@AssistedInject
+constructor(
+ private val savedPostsRepository: SavedPostsRepository,
+ private val lobstersApi: LobstersApi,
+ @Assisted appContext: Context,
+ @Assisted workerParams: WorkerParameters,
+) : CoroutineWorker(appContext, workerParams) {
+ override suspend fun doWork(): Result {
+ savedPostsRepository
+ .savedPosts
+ .first()
+ .mapNotNull { post -> runCatching { lobstersApi.getPostDetails(post.shortId) }.getOrNull() }
+ .map { postDetails -> postDetails.toDbModel() }
+ .map { post -> savedPostsRepository.savePost(post) }
+ return Result.success()
+ }
+}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 68c73bb1..e26273a7 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -2,11 +2,13 @@
accompanist = "0.24.5-alpha"
aurora = "1.1.0"
coroutines = "1.6.1"
-hilt = "2.41"
+dagger = "2.41"
+hilt = "1.0.0"
kotlin = "1.6.10"
richtext = "0.11.0"
serialization = "1.3.2"
sqldelight = "1.5.3"
+workmanager = "2.8.0-alpha01"
[libraries]
accompanist-swiperefresh = { module = "com.google.accompanist:accompanist-swiperefresh", version.ref = "accompanist" }
@@ -16,9 +18,12 @@ androidx-appcompat = "androidx.appcompat:appcompat:1.6.0-alpha01"
androidx-browser = "androidx.browser:browser:1.4.0"
androidx-core-ktx = "androidx.core:core-ktx:1.9.0-alpha02"
androidx-core-splashscreen = "androidx.core:core-splashscreen:1.0.0-beta02"
+androidx-hilt-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "hilt" }
+androidx-hilt-work = { module = "androidx.hilt:hilt-work", version.ref = "hilt" }
androidx-lifecycle-compose = "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.0-alpha05"
androidx-navigation-compose = "androidx.navigation:navigation-compose:2.5.0-alpha03"
androidx-paging-compose = "androidx.paging:paging-compose:1.0.0-alpha14"
+androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workmanager" }
aurora-component = { module = "org.pushing-pixels:aurora-component", version.ref = "aurora" }
aurora-theming = { module = "org.pushing-pixels:aurora-theming", version.ref = "aurora" }
aurora-window = { module = "org.pushing-pixels:aurora-window", version.ref = "aurora" }
@@ -33,8 +38,8 @@ compose-richtext-markdown = { module = "com.halilibo.compose-richtext:richtext-c
compose-richtext-material = { module = "com.halilibo.compose-richtext:richtext-ui-material", version.ref = "richtext" }
compose-richtext-ui = { module = "com.halilibo.compose-richtext:richtext-ui", version.ref = "richtext" }
copydown = "io.github.furstenheim:copy_down:1.0"
-dagger-hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" }
-dagger-hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" }
+dagger-hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "dagger" }
+dagger-hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "dagger" }
javapoet = "com.squareup:javapoet:1.13.0"
kamel-image = "com.alialbaali.kamel:kamel-image:0.3.0"
kotlin-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
@@ -51,5 +56,5 @@ testing-mockWebServer = "com.squareup.okhttp3:mockwebserver3-junit4:5.0.0-alpha.
[plugins]
compose = "org.jetbrains.compose:1.1.1"
-hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
+hilt = { id = "com.google.dagger.hilt.android", version.ref = "dagger" }
sqldelight = "com.squareup.sqldelight:1.5.3"