From 27a8d1648708ca2fa2b491a8053aa24fa736a7d9 Mon Sep 17 00:00:00 2001 From: Harsh Shandilya Date: Tue, 5 Apr 2022 23:23:55 +0530 Subject: [PATCH] android: use WorkManager to update saved posts periodically --- android/build.gradle.kts | 3 ++ android/src/main/AndroidManifest.xml | 11 +++++ .../msfjarvis/claw/android/ClawApplication.kt | 15 ++++++- .../msfjarvis/claw/android/MainActivity.kt | 32 +++++++++++++-- .../android/work/SavedPostUpdaterWorker.kt | 40 +++++++++++++++++++ gradle/libs.versions.toml | 13 ++++-- 6 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 android/src/main/kotlin/dev/msfjarvis/claw/android/work/SavedPostUpdaterWorker.kt 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"