Revert "Replace refresh work with coroutines"

Widgets 😢
pull/2860/head
Alex Baker 5 months ago
parent 7fb85b6da1
commit f84a37a60a

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

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

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

@ -0,0 +1,25 @@
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()
}

@ -0,0 +1,26 @@
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,6 +20,10 @@ interface WorkManager {
fun updateBackgroundSync() fun updateBackgroundSync()
fun scheduleRefresh(time: Long)
fun scheduleMidnightRefresh()
fun scheduleNotification(scheduledTime: Long) fun scheduleNotification(scheduledTime: Long)
fun scheduleBackup() fun scheduleBackup()
@ -35,6 +39,8 @@ interface WorkManager {
companion object { companion object {
val REMOTE_CONFIG_INTERVAL_HOURS = if (BuildConfig.DEBUG) 1 else 12.toLong() val REMOTE_CONFIG_INTERVAL_HOURS = if (BuildConfig.DEBUG) 1 else 12.toLong()
const val TAG_BACKUP = "tag_backup" 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_SYNC = "tag_sync"
const val TAG_BACKGROUND_SYNC = "tag_background_sync" const val TAG_BACKGROUND_SYNC = "tag_background_sync"
const val TAG_REMOTE_CONFIG = "tag_remote_config" const val TAG_REMOTE_CONFIG = "tag_remote_config"

@ -6,18 +6,9 @@ import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import androidx.work.Constraints import androidx.work.*
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.ExistingWorkPolicy.APPEND_OR_REPLACE import androidx.work.ExistingWorkPolicy.APPEND_OR_REPLACE
import androidx.work.ExistingWorkPolicy.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
import com.todoroo.andlib.utility.AndroidUtilities.atLeastS import com.todoroo.andlib.utility.AndroidUtilities.atLeastS
import com.todoroo.andlib.utility.DateUtilities import com.todoroo.andlib.utility.DateUtilities
@ -26,14 +17,11 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.tasks.BuildConfig import org.tasks.BuildConfig
import org.tasks.R import org.tasks.R
import org.tasks.data.CaldavAccount import org.tasks.data.*
import org.tasks.data.CaldavAccount.Companion.TYPE_CALDAV import org.tasks.data.CaldavAccount.Companion.TYPE_CALDAV
import org.tasks.data.CaldavAccount.Companion.TYPE_ETEBASE import org.tasks.data.CaldavAccount.Companion.TYPE_ETEBASE
import org.tasks.data.CaldavAccount.Companion.TYPE_GOOGLE_TASKS import org.tasks.data.CaldavAccount.Companion.TYPE_GOOGLE_TASKS
import org.tasks.data.CaldavAccount.Companion.TYPE_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.midnight
import org.tasks.date.DateTimeUtils.newDateTime import org.tasks.date.DateTimeUtils.newDateTime
import org.tasks.jobs.DriveUploader.Companion.EXTRA_PURGE import org.tasks.jobs.DriveUploader.Companion.EXTRA_PURGE
@ -44,7 +32,9 @@ import org.tasks.jobs.SyncWork.Companion.EXTRA_IMMEDIATE
import org.tasks.jobs.WorkManager.Companion.REMOTE_CONFIG_INTERVAL_HOURS 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_BACKGROUND_SYNC
import org.tasks.jobs.WorkManager.Companion.TAG_BACKUP 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_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_REMOTE_CONFIG
import org.tasks.jobs.WorkManager.Companion.TAG_SYNC import org.tasks.jobs.WorkManager.Companion.TAG_SYNC
import org.tasks.jobs.WorkManager.Companion.TAG_UPDATE_PURCHASES import org.tasks.jobs.WorkManager.Companion.TAG_UPDATE_PURCHASES
@ -52,7 +42,7 @@ import org.tasks.notifications.Throttle
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.time.DateTimeUtils import org.tasks.time.DateTimeUtils
import timber.log.Timber import timber.log.Timber
import java.util.Random import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.math.max import kotlin.math.max
@ -136,6 +126,11 @@ 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) { override fun scheduleNotification(scheduledTime: Long) {
val time = max(DateUtilities.now(), scheduledTime) val time = max(DateUtilities.now(), scheduledTime)
if (time < DateTimeUtils.currentTimeMillis()) { if (time < DateTimeUtils.currentTimeMillis()) {

@ -0,0 +1,56 @@
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