Move cache pruning to a WorkManager worker (#3649)
- Extend what was `NotificationWorkerFactory` to `WorkerFactory`. This can construct arbitrary Workers as long as they provide their own Factory for construction. The per-Worker factory contains any injected components just for that worker type, keeping `WorkerFactory` clean. - Move `NotificationWorkerFactory` to the new model. - Implement `PruneCacheWorker`, and remove the code from `CachedTimelineViewModel`. - Create the periodic worker in `TuskyApplication`, ensuring that the database is only pruned when the device is idle.
This commit is contained in:
parent
85b7caa887
commit
4025ab35ff
11 changed files with 227 additions and 71 deletions
|
@ -18,15 +18,19 @@ package com.keylesspalace.tusky
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.work.Constraints
|
||||||
|
import androidx.work.ExistingPeriodicWorkPolicy
|
||||||
|
import androidx.work.PeriodicWorkRequestBuilder
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import autodispose2.AutoDisposePlugins
|
import autodispose2.AutoDisposePlugins
|
||||||
import com.keylesspalace.tusky.components.notifications.NotificationWorkerFactory
|
|
||||||
import com.keylesspalace.tusky.di.AppInjector
|
import com.keylesspalace.tusky.di.AppInjector
|
||||||
import com.keylesspalace.tusky.settings.PrefKeys
|
import com.keylesspalace.tusky.settings.PrefKeys
|
||||||
import com.keylesspalace.tusky.settings.SCHEMA_VERSION
|
import com.keylesspalace.tusky.settings.SCHEMA_VERSION
|
||||||
import com.keylesspalace.tusky.util.APP_THEME_DEFAULT
|
import com.keylesspalace.tusky.util.APP_THEME_DEFAULT
|
||||||
import com.keylesspalace.tusky.util.LocaleManager
|
import com.keylesspalace.tusky.util.LocaleManager
|
||||||
import com.keylesspalace.tusky.util.setAppNightMode
|
import com.keylesspalace.tusky.util.setAppNightMode
|
||||||
|
import com.keylesspalace.tusky.worker.PruneCacheWorker
|
||||||
|
import com.keylesspalace.tusky.worker.WorkerFactory
|
||||||
import dagger.android.DispatchingAndroidInjector
|
import dagger.android.DispatchingAndroidInjector
|
||||||
import dagger.android.HasAndroidInjector
|
import dagger.android.HasAndroidInjector
|
||||||
import de.c1710.filemojicompat_defaults.DefaultEmojiPackList
|
import de.c1710.filemojicompat_defaults.DefaultEmojiPackList
|
||||||
|
@ -35,6 +39,7 @@ import de.c1710.filemojicompat_ui.helpers.EmojiPreference
|
||||||
import io.reactivex.rxjava3.plugins.RxJavaPlugins
|
import io.reactivex.rxjava3.plugins.RxJavaPlugins
|
||||||
import org.conscrypt.Conscrypt
|
import org.conscrypt.Conscrypt
|
||||||
import java.security.Security
|
import java.security.Security
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class TuskyApplication : Application(), HasAndroidInjector {
|
class TuskyApplication : Application(), HasAndroidInjector {
|
||||||
|
@ -42,7 +47,7 @@ class TuskyApplication : Application(), HasAndroidInjector {
|
||||||
lateinit var androidInjector: DispatchingAndroidInjector<Any>
|
lateinit var androidInjector: DispatchingAndroidInjector<Any>
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var notificationWorkerFactory: NotificationWorkerFactory
|
lateinit var workerFactory: WorkerFactory
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var localeManager: LocaleManager
|
lateinit var localeManager: LocaleManager
|
||||||
|
@ -93,9 +98,19 @@ class TuskyApplication : Application(), HasAndroidInjector {
|
||||||
WorkManager.initialize(
|
WorkManager.initialize(
|
||||||
this,
|
this,
|
||||||
androidx.work.Configuration.Builder()
|
androidx.work.Configuration.Builder()
|
||||||
.setWorkerFactory(notificationWorkerFactory)
|
.setWorkerFactory(workerFactory)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Prune the database every ~ 12 hours when the device is idle.
|
||||||
|
val pruneCacheWorker = PeriodicWorkRequestBuilder<PruneCacheWorker>(12, TimeUnit.HOURS)
|
||||||
|
.setConstraints(Constraints.Builder().setRequiresDeviceIdle(true).build())
|
||||||
|
.build()
|
||||||
|
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
|
||||||
|
PruneCacheWorker.PERIODIC_WORK_TAG,
|
||||||
|
ExistingPeriodicWorkPolicy.KEEP,
|
||||||
|
pruneCacheWorker
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun androidInjector() = androidInjector
|
override fun androidInjector() = androidInjector
|
||||||
|
|
|
@ -63,6 +63,7 @@ import com.keylesspalace.tusky.entity.Status;
|
||||||
import com.keylesspalace.tusky.receiver.SendStatusBroadcastReceiver;
|
import com.keylesspalace.tusky.receiver.SendStatusBroadcastReceiver;
|
||||||
import com.keylesspalace.tusky.util.StringUtils;
|
import com.keylesspalace.tusky.util.StringUtils;
|
||||||
import com.keylesspalace.tusky.viewdata.PollViewDataKt;
|
import com.keylesspalace.tusky.viewdata.PollViewDataKt;
|
||||||
|
import com.keylesspalace.tusky.worker.NotificationWorker;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
/* Copyright 2020 Tusky Contributors
|
|
||||||
*
|
|
||||||
* This file is part of Tusky.
|
|
||||||
*
|
|
||||||
* Tusky is free software: you can redistribute it and/or modify it under the terms of the GNU
|
|
||||||
* Lesser General Public License as published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Tusky 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 Lesser
|
|
||||||
* General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along with Tusky. If
|
|
||||||
* not, see <http://www.gnu.org/licenses/>. */
|
|
||||||
|
|
||||||
package com.keylesspalace.tusky.components.notifications
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.work.ListenableWorker
|
|
||||||
import androidx.work.Worker
|
|
||||||
import androidx.work.WorkerFactory
|
|
||||||
import androidx.work.WorkerParameters
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class NotificationWorker(
|
|
||||||
context: Context,
|
|
||||||
params: WorkerParameters,
|
|
||||||
private val notificationsFetcher: NotificationFetcher
|
|
||||||
) : Worker(context, params) {
|
|
||||||
|
|
||||||
override fun doWork(): Result {
|
|
||||||
notificationsFetcher.fetchAndShow()
|
|
||||||
return Result.success()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NotificationWorkerFactory @Inject constructor(
|
|
||||||
private val notificationsFetcher: NotificationFetcher
|
|
||||||
) : WorkerFactory() {
|
|
||||||
|
|
||||||
override fun createWorker(
|
|
||||||
appContext: Context,
|
|
||||||
workerClassName: String,
|
|
||||||
workerParameters: WorkerParameters
|
|
||||||
): ListenableWorker? {
|
|
||||||
if (workerClassName == NotificationWorker::class.java.name) {
|
|
||||||
return NotificationWorker(appContext, workerParameters, notificationsFetcher)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -50,14 +50,11 @@ import com.keylesspalace.tusky.util.EmptyPagingSource
|
||||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.asExecutor
|
import kotlinx.coroutines.asExecutor
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.time.DurationUnit
|
|
||||||
import kotlin.time.toDuration
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TimelineViewModel that caches all statuses in a local database
|
* TimelineViewModel that caches all statuses in a local database
|
||||||
|
@ -107,16 +104,6 @@ class CachedTimelineViewModel @Inject constructor(
|
||||||
.flowOn(Dispatchers.Default)
|
.flowOn(Dispatchers.Default)
|
||||||
.cachedIn(viewModelScope)
|
.cachedIn(viewModelScope)
|
||||||
|
|
||||||
init {
|
|
||||||
viewModelScope.launch {
|
|
||||||
delay(5.toDuration(DurationUnit.SECONDS)) // delay so the db is not locked during initial ui refresh
|
|
||||||
accountManager.activeAccount?.id?.let { accountId ->
|
|
||||||
db.timelineDao().cleanup(accountId, MAX_STATUSES_IN_CACHE)
|
|
||||||
db.timelineDao().cleanupAccounts(accountId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun updatePoll(newPoll: Poll, status: StatusViewData.Concrete) {
|
override fun updatePoll(newPoll: Poll, status: StatusViewData.Concrete) {
|
||||||
// handled by CacheUpdater
|
// handled by CacheUpdater
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,8 @@ import javax.inject.Singleton
|
||||||
ActivitiesModule::class,
|
ActivitiesModule::class,
|
||||||
ServicesModule::class,
|
ServicesModule::class,
|
||||||
BroadcastReceiverModule::class,
|
BroadcastReceiverModule::class,
|
||||||
ViewModelModule::class
|
ViewModelModule::class,
|
||||||
|
WorkerModule::class
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
interface AppComponent {
|
interface AppComponent {
|
||||||
|
|
45
app/src/main/java/com/keylesspalace/tusky/di/WorkerModule.kt
Normal file
45
app/src/main/java/com/keylesspalace/tusky/di/WorkerModule.kt
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Tusky Contributors
|
||||||
|
*
|
||||||
|
* This file is a part of Tusky.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Tusky 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 Tusky; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky.di
|
||||||
|
|
||||||
|
import androidx.work.ListenableWorker
|
||||||
|
import com.keylesspalace.tusky.worker.ChildWorkerFactory
|
||||||
|
import com.keylesspalace.tusky.worker.NotificationWorker
|
||||||
|
import com.keylesspalace.tusky.worker.PruneCacheWorker
|
||||||
|
import dagger.Binds
|
||||||
|
import dagger.MapKey
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.multibindings.IntoMap
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
@MapKey
|
||||||
|
annotation class WorkerKey(val value: KClass<out ListenableWorker>)
|
||||||
|
|
||||||
|
@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
|
||||||
|
}
|
|
@ -20,11 +20,11 @@ import android.content.Intent
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.work.OneTimeWorkRequest
|
import androidx.work.OneTimeWorkRequest
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import com.keylesspalace.tusky.components.notifications.NotificationWorker
|
|
||||||
import com.keylesspalace.tusky.components.notifications.registerUnifiedPushEndpoint
|
import com.keylesspalace.tusky.components.notifications.registerUnifiedPushEndpoint
|
||||||
import com.keylesspalace.tusky.components.notifications.unregisterUnifiedPushEndpoint
|
import com.keylesspalace.tusky.components.notifications.unregisterUnifiedPushEndpoint
|
||||||
import com.keylesspalace.tusky.db.AccountManager
|
import com.keylesspalace.tusky.db.AccountManager
|
||||||
import com.keylesspalace.tusky.network.MastodonApi
|
import com.keylesspalace.tusky.network.MastodonApi
|
||||||
|
import com.keylesspalace.tusky.worker.NotificationWorker
|
||||||
import dagger.android.AndroidInjection
|
import dagger.android.AndroidInjection
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Tusky Contributors
|
||||||
|
*
|
||||||
|
* This file is a part of Tusky.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Tusky 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 Tusky; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky.worker
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.work.Worker
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
|
import com.keylesspalace.tusky.components.notifications.NotificationFetcher
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/** Fetch and show new notifications. */
|
||||||
|
class NotificationWorker(
|
||||||
|
appContext: Context,
|
||||||
|
params: WorkerParameters,
|
||||||
|
private val notificationsFetcher: NotificationFetcher
|
||||||
|
) : Worker(appContext, params) {
|
||||||
|
override fun doWork(): Result {
|
||||||
|
notificationsFetcher.fetchAndShow()
|
||||||
|
return Result.success()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Factory @Inject constructor(
|
||||||
|
private val notificationsFetcher: NotificationFetcher
|
||||||
|
) : ChildWorkerFactory {
|
||||||
|
override fun createWorker(appContext: Context, params: WorkerParameters): Worker {
|
||||||
|
return NotificationWorker(appContext, params, notificationsFetcher)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Tusky Contributors
|
||||||
|
*
|
||||||
|
* This file is a part of Tusky.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Tusky 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 Tusky; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky.worker
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.work.CoroutineWorker
|
||||||
|
import androidx.work.ListenableWorker
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
|
import com.keylesspalace.tusky.db.AccountManager
|
||||||
|
import com.keylesspalace.tusky.db.AppDatabase
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/** Prune the database cache of old statuses. */
|
||||||
|
class PruneCacheWorker(
|
||||||
|
appContext: Context,
|
||||||
|
workerParams: WorkerParameters,
|
||||||
|
private val appDatabase: AppDatabase,
|
||||||
|
private val accountManager: AccountManager
|
||||||
|
) : CoroutineWorker(appContext, workerParams) {
|
||||||
|
override suspend fun doWork(): Result {
|
||||||
|
for (account in accountManager.accounts) {
|
||||||
|
Log.d(TAG, "Pruning database using account ID: ${account.id}")
|
||||||
|
appDatabase.timelineDao().cleanup(account.id, MAX_STATUSES_IN_CACHE)
|
||||||
|
}
|
||||||
|
return Result.success()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "PruneCacheWorker"
|
||||||
|
private const val MAX_STATUSES_IN_CACHE = 1000
|
||||||
|
const val PERIODIC_WORK_TAG = "PruneCacheWorker_periodic"
|
||||||
|
}
|
||||||
|
|
||||||
|
class Factory @Inject constructor(
|
||||||
|
private val appDatabase: AppDatabase,
|
||||||
|
private val accountManager: AccountManager
|
||||||
|
) : ChildWorkerFactory {
|
||||||
|
override fun createWorker(appContext: Context, params: WorkerParameters): ListenableWorker {
|
||||||
|
return PruneCacheWorker(appContext, params, appDatabase, accountManager)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Tusky Contributors
|
||||||
|
*
|
||||||
|
* This file is a part of Tusky.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Tusky 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 Tusky; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky.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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Workers implement this and are added to the map in [com.keylesspalace.tusky.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 [com.keylesspalace.tusky.components.notifications.NotificationWorker]
|
||||||
|
*/
|
||||||
|
class WorkerFactory @Inject constructor(
|
||||||
|
val workerFactories: Map<Class<out ListenableWorker>, @JvmSuppressWildcards Provider<ChildWorkerFactory>>
|
||||||
|
) : WorkerFactory() {
|
||||||
|
override fun createWorker(
|
||||||
|
appContext: Context,
|
||||||
|
workerClassName: String,
|
||||||
|
workerParameters: WorkerParameters
|
||||||
|
): ListenableWorker? {
|
||||||
|
val key = Class.forName(workerClassName)
|
||||||
|
workerFactories[key]?.let {
|
||||||
|
return it.get().createWorker(appContext, workerParameters)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
|
@ -92,7 +92,7 @@ androidx-sharetarget = { module = "androidx.sharetarget:sharetarget", version.re
|
||||||
androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "androidx-swiperefresh-layout" }
|
androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "androidx-swiperefresh-layout" }
|
||||||
androidx-test-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-junit" }
|
androidx-test-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-junit" }
|
||||||
androidx-viewpager2 = { module = "androidx.viewpager2:viewpager2", version.ref = "androidx-viewpager2" }
|
androidx-viewpager2 = { module = "androidx.viewpager2:viewpager2", version.ref = "androidx-viewpager2" }
|
||||||
androidx-work-runtime = { module = "androidx.work:work-runtime", version.ref = "androidx-work" }
|
androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "androidx-work" }
|
||||||
androidx-work-testing = { module = "androidx.work:work-testing", version.ref = "androidx-work" }
|
androidx-work-testing = { module = "androidx.work:work-testing", version.ref = "androidx-work" }
|
||||||
autodispose-android-lifecycle = { module = "com.uber.autodispose2:autodispose-androidx-lifecycle", version.ref = "autodispose" }
|
autodispose-android-lifecycle = { module = "com.uber.autodispose2:autodispose-androidx-lifecycle", version.ref = "autodispose" }
|
||||||
autodispose-core = { module = "com.uber.autodispose2:autodispose", version.ref = "autodispose" }
|
autodispose-core = { module = "com.uber.autodispose2:autodispose", version.ref = "autodispose" }
|
||||||
|
@ -146,7 +146,7 @@ androidx = ["androidx-core-ktx", "androidx-appcompat", "androidx-fragment-ktx",
|
||||||
"androidx-recyclerview", "androidx-exifinterface", "androidx-cardview", "androidx-preference-ktx", "androidx-sharetarget",
|
"androidx-recyclerview", "androidx-exifinterface", "androidx-cardview", "androidx-preference-ktx", "androidx-sharetarget",
|
||||||
"androidx-emoji2-core", "androidx-emoji2-views-core", "androidx-emoji2-view-helper", "androidx-lifecycle-viewmodel-ktx",
|
"androidx-emoji2-core", "androidx-emoji2-views-core", "androidx-emoji2-view-helper", "androidx-lifecycle-viewmodel-ktx",
|
||||||
"androidx-lifecycle-livedata-ktx", "androidx-lifecycle-common-java8", "androidx-lifecycle-reactivestreams-ktx",
|
"androidx-lifecycle-livedata-ktx", "androidx-lifecycle-common-java8", "androidx-lifecycle-reactivestreams-ktx",
|
||||||
"androidx-constraintlayout", "androidx-paging-runtime-ktx", "androidx-viewpager2", "androidx-work-runtime",
|
"androidx-constraintlayout", "androidx-paging-runtime-ktx", "androidx-viewpager2", "androidx-work-runtime-ktx",
|
||||||
"androidx-core-splashscreen", "androidx-activity"]
|
"androidx-core-splashscreen", "androidx-activity"]
|
||||||
autodispose = ["autodispose-core", "autodispose-android-lifecycle"]
|
autodispose = ["autodispose-core", "autodispose-android-lifecycle"]
|
||||||
dagger = ["dagger-core", "dagger-android-core", "dagger-android-support"]
|
dagger = ["dagger-core", "dagger-android-core", "dagger-android-support"]
|
||||||
|
|
Loading…
Reference in a new issue