Replace refresh work with coroutines

pull/2860/head
Alex Baker 7 months ago
parent dc90e583e4
commit 7fb85b6da1

@ -21,13 +21,11 @@ import org.tasks.jobs.WorkManager
import org.tasks.location.GeofenceApi
import org.tasks.notifications.NotificationManager
import org.tasks.preferences.Preferences
import org.tasks.scheduling.RefreshScheduler
import org.tasks.sync.SyncAdapters
import javax.inject.Inject
class TaskDao @Inject constructor(
private val taskDao: TaskDao,
private val refreshScheduler: RefreshScheduler,
private val localBroadcastManager: LocalBroadcastManager,
private val notificationManager: NotificationManager,
private val geofenceApi: GeofenceApi,
@ -134,7 +132,6 @@ class TaskDao @Inject constructor(
geofenceApi.update(task.id)
}
alarmService.scheduleAlarms(task)
refreshScheduler.scheduleRefresh(task)
if (!task.isSuppressRefresh()) {
localBroadcastManager.broadcastRefresh()
}

@ -8,9 +8,11 @@ import android.util.Log
import androidx.core.app.JobIntentService
import androidx.hilt.work.HiltWorkerFactory
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.coroutineScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.work.Configuration
import com.todoroo.andlib.utility.DateUtilities.now
import com.todoroo.astrid.service.Upgrader
@ -18,10 +20,19 @@ import dagger.Lazy
import dagger.hilt.android.HiltAndroidApp
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.tasks.billing.Inventory
import org.tasks.caldav.CaldavSynchronizer
import org.tasks.data.TaskDao
import org.tasks.date.DateTimeUtils.midnight
import org.tasks.files.FileHelper
import org.tasks.injection.InjectingJobIntentService
import org.tasks.jobs.WorkManager
@ -30,7 +41,6 @@ import org.tasks.opentasks.OpenTaskContentObserver
import org.tasks.preferences.Preferences
import org.tasks.receivers.RefreshReceiver
import org.tasks.scheduling.NotificationSchedulerIntentService
import org.tasks.scheduling.RefreshScheduler
import org.tasks.themes.ThemeBase
import org.tasks.widget.AppWidgetManager
import timber.log.Timber
@ -47,12 +57,13 @@ class Tasks : Application(), Configuration.Provider {
@Inject lateinit var localBroadcastManager: LocalBroadcastManager
@Inject lateinit var upgrader: Lazy<Upgrader>
@Inject lateinit var workManager: Lazy<WorkManager>
@Inject lateinit var refreshScheduler: Lazy<RefreshScheduler>
@Inject lateinit var geofenceApi: Lazy<GeofenceApi>
@Inject lateinit var appWidgetManager: Lazy<AppWidgetManager>
@Inject lateinit var workerFactory: HiltWorkerFactory
@Inject lateinit var contentObserver: Lazy<OpenTaskContentObserver>
@Inject lateinit var taskDao: TaskDao
@OptIn(DelicateCoroutinesApi::class, ExperimentalCoroutinesApi::class)
override fun onCreate() {
super.onCreate()
buildSetup.setup()
@ -61,6 +72,32 @@ class Tasks : Application(), Configuration.Provider {
ThemeBase.getThemeBase(preferences, inventory, null).setDefaultNightMode()
localBroadcastManager.registerRefreshReceiver(RefreshBroadcastReceiver())
backgroundWork()
GlobalScope.launch {
launch {
ProcessLifecycleOwner.get().repeatOnLifecycle(Lifecycle.State.RESUMED) {
val lastRefresh = MutableStateFlow(now())
lastRefresh
.flatMapLatest {
localBroadcastManager.broadcastRefresh()
taskDao.nextRefresh(it)
}
.collect {
delay(it - now())
lastRefresh.update { now() }
}
}
}
launch {
ProcessLifecycleOwner.get().repeatOnLifecycle(Lifecycle.State.RESUMED) {
val midnight = MutableStateFlow(midnight())
midnight.collect {
delay(it - now())
localBroadcastManager.broadcastRefresh()
midnight.update { midnight() }
}
}
}
}
ProcessLifecycleOwner.get().lifecycle.addObserver(
object : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) {
@ -95,15 +132,13 @@ class Tasks : Application(), Configuration.Provider {
private fun backgroundWork() = CoroutineScope(Dispatchers.Default).launch {
inventory.updateTasksAccount()
NotificationSchedulerIntentService.enqueueWork(context)
refreshScheduler.get().scheduleAll()
workManager.get().apply {
updateBackgroundSync()
scheduleMidnightRefresh()
scheduleBackup()
scheduleConfigRefresh()
OpenTaskContentObserver.registerObserver(context, contentObserver.get())
updatePurchases()
}
OpenTaskContentObserver.registerObserver(context, contentObserver.get())
geofenceApi.get().registerAll()
FileHelper.delete(context, preferences.cacheDirectory)
appWidgetManager.get().reconfigureWidgets()

@ -17,6 +17,7 @@ import com.todoroo.astrid.dao.Database
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.data.Task.Companion.NO_ID
import com.todoroo.astrid.helper.UUIDHelper
import kotlinx.coroutines.flow.Flow
import org.tasks.BuildConfig
import org.tasks.data.Alarm.Companion.TYPE_SNOOZE
import org.tasks.db.SuspendDbUtils.chunkedMap
@ -25,11 +26,24 @@ import org.tasks.preferences.QueryPreferences
import org.tasks.time.DateTimeUtils.currentTimeMillis
import timber.log.Timber
private const val MAX_TIME = 9999999999999
@Dao
abstract class TaskDao(private val database: Database) {
@Query("SELECT * FROM tasks WHERE completed = 0 AND deleted = 0 AND (hideUntil > :now OR dueDate > :now)")
internal abstract suspend fun needsRefresh(now: Long = now()): List<Task>
@Query("""
SELECT MIN(min_value)
FROM (
SELECT
MIN(
CASE WHEN dueDate > :now THEN dueDate ELSE $MAX_TIME END,
CASE WHEN hideUntil > :now THEN hideUntil ELSE $MAX_TIME END
) as min_value
FROM tasks
WHERE completed = 0 AND deleted = 0
)
""")
abstract fun nextRefresh(now: Long): Flow<Long>
@Query("SELECT * FROM tasks WHERE _id = :id LIMIT 1")
abstract suspend fun fetch(id: Long): Task?

@ -1,25 +0,0 @@
package org.tasks.jobs
import android.content.Context
import androidx.hilt.work.HiltWorker
import androidx.work.WorkerParameters
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import org.tasks.LocalBroadcastManager
import org.tasks.analytics.Firebase
@HiltWorker
class MidnightRefreshWork @AssistedInject constructor(
@Assisted context: Context,
@Assisted workerParams: WorkerParameters,
firebase: Firebase,
private val workManager: WorkManager,
private val localBroadcastManager: LocalBroadcastManager) : RepeatingWorker(context, workerParams, firebase) {
override suspend fun run(): Result {
localBroadcastManager.broadcastRefresh()
return Result.success()
}
override suspend fun scheduleNext() = workManager.scheduleMidnightRefresh()
}

@ -1,26 +0,0 @@
package org.tasks.jobs
import android.content.Context
import androidx.hilt.work.HiltWorker
import androidx.work.WorkerParameters
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import org.tasks.LocalBroadcastManager
import org.tasks.analytics.Firebase
import org.tasks.scheduling.RefreshScheduler
@HiltWorker
class RefreshWork @AssistedInject constructor(
@Assisted context: Context,
@Assisted workerParams: WorkerParameters,
firebase: Firebase,
private val refreshScheduler: RefreshScheduler,
private val localBroadcastManager: LocalBroadcastManager) : RepeatingWorker(context, workerParams, firebase) {
override suspend fun run(): Result {
localBroadcastManager.broadcastRefresh()
return Result.success()
}
override suspend fun scheduleNext() = refreshScheduler.scheduleNext()
}

@ -20,10 +20,6 @@ interface WorkManager {
fun updateBackgroundSync()
fun scheduleRefresh(time: Long)
fun scheduleMidnightRefresh()
fun scheduleNotification(scheduledTime: Long)
fun scheduleBackup()
@ -39,8 +35,6 @@ interface WorkManager {
companion object {
val REMOTE_CONFIG_INTERVAL_HOURS = if (BuildConfig.DEBUG) 1 else 12.toLong()
const val TAG_BACKUP = "tag_backup"
const val TAG_REFRESH = "tag_refresh"
const val TAG_MIDNIGHT_REFRESH = "tag_midnight_refresh"
const val TAG_SYNC = "tag_sync"
const val TAG_BACKGROUND_SYNC = "tag_background_sync"
const val TAG_REMOTE_CONFIG = "tag_remote_config"

@ -6,9 +6,18 @@ import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.work.*
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.ExistingWorkPolicy.APPEND_OR_REPLACE
import androidx.work.ExistingWorkPolicy.REPLACE
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequest
import androidx.work.PeriodicWorkRequest
import androidx.work.WorkContinuation
import androidx.work.WorkInfo
import androidx.work.WorkRequest
import androidx.work.Worker
import androidx.work.workDataOf
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.andlib.utility.AndroidUtilities.atLeastS
import com.todoroo.andlib.utility.DateUtilities
@ -17,11 +26,14 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.tasks.BuildConfig
import org.tasks.R
import org.tasks.data.*
import org.tasks.data.CaldavAccount
import org.tasks.data.CaldavAccount.Companion.TYPE_CALDAV
import org.tasks.data.CaldavAccount.Companion.TYPE_ETEBASE
import org.tasks.data.CaldavAccount.Companion.TYPE_GOOGLE_TASKS
import org.tasks.data.CaldavAccount.Companion.TYPE_TASKS
import org.tasks.data.CaldavDao
import org.tasks.data.OpenTaskDao
import org.tasks.data.Place
import org.tasks.date.DateTimeUtils.midnight
import org.tasks.date.DateTimeUtils.newDateTime
import org.tasks.jobs.DriveUploader.Companion.EXTRA_PURGE
@ -32,9 +44,7 @@ import org.tasks.jobs.SyncWork.Companion.EXTRA_IMMEDIATE
import org.tasks.jobs.WorkManager.Companion.REMOTE_CONFIG_INTERVAL_HOURS
import org.tasks.jobs.WorkManager.Companion.TAG_BACKGROUND_SYNC
import org.tasks.jobs.WorkManager.Companion.TAG_BACKUP
import org.tasks.jobs.WorkManager.Companion.TAG_MIDNIGHT_REFRESH
import org.tasks.jobs.WorkManager.Companion.TAG_MIGRATE_LOCAL
import org.tasks.jobs.WorkManager.Companion.TAG_REFRESH
import org.tasks.jobs.WorkManager.Companion.TAG_REMOTE_CONFIG
import org.tasks.jobs.WorkManager.Companion.TAG_SYNC
import org.tasks.jobs.WorkManager.Companion.TAG_UPDATE_PURCHASES
@ -42,7 +52,7 @@ import org.tasks.notifications.Throttle
import org.tasks.preferences.Preferences
import org.tasks.time.DateTimeUtils
import timber.log.Timber
import java.util.*
import java.util.Random
import java.util.concurrent.TimeUnit
import kotlin.math.max
@ -126,11 +136,6 @@ class WorkManagerImpl(
}
}
override fun scheduleRefresh(time: Long) = enqueueUnique(TAG_REFRESH, RefreshWork::class.java, time)
override fun scheduleMidnightRefresh() =
enqueueUnique(TAG_MIDNIGHT_REFRESH, MidnightRefreshWork::class.java, midnight())
override fun scheduleNotification(scheduledTime: Long) {
val time = max(DateUtilities.now(), scheduledTime)
if (time < DateTimeUtils.currentTimeMillis()) {

@ -1,56 +0,0 @@
package org.tasks.scheduling
import com.todoroo.astrid.data.Task
import org.tasks.data.TaskDao
import org.tasks.jobs.WorkManager
import org.tasks.time.DateTimeUtils
import java.util.SortedSet
import java.util.TreeSet
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class RefreshScheduler @Inject internal constructor(
private val workManager: WorkManager,
private val taskDao: TaskDao,
) {
private val jobs: SortedSet<Long> = TreeSet()
suspend fun scheduleAll() {
for (task in taskDao.needsRefresh()) {
scheduleRefresh(task)
}
}
@Synchronized
fun scheduleRefresh(task: Task) {
if (task.hasDueDate()) {
scheduleRefresh(task.dueDate)
}
if (task.hasStartDate()) {
scheduleRefresh(task.hideUntil)
}
}
@Synchronized
fun scheduleNext() {
val lapsed = jobs.headSet(DateTimeUtils.currentTimeMillis() + 1).toList()
jobs.removeAll(lapsed)
if (!jobs.isEmpty()) {
workManager.scheduleRefresh(jobs.first())
}
}
private fun scheduleRefresh(timestamp: Long) {
val now = DateTimeUtils.currentTimeMillis()
if (now < timestamp) {
val upcoming = jobs.tailSet(now)
val reschedule = upcoming.isEmpty() || timestamp < upcoming.first()
jobs.add(timestamp)
if (reschedule) {
scheduleNext()
}
}
}
}
Loading…
Cancel
Save