Options to run background workers only using wifi

This commit is contained in:
Koitharu
2023-07-20 09:25:12 +03:00
parent dafca9e1e1
commit 297029a659
21 changed files with 250 additions and 185 deletions

View File

@@ -17,7 +17,6 @@ import org.koitharu.kotatsu.core.ui.BaseFragment
import org.koitharu.kotatsu.core.ui.list.PaginationScrollListener
import org.koitharu.kotatsu.core.ui.list.decor.TypedSpacingItemDecoration
import org.koitharu.kotatsu.core.util.ext.addMenuProvider
import org.koitharu.kotatsu.core.util.ext.getThemeColor
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent
import org.koitharu.kotatsu.databinding.FragmentFeedBinding
@@ -29,7 +28,6 @@ import org.koitharu.kotatsu.main.ui.owners.BottomNavOwner
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.tracker.ui.feed.adapter.FeedAdapter
import org.koitharu.kotatsu.tracker.work.TrackWorker
import javax.inject.Inject
@AndroidEntryPoint
@@ -64,11 +62,7 @@ class FeedFragment :
)
addItemDecoration(decoration)
}
with(binding.swipeRefreshLayout) {
setProgressBackgroundColorSchemeColor(context.getThemeColor(com.google.android.material.R.attr.colorPrimary))
setColorSchemeColors(context.getThemeColor(com.google.android.material.R.attr.colorOnPrimary))
setOnRefreshListener(this@FeedFragment)
}
binding.swipeRefreshLayout.setOnRefreshListener(this)
addMenuProvider(
FeedMenuProvider(
binding.recyclerView,
@@ -81,8 +75,7 @@ class FeedFragment :
viewModel.onFeedCleared.observeEvent(viewLifecycleOwner) {
onFeedCleared()
}
TrackWorker.observeIsRunning(binding.root.context.applicationContext)
.observe(viewLifecycleOwner, this::onIsTrackerRunningChanged)
viewModel.isRunning.observe(viewLifecycleOwner, this::onIsTrackerRunningChanged)
}
override fun onDestroyView() {
@@ -97,7 +90,7 @@ class FeedFragment :
}
override fun onRefresh() {
TrackWorker.startNow(context ?: return)
viewModel.update()
}
override fun onRetryClick(error: Throwable) = Unit

View File

@@ -8,7 +8,6 @@ import android.view.View
import androidx.core.view.MenuProvider
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.dialog.CheckBoxAlertDialog
import org.koitharu.kotatsu.tracker.work.TrackWorker
class FeedMenuProvider(
private val snackbarHost: View,
@@ -24,7 +23,7 @@ class FeedMenuProvider(
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
R.id.action_update -> {
TrackWorker.startNow(context)
viewModel.update()
true
}

View File

@@ -21,6 +21,7 @@ import org.koitharu.kotatsu.list.ui.model.LoadingState
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
import org.koitharu.kotatsu.tracker.domain.model.TrackingLogItem
import org.koitharu.kotatsu.tracker.ui.feed.model.toFeedItem
import org.koitharu.kotatsu.tracker.work.TrackWorker
import java.util.Date
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
@@ -31,11 +32,15 @@ private const val PAGE_SIZE = 20
@HiltViewModel
class FeedViewModel @Inject constructor(
private val repository: TrackingRepository,
private val scheduler: TrackWorker.Scheduler,
) : BaseViewModel() {
private val limit = MutableStateFlow(PAGE_SIZE)
private val isReady = AtomicBoolean(false)
val isRunning = scheduler.observeIsRunning()
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, false)
val onFeedCleared = MutableEventFlow<Unit>()
val content = repository.observeTrackingLog(limit)
.map { list ->
@@ -70,6 +75,10 @@ class FeedViewModel @Inject constructor(
}
}
fun update() {
scheduler.startNow()
}
private fun List<TrackingLogItem>.mapList(): List<ListModel> {
val destination = ArrayList<ListModel>((size * 1.4).toInt())
var prevDate: DateTimeAgo? = null

View File

@@ -29,6 +29,7 @@ import androidx.work.WorkerParameters
import androidx.work.await
import coil.ImageLoader
import coil.request.ImageRequest
import dagger.Reusable
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.Dispatchers
@@ -43,7 +44,7 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.logs.FileLogger
import org.koitharu.kotatsu.core.logs.TrackerLogger
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.WorkManagerHelper
import org.koitharu.kotatsu.core.util.ext.awaitUniqueWorkInfoByName
import org.koitharu.kotatsu.core.util.ext.toBitmapOrNull
import org.koitharu.kotatsu.core.util.ext.trySetForeground
import org.koitharu.kotatsu.details.ui.DetailsActivity
@@ -54,6 +55,7 @@ import org.koitharu.kotatsu.settings.work.PeriodicWorkScheduler
import org.koitharu.kotatsu.tracker.domain.Tracker
import org.koitharu.kotatsu.tracker.domain.model.MangaUpdates
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@HiltWorker
class TrackWorker @AssistedInject constructor(
@@ -237,57 +239,68 @@ class TrackWorker @AssistedInject constructor(
.build()
}
companion object : PeriodicWorkScheduler {
@Reusable
class Scheduler @Inject constructor(
private val workManager: WorkManager,
private val settings: AppSettings,
) : PeriodicWorkScheduler {
private const val WORKER_CHANNEL_ID = "track_worker"
private const val WORKER_NOTIFICATION_ID = 35
private const val TAG = "tracking"
private const val TAG_ONESHOT = "tracking_oneshot"
private const val MAX_PARALLELISM = 4
private const val DATA_KEY_SUCCESS = "success"
private const val DATA_KEY_FAILED = "failed"
override suspend fun schedule(context: Context) {
val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
override suspend fun schedule() {
val constraints = createConstraints()
val request = PeriodicWorkRequestBuilder<TrackWorker>(4, TimeUnit.HOURS)
.setConstraints(constraints)
.addTag(TAG)
.setBackoffCriteria(BackoffPolicy.LINEAR, 30, TimeUnit.MINUTES)
.build()
WorkManager.getInstance(context)
workManager
.enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.KEEP, request)
.await()
}
override suspend fun unschedule(context: Context) {
WorkManager.getInstance(context)
override suspend fun unschedule() {
workManager
.cancelUniqueWork(TAG)
.await()
}
override suspend fun isScheduled(context: Context): Boolean {
return WorkManagerHelper(WorkManager.getInstance(context))
.getUniqueWorkInfoByName(TAG)
override suspend fun isScheduled(): Boolean {
return workManager
.awaitUniqueWorkInfoByName(TAG)
.any { !it.state.isFinished }
}
fun startNow(context: Context) {
fun startNow() {
val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
val request = OneTimeWorkRequestBuilder<TrackWorker>()
.setConstraints(constraints)
.addTag(TAG_ONESHOT)
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.build()
WorkManager.getInstance(context).enqueue(request)
workManager.enqueue(request)
}
fun observeIsRunning(context: Context): Flow<Boolean> {
fun observeIsRunning(): Flow<Boolean> {
val query = WorkQuery.Builder.fromTags(listOf(TAG, TAG_ONESHOT)).build()
return WorkManager.getInstance(context).getWorkInfosLiveData(query)
return workManager.getWorkInfosLiveData(query)
.asFlow()
.map { works ->
works.any { x -> x.state == WorkInfo.State.RUNNING }
}
}
private fun createConstraints() = Constraints.Builder()
.setRequiredNetworkType(if (settings.isTrackerWifiOnly) NetworkType.UNMETERED else NetworkType.CONNECTED)
.build()
}
private companion object {
const val WORKER_CHANNEL_ID = "track_worker"
const val WORKER_NOTIFICATION_ID = 35
const val TAG = "tracking"
const val TAG_ONESHOT = "tracking_oneshot"
const val MAX_PARALLELISM = 4
const val DATA_KEY_SUCCESS = "success"
const val DATA_KEY_FAILED = "failed"
}
}