refactor: Replace custom worker factory with HiltWorkerFactory (#517)
Removes the need for a separate `WorkerModule` and factory methods in each worker.
This commit is contained in:
parent
442f3bc80c
commit
4762411147
|
@ -116,6 +116,11 @@ dependencies {
|
||||||
compileOnly(libs.bundles.room)
|
compileOnly(libs.bundles.room)
|
||||||
testCompileOnly(libs.bundles.room)
|
testCompileOnly(libs.bundles.room)
|
||||||
|
|
||||||
|
// @HiltWorker annotation
|
||||||
|
implementation(libs.androidx.hilt.common)
|
||||||
|
implementation(libs.androidx.hilt.work)
|
||||||
|
ksp(libs.androidx.hilt.compiler)
|
||||||
|
|
||||||
implementation(projects.core.accounts)
|
implementation(projects.core.accounts)
|
||||||
implementation(projects.core.activity)
|
implementation(projects.core.activity)
|
||||||
implementation(projects.core.common)
|
implementation(projects.core.common)
|
||||||
|
|
|
@ -212,17 +212,11 @@
|
||||||
android:resource="@xml/file_paths" />
|
android:resource="@xml/file_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
<!-- disable automatic WorkManager initialization -->
|
<!-- Disable automatic WorkManager initialization, use HiltWorkerFactory -->
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.startup.InitializationProvider"
|
android:name="androidx.startup.InitializationProvider"
|
||||||
android:authorities="${applicationId}.androidx-startup"
|
android:authorities="${applicationId}.androidx-startup"
|
||||||
android:exported="false"
|
tools:node="remove" />
|
||||||
tools:node="merge">
|
|
||||||
<meta-data
|
|
||||||
android:name="androidx.work.WorkManagerInitializer"
|
|
||||||
android:value="androidx.startup"
|
|
||||||
tools:node="remove" />
|
|
||||||
</provider>
|
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ package app.pachli
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
|
import androidx.hilt.work.HiltWorkerFactory
|
||||||
import androidx.work.Constraints
|
import androidx.work.Constraints
|
||||||
import androidx.work.ExistingPeriodicWorkPolicy
|
import androidx.work.ExistingPeriodicWorkPolicy
|
||||||
import androidx.work.PeriodicWorkRequestBuilder
|
import androidx.work.PeriodicWorkRequestBuilder
|
||||||
|
@ -37,7 +38,6 @@ import app.pachli.util.LocaleManager
|
||||||
import app.pachli.util.setAppNightMode
|
import app.pachli.util.setAppNightMode
|
||||||
import app.pachli.worker.PruneCacheWorker
|
import app.pachli.worker.PruneCacheWorker
|
||||||
import app.pachli.worker.PruneLogEntryEntityWorker
|
import app.pachli.worker.PruneLogEntryEntityWorker
|
||||||
import app.pachli.worker.WorkerFactory
|
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
import de.c1710.filemojicompat_defaults.DefaultEmojiPackList
|
import de.c1710.filemojicompat_defaults.DefaultEmojiPackList
|
||||||
import de.c1710.filemojicompat_ui.helpers.EmojiPackHelper
|
import de.c1710.filemojicompat_ui.helpers.EmojiPackHelper
|
||||||
|
@ -51,7 +51,7 @@ import timber.log.Timber
|
||||||
@HiltAndroidApp
|
@HiltAndroidApp
|
||||||
class PachliApplication : Application() {
|
class PachliApplication : Application() {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var workerFactory: WorkerFactory
|
lateinit var workerFactory: HiltWorkerFactory
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var localeManager: LocaleManager
|
lateinit var localeManager: LocaleManager
|
||||||
|
@ -110,9 +110,7 @@ class PachliApplication : Application() {
|
||||||
|
|
||||||
WorkManager.initialize(
|
WorkManager.initialize(
|
||||||
this,
|
this,
|
||||||
androidx.work.Configuration.Builder()
|
androidx.work.Configuration.Builder().setWorkerFactory(workerFactory).build(),
|
||||||
.setWorkerFactory(workerFactory)
|
|
||||||
.build(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val workManager = WorkManager.getInstance(this)
|
val workManager = WorkManager.getInstance(this)
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023 Pachli Association
|
|
||||||
*
|
|
||||||
* This file is a part of Pachli.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
|
||||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
|
||||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
||||||
* Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along with Pachli; if not,
|
|
||||||
* see <http://www.gnu.org/licenses>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package app.pachli.di
|
|
||||||
|
|
||||||
import androidx.work.ListenableWorker
|
|
||||||
import app.pachli.worker.ChildWorkerFactory
|
|
||||||
import app.pachli.worker.NotificationWorker
|
|
||||||
import app.pachli.worker.PruneCacheWorker
|
|
||||||
import dagger.Binds
|
|
||||||
import dagger.MapKey
|
|
||||||
import dagger.Module
|
|
||||||
import dagger.hilt.InstallIn
|
|
||||||
import dagger.hilt.components.SingletonComponent
|
|
||||||
import dagger.multibindings.IntoMap
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
@MapKey
|
|
||||||
annotation class WorkerKey(val value: KClass<out ListenableWorker>)
|
|
||||||
|
|
||||||
@InstallIn(SingletonComponent::class)
|
|
||||||
@Module
|
|
||||||
abstract class WorkerModule {
|
|
||||||
@Binds
|
|
||||||
@IntoMap
|
|
||||||
@WorkerKey(NotificationWorker::class)
|
|
||||||
internal abstract fun bindNotificationWorkerFactory(worker: NotificationWorker.Factory): ChildWorkerFactory
|
|
||||||
|
|
||||||
@Binds
|
|
||||||
@IntoMap
|
|
||||||
@WorkerKey(PruneCacheWorker::class)
|
|
||||||
internal abstract fun bindPruneCacheWorkerFactory(worker: PruneCacheWorker.Factory): ChildWorkerFactory
|
|
||||||
}
|
|
|
@ -19,6 +19,7 @@ package app.pachli.worker
|
||||||
|
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.hilt.work.HiltWorker
|
||||||
import androidx.work.CoroutineWorker
|
import androidx.work.CoroutineWorker
|
||||||
import androidx.work.ForegroundInfo
|
import androidx.work.ForegroundInfo
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
|
@ -26,13 +27,15 @@ import app.pachli.R
|
||||||
import app.pachli.components.notifications.NOTIFICATION_ID_FETCH_NOTIFICATION
|
import app.pachli.components.notifications.NOTIFICATION_ID_FETCH_NOTIFICATION
|
||||||
import app.pachli.components.notifications.NotificationFetcher
|
import app.pachli.components.notifications.NotificationFetcher
|
||||||
import app.pachli.components.notifications.createWorkerNotification
|
import app.pachli.components.notifications.createWorkerNotification
|
||||||
import javax.inject.Inject
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
/** Fetch and show new notifications. */
|
/** Fetch and show new notifications. */
|
||||||
class NotificationWorker(
|
@HiltWorker
|
||||||
appContext: Context,
|
class NotificationWorker @AssistedInject constructor(
|
||||||
params: WorkerParameters,
|
@Assisted appContext: Context,
|
||||||
|
@Assisted params: WorkerParameters,
|
||||||
private val notificationsFetcher: NotificationFetcher,
|
private val notificationsFetcher: NotificationFetcher,
|
||||||
) : CoroutineWorker(appContext, params) {
|
) : CoroutineWorker(appContext, params) {
|
||||||
val notification: Notification = createWorkerNotification(applicationContext, R.string.notification_notification_worker)
|
val notification: Notification = createWorkerNotification(applicationContext, R.string.notification_notification_worker)
|
||||||
|
@ -44,12 +47,4 @@ class NotificationWorker(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getForegroundInfo() = ForegroundInfo(NOTIFICATION_ID_FETCH_NOTIFICATION, notification)
|
override suspend fun getForegroundInfo() = ForegroundInfo(NOTIFICATION_ID_FETCH_NOTIFICATION, notification)
|
||||||
|
|
||||||
class Factory @Inject constructor(
|
|
||||||
private val notificationsFetcher: NotificationFetcher,
|
|
||||||
) : ChildWorkerFactory {
|
|
||||||
override fun createWorker(appContext: Context, params: WorkerParameters): CoroutineWorker {
|
|
||||||
return NotificationWorker(appContext, params, notificationsFetcher)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,22 +19,24 @@ package app.pachli.worker
|
||||||
|
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.hilt.work.HiltWorker
|
||||||
import androidx.work.CoroutineWorker
|
import androidx.work.CoroutineWorker
|
||||||
import androidx.work.ForegroundInfo
|
import androidx.work.ForegroundInfo
|
||||||
import androidx.work.ListenableWorker
|
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import app.pachli.R
|
import app.pachli.R
|
||||||
import app.pachli.components.notifications.NOTIFICATION_ID_PRUNE_CACHE
|
import app.pachli.components.notifications.NOTIFICATION_ID_PRUNE_CACHE
|
||||||
import app.pachli.components.notifications.createWorkerNotification
|
import app.pachli.components.notifications.createWorkerNotification
|
||||||
import app.pachli.core.accounts.AccountManager
|
import app.pachli.core.accounts.AccountManager
|
||||||
import app.pachli.core.database.dao.TimelineDao
|
import app.pachli.core.database.dao.TimelineDao
|
||||||
import javax.inject.Inject
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
/** Prune the database cache of old statuses. */
|
/** Prune the database cache of old statuses. */
|
||||||
class PruneCacheWorker(
|
@HiltWorker
|
||||||
appContext: Context,
|
class PruneCacheWorker @AssistedInject constructor(
|
||||||
workerParams: WorkerParameters,
|
@Assisted appContext: Context,
|
||||||
|
@Assisted workerParams: WorkerParameters,
|
||||||
private val timelineDao: TimelineDao,
|
private val timelineDao: TimelineDao,
|
||||||
private val accountManager: AccountManager,
|
private val accountManager: AccountManager,
|
||||||
) : CoroutineWorker(appContext, workerParams) {
|
) : CoroutineWorker(appContext, workerParams) {
|
||||||
|
@ -54,13 +56,4 @@ class PruneCacheWorker(
|
||||||
private const val MAX_STATUSES_IN_CACHE = 1000
|
private const val MAX_STATUSES_IN_CACHE = 1000
|
||||||
const val PERIODIC_WORK_TAG = "PruneCacheWorker_periodic"
|
const val PERIODIC_WORK_TAG = "PruneCacheWorker_periodic"
|
||||||
}
|
}
|
||||||
|
|
||||||
class Factory @Inject constructor(
|
|
||||||
private val timelineDao: TimelineDao,
|
|
||||||
private val accountManager: AccountManager,
|
|
||||||
) : ChildWorkerFactory {
|
|
||||||
override fun createWorker(appContext: Context, params: WorkerParameters): ListenableWorker {
|
|
||||||
return PruneCacheWorker(appContext, params, timelineDao, accountManager)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,31 +19,39 @@ package app.pachli.worker
|
||||||
|
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.hilt.work.HiltWorker
|
||||||
import androidx.work.CoroutineWorker
|
import androidx.work.CoroutineWorker
|
||||||
import androidx.work.ForegroundInfo
|
import androidx.work.ForegroundInfo
|
||||||
import androidx.work.ListenableWorker
|
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import app.pachli.R
|
import app.pachli.R
|
||||||
import app.pachli.components.notifications.NOTIFICATION_ID_PRUNE_CACHE
|
import app.pachli.components.notifications.NOTIFICATION_ID_PRUNE_CACHE
|
||||||
import app.pachli.components.notifications.createWorkerNotification
|
import app.pachli.components.notifications.createWorkerNotification
|
||||||
import app.pachli.core.database.dao.LogEntryDao
|
import app.pachli.core.database.dao.LogEntryDao
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import javax.inject.Inject
|
|
||||||
import kotlin.time.Duration.Companion.hours
|
import kotlin.time.Duration.Companion.hours
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
/** Prune the database cache of old statuses. */
|
/** Prune the database cache of old statuses. */
|
||||||
class PruneLogEntryEntityWorker(
|
@HiltWorker
|
||||||
appContext: Context,
|
class PruneLogEntryEntityWorker @AssistedInject constructor(
|
||||||
workerParams: WorkerParameters,
|
@Assisted appContext: Context,
|
||||||
|
@Assisted workerParams: WorkerParameters,
|
||||||
private val logEntryDao: LogEntryDao,
|
private val logEntryDao: LogEntryDao,
|
||||||
) : CoroutineWorker(appContext, workerParams) {
|
) : CoroutineWorker(appContext, workerParams) {
|
||||||
val notification: Notification = createWorkerNotification(applicationContext, R.string.notification_prune_cache)
|
val notification: Notification = createWorkerNotification(applicationContext, R.string.notification_prune_cache)
|
||||||
|
|
||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
val now = Instant.now()
|
return try {
|
||||||
val oldest = now.minusMillis(OLDEST_ENTRY.inWholeMilliseconds)
|
val now = Instant.now()
|
||||||
logEntryDao.prune(oldest)
|
val oldest = now.minusMillis(OLDEST_ENTRY.inWholeMilliseconds)
|
||||||
return Result.success()
|
logEntryDao.prune(oldest)
|
||||||
|
Result.success()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "error in PruneLogEntryEntityWorker.doWork")
|
||||||
|
Result.failure()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getForegroundInfo() = ForegroundInfo(NOTIFICATION_ID_PRUNE_CACHE, notification)
|
override suspend fun getForegroundInfo() = ForegroundInfo(NOTIFICATION_ID_PRUNE_CACHE, notification)
|
||||||
|
@ -52,12 +60,4 @@ class PruneLogEntryEntityWorker(
|
||||||
private val OLDEST_ENTRY = 48.hours
|
private val OLDEST_ENTRY = 48.hours
|
||||||
const val PERIODIC_WORK_TAG = "PruneLogEntryEntityWorker_periodic"
|
const val PERIODIC_WORK_TAG = "PruneLogEntryEntityWorker_periodic"
|
||||||
}
|
}
|
||||||
|
|
||||||
class Factory @Inject constructor(
|
|
||||||
private val logEntryDao: LogEntryDao,
|
|
||||||
) : ChildWorkerFactory {
|
|
||||||
override fun createWorker(appContext: Context, params: WorkerParameters): ListenableWorker {
|
|
||||||
return PruneLogEntryEntityWorker(appContext, params, logEntryDao)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023 Pachli Association
|
|
||||||
*
|
|
||||||
* This file is a part of Pachli.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
|
||||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
|
||||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
||||||
* Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along with Pachli; if not,
|
|
||||||
* see <http://www.gnu.org/licenses>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package app.pachli.worker
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.work.ListenableWorker
|
|
||||||
import androidx.work.WorkerFactory
|
|
||||||
import androidx.work.WorkerParameters
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Provider
|
|
||||||
import javax.inject.Singleton
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Workers implement this and are added to the map in [app.pachli.di.WorkerModule]
|
|
||||||
* so they can be created by [WorkerFactory.createWorker].
|
|
||||||
*/
|
|
||||||
interface ChildWorkerFactory {
|
|
||||||
/** Create a new instance of the given worker. */
|
|
||||||
fun createWorker(appContext: Context, params: WorkerParameters): ListenableWorker
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates workers, delegating to each worker's [ChildWorkerFactory.createWorker] to do the
|
|
||||||
* creation.
|
|
||||||
*
|
|
||||||
* @see [app.pachli.worker.NotificationWorker]
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
class WorkerFactory @Inject constructor(
|
|
||||||
private val workerFactories: Map<Class<out ListenableWorker>, @JvmSuppressWildcards Provider<ChildWorkerFactory>>,
|
|
||||||
) : WorkerFactory() {
|
|
||||||
override fun createWorker(
|
|
||||||
appContext: Context,
|
|
||||||
workerClassName: String,
|
|
||||||
workerParameters: WorkerParameters,
|
|
||||||
): ListenableWorker? {
|
|
||||||
val key = try {
|
|
||||||
Class.forName(workerClassName)
|
|
||||||
} catch (e: ClassNotFoundException) {
|
|
||||||
// Class might be missing if it was renamed / moved to a different package, as
|
|
||||||
// periodic work requests from before the rename might still exist. Catch and
|
|
||||||
// return null, which should stop future requests.
|
|
||||||
Timber.d(e, "Invalid class: %s", workerClassName)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
workerFactories[key]?.let {
|
|
||||||
return it.get().createWorker(appContext, workerParameters)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -10,6 +10,7 @@ androidx-constraintlayout = "2.1.4"
|
||||||
androidx-core = "1.12.0"
|
androidx-core = "1.12.0"
|
||||||
androidx-exifinterface = "1.3.7"
|
androidx-exifinterface = "1.3.7"
|
||||||
androidx-fragment = "1.6.2"
|
androidx-fragment = "1.6.2"
|
||||||
|
androidx-hilt = "1.2.0"
|
||||||
androidx-junit = "1.1.5"
|
androidx-junit = "1.1.5"
|
||||||
androidx-lifecycle = "2.7.0"
|
androidx-lifecycle = "2.7.0"
|
||||||
androidx-media3 = "1.2.1"
|
androidx-media3 = "1.2.1"
|
||||||
|
@ -109,6 +110,9 @@ androidx-emoji2-views-core = { module = "androidx.emoji2:emoji2-views", version.
|
||||||
androidx-emoji2-view-helper = { module = "androidx.emoji2:emoji2-views-helper", version.ref = "emoji2" }
|
androidx-emoji2-view-helper = { module = "androidx.emoji2:emoji2-views-helper", version.ref = "emoji2" }
|
||||||
androidx-exifinterface = { module = "androidx.exifinterface:exifinterface", version.ref = "androidx-exifinterface" }
|
androidx-exifinterface = { module = "androidx.exifinterface:exifinterface", version.ref = "androidx-exifinterface" }
|
||||||
androidx-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "androidx-fragment" }
|
androidx-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "androidx-fragment" }
|
||||||
|
androidx-hilt-common = { module = "androidx.hilt:hilt-common", version.ref = "androidx-hilt" }
|
||||||
|
androidx-hilt-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "androidx-hilt" }
|
||||||
|
androidx-hilt-work = { module = "androidx.hilt:hilt-work", version.ref = "androidx-hilt" }
|
||||||
androidx-lifecycle-common-java8 = { module = "androidx.lifecycle:lifecycle-common-java8", version.ref = "androidx-lifecycle" }
|
androidx-lifecycle-common-java8 = { module = "androidx.lifecycle:lifecycle-common-java8", version.ref = "androidx-lifecycle" }
|
||||||
androidx-lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "androidx-lifecycle" }
|
androidx-lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "androidx-lifecycle" }
|
||||||
androidx-lifecycle-reactivestreams-ktx = { module = "androidx.lifecycle:lifecycle-reactivestreams-ktx", version.ref = "androidx-lifecycle" }
|
androidx-lifecycle-reactivestreams-ktx = { module = "androidx.lifecycle:lifecycle-reactivestreams-ktx", version.ref = "androidx-lifecycle" }
|
||||||
|
|
Loading…
Reference in New Issue