diff --git a/app/src/androidTest/java/com/todoroo/astrid/alarms/AlarmJobServiceTest.kt b/app/src/androidTest/java/com/todoroo/astrid/alarms/AlarmJobServiceTest.kt index 5a3013435..8f99a531a 100644 --- a/app/src/androidTest/java/com/todoroo/astrid/alarms/AlarmJobServiceTest.kt +++ b/app/src/androidTest/java/com/todoroo/astrid/alarms/AlarmJobServiceTest.kt @@ -19,7 +19,6 @@ import org.tasks.date.DateTimeUtils.newDateTime import org.tasks.injection.InjectingTestCase import org.tasks.injection.ProductionModule import org.tasks.jobs.AlarmEntry -import org.tasks.jobs.NotificationQueue import org.tasks.makers.TaskMaker.COMPLETION_TIME import org.tasks.makers.TaskMaker.DELETION_TIME import org.tasks.makers.TaskMaker.DUE_DATE @@ -34,7 +33,6 @@ import javax.inject.Inject class AlarmJobServiceTest : InjectingTestCase() { @Inject lateinit var alarmDao: AlarmDao @Inject lateinit var taskDao: TaskDao - @Inject lateinit var jobs: NotificationQueue @Inject lateinit var alarmService: AlarmService @Test @@ -42,7 +40,7 @@ class AlarmJobServiceTest : InjectingTestCase() { val task = taskDao.createNew(newTask()) val alarm = insertAlarm(Alarm(task, DateTime(2017, 9, 24, 19, 57).millis, TYPE_DATE_TIME)) - verify(AlarmEntry(alarm, task, DateTime(2017, 9, 24, 19, 57).millis, TYPE_DATE_TIME)) + verify(overdue = listOf(AlarmEntry(alarm, task, DateTime(2017, 9, 24, 19, 57).millis, TYPE_DATE_TIME))) } @Test @@ -90,7 +88,7 @@ class AlarmJobServiceTest : InjectingTestCase() { alarmDao.insert(Alarm(task, DateUtilities.ONE_HOUR, TYPE_RANDOM)) val alarm = alarmDao.insert(Alarm(task, now.plusMonths(12).millis, TYPE_SNOOZE)) - verify(AlarmEntry(alarm, task, now.plusMonths(12).millis, TYPE_SNOOZE)) + verify(future = listOf(AlarmEntry(alarm, task, now.plusMonths(12).millis, TYPE_SNOOZE))) } private suspend fun insertAlarm(alarm: Alarm): Long { @@ -98,9 +96,13 @@ class AlarmJobServiceTest : InjectingTestCase() { return alarm.id } - private suspend fun verify(vararg alarms: AlarmEntry) { - alarmService.scheduleAllAlarms() + private suspend fun verify( + overdue: List = emptyList(), + future: List = emptyList(), + ) { + val (actualOverdue, actualFuture) = alarmService.getAlarms() - assertEquals(alarms.toList(), jobs.getJobs()) + assertEquals(overdue, actualOverdue) + assertEquals(future, actualFuture) } } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 64975945c..a4403af77 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -532,10 +532,6 @@ android:value="org.tasks.dashclock.DashClockSettings"/> - - @@ -601,6 +597,8 @@ + + diff --git a/app/src/main/java/com/todoroo/astrid/alarms/AlarmService.kt b/app/src/main/java/com/todoroo/astrid/alarms/AlarmService.kt index 5de053745..56c8dda93 100644 --- a/app/src/main/java/com/todoroo/astrid/alarms/AlarmService.kt +++ b/app/src/main/java/com/todoroo/astrid/alarms/AlarmService.kt @@ -5,14 +5,16 @@ */ package com.todoroo.astrid.alarms -import com.todoroo.astrid.data.Task +import com.todoroo.andlib.utility.DateUtilities import org.tasks.LocalBroadcastManager import org.tasks.data.Alarm import org.tasks.data.Alarm.Companion.TYPE_SNOOZE import org.tasks.data.AlarmDao import org.tasks.data.TaskDao -import org.tasks.jobs.NotificationQueue +import org.tasks.jobs.AlarmEntry +import org.tasks.jobs.WorkManager import org.tasks.notifications.NotificationManager +import timber.log.Timber import javax.inject.Inject /** @@ -22,10 +24,10 @@ import javax.inject.Inject */ class AlarmService @Inject constructor( private val alarmDao: AlarmDao, - private val jobs: NotificationQueue, private val taskDao: TaskDao, private val localBroadcastManager: LocalBroadcastManager, private val notificationManager: NotificationManager, + private val workManager: WorkManager, private val alarmCalculator: AlarmCalculator, ) { suspend fun getAlarms(taskId: Long): List = alarmDao.getAlarms(taskId) @@ -36,7 +38,6 @@ class AlarmService @Inject constructor( * @return true if data was changed */ suspend fun synchronizeAlarms(taskId: Long, alarms: MutableSet): Boolean { - val task = taskDao.fetch(taskId) ?: return false var changed = false for (existing in alarmDao.getAlarms(taskId)) { if (!alarms.removeIf { @@ -55,51 +56,42 @@ class AlarmService @Inject constructor( changed = true } if (changed) { - scheduleAlarms(task) + workManager.triggerNotifications() localBroadcastManager.broadcastRefreshList() } return changed } - suspend fun scheduleAllAlarms() { - alarmDao - .getActiveAlarms() - .groupBy { it.task } - .forEach { (taskId, alarms) -> - val task = taskDao.fetch(taskId) ?: return@forEach - scheduleAlarms(task, alarms) - } - } - - fun cancelAlarms(taskId: Long) { - jobs.cancelForTask(taskId) - } - suspend fun snooze(time: Long, taskIds: List) { notificationManager.cancel(taskIds) alarmDao.getSnoozed(taskIds).let { alarmDao.delete(it) } taskIds.map { Alarm(it, time, TYPE_SNOOZE) }.let { alarmDao.insert(it) } taskDao.touch(taskIds) - scheduleAlarms(taskIds) + workManager.triggerNotifications() } - suspend fun scheduleAlarms(taskIds: List) { - taskDao.fetch(taskIds).forEach { scheduleAlarms(it) } - } - - /** Schedules alarms for a single task */ - suspend fun scheduleAlarms(task: Task) { - scheduleAlarms(task, alarmDao.getActiveAlarms(task.id)) - } - - private fun scheduleAlarms(task: Task, alarms: List) { - jobs.cancelForTask(task.id) - val alarmEntries = alarms.mapNotNull { - alarmCalculator.toAlarmEntry(task, it) - } - val next = - alarmEntries.find { it.type == TYPE_SNOOZE } ?: alarmEntries.minByOrNull { it.time } - next?.let { jobs.add(it) } + suspend fun getAlarms(): Pair, List> { + val start = DateUtilities.now() + val overdue = ArrayList() + val future = ArrayList() + alarmDao.getActiveAlarms() + .groupBy { it.task } + .forEach { (taskId, alarms) -> + val task = taskDao.fetch(taskId) ?: return@forEach + val alarmEntries = alarms.mapNotNull { + alarmCalculator.toAlarmEntry(task, it) + } + val (now, later) = alarmEntries.partition { it.time <= DateUtilities.now() } + later + .find { it.type == TYPE_SNOOZE } + ?.let { future.add(it) } + ?: run { + now.firstOrNull()?.let { overdue.add(it) } + later.minByOrNull { it.time }?.let { future.add(it) } + } + } + Timber.d("took ${DateUtilities.now() - start}ms overdue=${overdue.size} future=${future.size}") + return overdue to future } companion object { diff --git a/app/src/main/java/com/todoroo/astrid/dao/TaskDao.kt b/app/src/main/java/com/todoroo/astrid/dao/TaskDao.kt index 1d69edd05..683a63d40 100644 --- a/app/src/main/java/com/todoroo/astrid/dao/TaskDao.kt +++ b/app/src/main/java/com/todoroo/astrid/dao/TaskDao.kt @@ -5,7 +5,6 @@ */ package com.todoroo.astrid.dao -import com.todoroo.astrid.alarms.AlarmService import com.todoroo.astrid.api.Filter import com.todoroo.astrid.data.Task import com.todoroo.astrid.timers.TimerPlugin @@ -28,7 +27,6 @@ class TaskDao @Inject constructor( private val geofenceApi: GeofenceApi, private val timerPlugin: TimerPlugin, private val syncAdapters: SyncAdapters, - private val alarmService: AlarmService, private val workManager: WorkManager, ) { @@ -123,7 +121,7 @@ class TaskDao @Inject constructor( if (completionDateModified || deletionDateModified) { geofenceApi.update(task.id) } - alarmService.scheduleAlarms(task) + workManager.triggerNotifications() workManager.scheduleRefresh() if (!task.isSuppressRefresh()) { localBroadcastManager.broadcastRefresh() diff --git a/app/src/main/java/com/todoroo/astrid/service/TaskDeleter.kt b/app/src/main/java/com/todoroo/astrid/service/TaskDeleter.kt index 5a271fd83..72d666bf8 100644 --- a/app/src/main/java/com/todoroo/astrid/service/TaskDeleter.kt +++ b/app/src/main/java/com/todoroo/astrid/service/TaskDeleter.kt @@ -2,7 +2,6 @@ package com.todoroo.astrid.service import android.content.Context import androidx.room.withTransaction -import com.todoroo.astrid.alarms.AlarmService import com.todoroo.astrid.dao.Database import com.todoroo.astrid.data.Task import com.todoroo.astrid.timers.TimerPlugin @@ -22,7 +21,6 @@ import org.tasks.db.SuspendDbUtils.chunkedMap import org.tasks.files.FileHelper import org.tasks.location.GeofenceApi import org.tasks.notifications.NotificationManager -import org.tasks.preferences.Preferences import org.tasks.sync.SyncAdapters import javax.inject.Inject @@ -32,13 +30,11 @@ class TaskDeleter @Inject constructor( private val deletionDao: DeletionDao, private val taskDao: TaskDao, private val localBroadcastManager: LocalBroadcastManager, - private val preferences: Preferences, private val syncAdapters: SyncAdapters, private val vtodoCache: VtodoCache, private val notificationManager: NotificationManager, private val geofenceApi: GeofenceApi, private val timerPlugin: TimerPlugin, - private val alarmService: AlarmService, private val userActivityDao: UserActivityDao, private val locationDao: LocationDao, ) { @@ -96,7 +92,6 @@ class TaskDeleter @Inject constructor( throw IllegalStateException() } tasks.forEach { task -> - alarmService.cancelAlarms(task) notificationManager.cancel(task) locationDao.getGeofencesForTask(task).forEach { locationDao.delete(it) diff --git a/app/src/main/java/org/tasks/Notifier.kt b/app/src/main/java/org/tasks/Notifier.kt index ce85a88ae..91a1d666f 100644 --- a/app/src/main/java/org/tasks/Notifier.kt +++ b/app/src/main/java/org/tasks/Notifier.kt @@ -100,7 +100,6 @@ class Notifier @Inject constructor( } ?: false } - .takeLast(NotificationManager.MAX_NOTIFICATIONS) if (notifications.isEmpty()) { return diff --git a/app/src/main/java/org/tasks/injection/InjectingService.kt b/app/src/main/java/org/tasks/injection/InjectingService.kt deleted file mode 100644 index d6f67bf02..000000000 --- a/app/src/main/java/org/tasks/injection/InjectingService.kt +++ /dev/null @@ -1,73 +0,0 @@ -package org.tasks.injection - -import android.app.Notification -import android.app.Service -import android.content.Intent -import androidx.core.app.NotificationCompat -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.launch -import org.tasks.R -import org.tasks.analytics.Firebase -import org.tasks.notifications.NotificationManager -import javax.inject.Inject - -abstract class InjectingService : Service() { - private val job = SupervisorJob() - private val scope = CoroutineScope(Dispatchers.Default + job) - - @Inject lateinit var firebase: Firebase - - override fun onCreate() { - super.onCreate() - startForeground() - } - - override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { - startForeground() - scope.launch { - try { - doWork() - } catch (e: Exception) { - firebase.reportException(e) - } finally { - done(startId) - } - } - return START_NOT_STICKY - } - - private fun done(startId: Int) { - scheduleNext() - stopSelf(startId) - } - - override fun onDestroy() { - super.onDestroy() - stopForeground(true) - job.cancel() - } - - private fun startForeground() { - startForeground(notificationId, buildNotification()) - } - - protected abstract val notificationId: Int - protected abstract val notificationBody: Int - - private fun buildNotification(): Notification { - return NotificationCompat.Builder( - this, NotificationManager.NOTIFICATION_CHANNEL_MISCELLANEOUS) - .setSound(null) - .setPriority(NotificationCompat.PRIORITY_LOW) - .setSmallIcon(R.drawable.ic_check_white_24dp) - .setContentTitle(getString(R.string.app_name)) - .setContentText(getString(notificationBody)) - .build() - } - - protected open fun scheduleNext() {} - - protected abstract suspend fun doWork() -} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/injection/ProductionModule.kt b/app/src/main/java/org/tasks/injection/ProductionModule.kt index b28b93847..17deb610c 100644 --- a/app/src/main/java/org/tasks/injection/ProductionModule.kt +++ b/app/src/main/java/org/tasks/injection/ProductionModule.kt @@ -13,7 +13,6 @@ import org.tasks.R import org.tasks.caldav.FileStorage import org.tasks.data.CaldavDao import org.tasks.data.OpenTaskDao -import org.tasks.data.TaskDao import org.tasks.db.Migrations import org.tasks.jobs.WorkManager import org.tasks.jobs.WorkManagerImpl @@ -53,6 +52,5 @@ internal class ProductionModule { preferences: Preferences, caldavDao: CaldavDao, openTaskDao: OpenTaskDao, - taskDao: TaskDao, - ): WorkManager = WorkManagerImpl(context, preferences, caldavDao, openTaskDao, taskDao) + ): WorkManager = WorkManagerImpl(context, preferences, caldavDao, openTaskDao) } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/jobs/NotificationQueue.kt b/app/src/main/java/org/tasks/jobs/NotificationQueue.kt deleted file mode 100644 index 42efa4c34..000000000 --- a/app/src/main/java/org/tasks/jobs/NotificationQueue.kt +++ /dev/null @@ -1,90 +0,0 @@ -package org.tasks.jobs - -import com.google.common.collect.Ordering -import com.google.common.collect.TreeMultimap -import com.google.common.primitives.Ints -import org.tasks.preferences.Preferences -import org.tasks.time.DateTime -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class NotificationQueue @Inject constructor( - private val preferences: Preferences, - private val workManager: WorkManager -) { - private val jobs = - TreeMultimap.create(Ordering.natural()) { l, r -> - Ints.compare(l.hashCode(), r.hashCode()) - } - - @Synchronized - fun add(entry: AlarmEntry) = add(listOf(entry)) - - @Synchronized - fun add(entries: Iterable) { - val originalFirstTime = firstTime() - entries.forEach { jobs.put(it.time, it) } - if (originalFirstTime != firstTime()) { - scheduleNext(true) - } - } - - @Synchronized - fun clear() { - jobs.clear() - workManager.cancelNotifications() - } - - fun cancelForTask(taskId: Long) { - val firstTime = firstTime() - jobs.values().filter { it.taskId == taskId }.forEach { remove(listOf(it)) } - if (firstTime != firstTime()) { - scheduleNext(true) - } - } - - @get:Synchronized - val overdueJobs: List - get() = jobs.keySet() - .headSet(DateTime().startOfMinute().plusMinutes(1).millis) - .flatMap { jobs[it] } - - @Synchronized - fun scheduleNext() = scheduleNext(false) - - private fun scheduleNext(cancelCurrent: Boolean) { - if (jobs.isEmpty) { - if (cancelCurrent) { - workManager.cancelNotifications() - } - } else { - workManager.scheduleNotification(nextScheduledTime()) - } - } - - private fun firstTime() = if (jobs.isEmpty) 0L else jobs.asMap().firstKey() - - fun nextScheduledTime(): Long { - val next = firstTime() - return if (next > 0) preferences.adjustForQuietHours(next) else 0 - } - - fun size() = jobs.size() - - fun getJobs() = jobs.values().toList() - - fun isEmpty() = jobs.isEmpty - - @Synchronized - fun remove(entries: List): Boolean { - var success = true - for (entry in entries) { - success = success and (!jobs.containsEntry(entry.time, entry) || jobs.remove( - entry.time, - entry - )) - } - return success - } -} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/jobs/NotificationReceiver.kt b/app/src/main/java/org/tasks/jobs/NotificationReceiver.kt new file mode 100644 index 000000000..2615f170c --- /dev/null +++ b/app/src/main/java/org/tasks/jobs/NotificationReceiver.kt @@ -0,0 +1,16 @@ +package org.tasks.jobs + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject + +@AndroidEntryPoint +class NotificationReceiver: BroadcastReceiver() { + @Inject lateinit var workManager: WorkManager + + override fun onReceive(context: Context?, intent: Intent?) { + workManager.triggerNotifications(expedited = true) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/jobs/NotificationService.kt b/app/src/main/java/org/tasks/jobs/NotificationService.kt deleted file mode 100644 index 2bf5e9452..000000000 --- a/app/src/main/java/org/tasks/jobs/NotificationService.kt +++ /dev/null @@ -1,50 +0,0 @@ -package org.tasks.jobs - -import android.content.Intent -import android.os.IBinder -import com.todoroo.andlib.utility.AndroidUtilities -import com.todoroo.astrid.alarms.AlarmService -import dagger.hilt.android.AndroidEntryPoint -import org.tasks.Notifier -import org.tasks.R -import org.tasks.data.Alarm.Companion.TYPE_SNOOZE -import org.tasks.data.AlarmDao -import org.tasks.injection.InjectingService -import org.tasks.preferences.Preferences -import javax.inject.Inject - -@AndroidEntryPoint -class NotificationService : InjectingService() { - @Inject lateinit var preferences: Preferences - @Inject lateinit var notifier: Notifier - @Inject lateinit var notificationQueue: NotificationQueue - @Inject lateinit var alarmDao: AlarmDao - @Inject lateinit var alarmService: AlarmService - - override fun onBind(intent: Intent): IBinder? = null - - override val notificationId = -1 - - override val notificationBody = R.string.building_notifications - - override suspend fun doWork() { - AndroidUtilities.assertNotMainThread() - if (!preferences.isCurrentlyQuietHours) { - val overdueJobs = notificationQueue.overdueJobs - if (!notificationQueue.remove(overdueJobs)) { - throw RuntimeException("Failed to remove jobs from queue") - } - notifier.triggerNotifications(overdueJobs.map { it.toNotification() }) - overdueJobs - .filter { it.type == TYPE_SNOOZE } - .takeIf { it.isNotEmpty() } - ?.map { it.id } - ?.let { alarmDao.deleteByIds(it) } - overdueJobs - .map { it.taskId } - .let { alarmService.scheduleAlarms(it) } - } - } - - override fun scheduleNext() = notificationQueue.scheduleNext() -} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/jobs/NotificationWork.kt b/app/src/main/java/org/tasks/jobs/NotificationWork.kt new file mode 100644 index 000000000..66177f287 --- /dev/null +++ b/app/src/main/java/org/tasks/jobs/NotificationWork.kt @@ -0,0 +1,64 @@ +package org.tasks.jobs + +import android.content.Context +import androidx.hilt.work.HiltWorker +import androidx.work.WorkerParameters +import com.todoroo.andlib.utility.DateUtilities.now +import com.todoroo.astrid.alarms.AlarmService +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import org.tasks.Notifier +import org.tasks.analytics.Firebase +import org.tasks.data.Alarm.Companion.TYPE_SNOOZE +import org.tasks.data.AlarmDao +import org.tasks.date.DateTimeUtils.toDateTime +import org.tasks.preferences.Preferences +import timber.log.Timber + +@HiltWorker +class NotificationWork @AssistedInject constructor( + @Assisted context: Context, + @Assisted workerParams: WorkerParameters, + firebase: Firebase, + private val workManager: WorkManager, + private val alarmService: AlarmService, + private val alarmDao: AlarmDao, + private val preferences: Preferences, + private val notifier: Notifier, +) : RepeatingWorker(context, workerParams, firebase) { + private var nextAlarm: Long = 0 + + override suspend fun run(): Result { + if (preferences.isCurrentlyQuietHours) { + nextAlarm = preferences.adjustForQuietHours(now()) + return Result.success() + } + repeat(3) { + val (overdue, future) = alarmService.getAlarms() + if (overdue.isNotEmpty()) { + overdue + .sortedBy { it.time } + .also { alarms -> + alarms + .filter { it.type == TYPE_SNOOZE } + .map { it.id } + .let { alarmDao.deleteByIds(it) } + } + .map { it.toNotification() } + .let { notifier.triggerNotifications(it) } + } else { + nextAlarm = future.minOfOrNull { it.time } ?: 0 + Timber.d("nextAlarm=${nextAlarm.toDateTime()}") + return Result.success() + } + } + firebase.reportException(IllegalStateException("Should have returned already")) + return Result.failure() + } + + override suspend fun scheduleNext() { + if (nextAlarm > 0) { + workManager.scheduleNotification(preferences.adjustForQuietHours(nextAlarm)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/jobs/WorkManager.kt b/app/src/main/java/org/tasks/jobs/WorkManager.kt index 0a41e1cbf..e3b0ab71b 100644 --- a/app/src/main/java/org/tasks/jobs/WorkManager.kt +++ b/app/src/main/java/org/tasks/jobs/WorkManager.kt @@ -23,6 +23,8 @@ interface WorkManager { suspend fun scheduleRefresh(timestamp: Long = now() + 5_000) + fun triggerNotifications(expedited: Boolean = false) + fun scheduleNotification(scheduledTime: Long) fun scheduleBackup() @@ -31,8 +33,6 @@ interface WorkManager { fun scheduleDriveUpload(uri: Uri, purge: Boolean) - fun cancelNotifications() - fun updatePurchases() companion object { @@ -44,5 +44,6 @@ interface WorkManager { const val TAG_REMOTE_CONFIG = "tag_remote_config" const val TAG_MIGRATE_LOCAL = "tag_migrate_local" const val TAG_UPDATE_PURCHASES = "tag_update_purchases" + const val TAG_NOTIFICATIONS = "tag_notifications" } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/jobs/WorkManagerImpl.kt b/app/src/main/java/org/tasks/jobs/WorkManagerImpl.kt index 72ee9329c..696561320 100644 --- a/app/src/main/java/org/tasks/jobs/WorkManagerImpl.kt +++ b/app/src/main/java/org/tasks/jobs/WorkManagerImpl.kt @@ -12,6 +12,7 @@ import androidx.work.ExistingWorkPolicy.APPEND_OR_REPLACE import androidx.work.ExistingWorkPolicy.REPLACE import androidx.work.NetworkType import androidx.work.OneTimeWorkRequest +import androidx.work.OutOfQuotaPolicy import androidx.work.PeriodicWorkRequest import androidx.work.WorkContinuation import androidx.work.WorkInfo @@ -20,7 +21,7 @@ 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 +import com.todoroo.andlib.utility.DateUtilities.now import com.todoroo.astrid.data.Task import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -34,7 +35,6 @@ 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.data.TaskDao import org.tasks.date.DateTimeUtils.midnight import org.tasks.date.DateTimeUtils.newDateTime import org.tasks.jobs.DriveUploader.Companion.EXTRA_PURGE @@ -46,6 +46,7 @@ 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_MIGRATE_LOCAL +import org.tasks.jobs.WorkManager.Companion.TAG_NOTIFICATIONS 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 @@ -63,7 +64,6 @@ class WorkManagerImpl( private val preferences: Preferences, private val caldavDao: CaldavDao, private val openTaskDao: OpenTaskDao, - private val taskDao: TaskDao, ): WorkManager { private val throttle = Throttle(200, 60000, "WORK") private val alarmManager: AlarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager @@ -142,9 +142,19 @@ class WorkManagerImpl( override suspend fun scheduleRefresh(timestamp: Long) = enqueueUnique(TAG_REFRESH, RefreshWork::class.java, timestamp) + override fun triggerNotifications(expedited: Boolean) { + enqueueUnique( + TAG_NOTIFICATIONS, + NotificationWork::class.java, + time = if (expedited) 0 else now() + 5_000, + expedited = expedited, + ) + } + override fun scheduleNotification(scheduledTime: Long) { - val time = max(DateUtilities.now(), scheduledTime) + val time = max(now(), scheduledTime) if (time < DateTimeUtils.currentTimeMillis()) { + val intent = notificationIntent if (AndroidUtilities.atLeastOreo()) { context.startForegroundService(intent) @@ -199,21 +209,25 @@ class WorkManagerImpl( private val networkConstraints: Constraints get() = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build() - override fun cancelNotifications() { - alarmManager.cancel(notificationPendingIntent) - } - override fun updatePurchases() = enqueueUnique(TAG_UPDATE_PURCHASES, UpdatePurchaseWork::class.java) @SuppressLint("EnqueueWork") - private fun enqueueUnique(key: String, c: Class, time: Long = 0) { - val delay = time - DateUtilities.now() + private fun enqueueUnique( + key: String, + c: Class, + time: Long = 0, + expedited: Boolean = false, + ) { + val delay = time - now() val builder = OneTimeWorkRequest.Builder(c) if (delay > 0) { builder.setInitialDelay(delay, TimeUnit.MILLISECONDS) } - Timber.d("$key: ${DateTimeUtils.printTimestamp(time)} (${DateTimeUtils.printDuration(delay)})") + if (expedited) { + builder.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) + } + Timber.d("$key: ${DateTimeUtils.printTimestamp(delay)} (${DateTimeUtils.printDuration(delay)})") enqueue(workManager.beginUniqueWork(key, REPLACE, builder.build())) } @@ -230,26 +244,15 @@ class WorkManagerImpl( } private val notificationIntent: Intent - get() = Intent(context, NotificationService::class.java) + get() = Intent(context, NotificationReceiver::class.java) private val notificationPendingIntent: PendingIntent - get() { - return if (AndroidUtilities.atLeastOreo()) { - PendingIntent.getForegroundService( - context, - 0, - notificationIntent, - PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT - ) - } else { - PendingIntent.getService( - context, - 0, - notificationIntent, - PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT - ) - } - } + get() = PendingIntent.getBroadcast( + context, + 0, + notificationIntent, + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + ) private suspend fun getSyncJob() = withContext(Dispatchers.IO) { workManager.getWorkInfosForUniqueWork(TAG_SYNC).get() diff --git a/app/src/main/java/org/tasks/notifications/NotificationManager.kt b/app/src/main/java/org/tasks/notifications/NotificationManager.kt index 4ac221425..67a695422 100644 --- a/app/src/main/java/org/tasks/notifications/NotificationManager.kt +++ b/app/src/main/java/org/tasks/notifications/NotificationManager.kt @@ -55,12 +55,14 @@ class NotificationManager @Inject constructor( } @SuppressLint("CheckResult") - suspend fun cancel(ids: Iterable) { + suspend fun cancel(ids: Iterable, delete: Boolean = true) { for (id in ids) { notificationManager.cancel(id.toInt()) } queue.remove(ids) - notificationDao.deleteAll(ids.toList()) + if (delete) { + notificationDao.deleteAll(ids.toList()) + } notifyTasks(emptyList(), alert = false, nonstop = false, fiveTimes = false) } @@ -210,7 +212,7 @@ class NotificationManager @Inject constructor( ) val evicted = queue.add(notificationId) if (evicted.size > 0) { - cancel(evicted) + cancel(evicted, delete = false) } for (i in 0 until ringTimes) { if (i > 0) { @@ -403,7 +405,6 @@ class NotificationManager @Inject constructor( const val NOTIFICATION_CHANNEL_DEFAULT = "notifications" const val NOTIFICATION_CHANNEL_TASKER = "notifications_tasker" const val NOTIFICATION_CHANNEL_TIMERS = "notifications_timers" - const val NOTIFICATION_CHANNEL_MISCELLANEOUS = "notifications_miscellaneous" const val MAX_NOTIFICATIONS = 21 const val EXTRA_NOTIFICATION_ID = "extra_notification_id" const val SUMMARY_NOTIFICATION_ID = 0 diff --git a/app/src/main/java/org/tasks/scheduling/NotificationSchedulerIntentService.kt b/app/src/main/java/org/tasks/scheduling/NotificationSchedulerIntentService.kt index a9ce22cd2..164b335ec 100644 --- a/app/src/main/java/org/tasks/scheduling/NotificationSchedulerIntentService.kt +++ b/app/src/main/java/org/tasks/scheduling/NotificationSchedulerIntentService.kt @@ -6,12 +6,11 @@ import android.content.Context import android.content.Intent import android.os.Build import com.todoroo.andlib.utility.AndroidUtilities -import com.todoroo.astrid.alarms.AlarmService import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.qualifiers.ApplicationContext import org.tasks.R import org.tasks.injection.InjectingJobIntentService -import org.tasks.jobs.NotificationQueue +import org.tasks.jobs.WorkManager import org.tasks.notifications.NotificationManager import timber.log.Timber import javax.inject.Inject @@ -19,17 +18,15 @@ import javax.inject.Inject @AndroidEntryPoint class NotificationSchedulerIntentService : InjectingJobIntentService() { @Inject @ApplicationContext lateinit var context: Context - @Inject lateinit var alarmService: AlarmService - @Inject lateinit var notificationQueue: NotificationQueue @Inject lateinit var notificationManager: NotificationManager + @Inject lateinit var workManager: WorkManager override suspend fun doWork(intent: Intent) { Timber.d("onHandleWork(%s)", intent) createNotificationChannels() - notificationQueue.clear() val cancelExistingNotifications = intent.getBooleanExtra(EXTRA_CANCEL_EXISTING_NOTIFICATIONS, false) notificationManager.restoreNotifications(cancelExistingNotifications) - alarmService.scheduleAllAlarms() + workManager.triggerNotifications() } private fun createNotificationChannels() { @@ -42,9 +39,6 @@ class NotificationSchedulerIntentService : InjectingJobIntentService() { notificationManager.createNotificationChannel( createNotificationChannel( NotificationManager.NOTIFICATION_CHANNEL_TIMERS, R.string.TEA_timer_controls, true)) - notificationManager.createNotificationChannel( - createNotificationChannel( - NotificationManager.NOTIFICATION_CHANNEL_MISCELLANEOUS, R.string.miscellaneous, false)) } } diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index bbb78bc4f..1f79582e4 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -157,7 +157,6 @@ أضف حساب إشعار LED عرض الإشعارات على الساعة الذكية - توليد الإخطارات ادمج عدة إشعارات في شعار واحد انقر هنا إذا كنت تواجه مشاكل في الإشعارات الموقع @@ -413,7 +412,6 @@ الرابط مطلوب %s م التباعد - متنوّع علامة: \? الأولويّة على الأقل \? ضوابط المؤقت diff --git a/app/src/main/res/values-bg-rBG/strings.xml b/app/src/main/res/values-bg-rBG/strings.xml index db827fd59..5d9e12a75 100644 --- a/app/src/main/res/values-bg-rBG/strings.xml +++ b/app/src/main/res/values-bg-rBG/strings.xml @@ -237,7 +237,6 @@ Папка за прикачени файлове Папка за резервни копия Резервно копие в Google Drive - Разни Включено Размер на шрифта Междуредие @@ -385,7 +384,6 @@ Отваряне на страницата Пристигнахте в %s Отпътувахте от %s - Създаване на известия Изберете местоположение Изберете това местоположение Или изберете местоположение diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index d212f3903..92e9d2c6e 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -139,7 +139,6 @@ Vibrace Období nerušení Složka pro zálohy - Různé Povoleno Velikost písma Zdrojové kódy @@ -492,7 +491,6 @@ Více barev Neplatné uživatelské jméno nebo heslo Synchronizujte své úkoly pomocí aplikace DAVx⁵ - Vytvářejí se oznámení Rozumím! Odemkněte si další funkce a podpořte software s otevřeným zdrojovým kódem Líbí se vám Tasks\? diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 69f76aa9a..4fe4a8a8f 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -182,7 +182,6 @@ Eller vælg et sted Vælg dette sted Vælg sted - Genererer notifikationer Afgået fra %s Ankommet til %s Besøg hjemmeside @@ -330,7 +329,6 @@ Skriftstørrelse Aktiveret Underopgaver - Diverse Google Drive-backup Mappe til sikkerhedskopi Mappe til vedhæftede filer diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 6acdfd100..2faf01bf5 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -230,7 +230,6 @@ Ordner für Anhänge Sicherungsordner Google Drive-Sicherung - Verschiedenes Aktiviert Schriftgröße Zeilenabstand @@ -376,7 +375,6 @@ Website öffnen Angekommen um %s Abgereist um %s - Benachrichtigungen generieren Ort auswählen Diesen Ort auswählen Oder Ort auswählen diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index 4f6a4c82f..09cbf2e36 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -265,7 +265,6 @@ Intervica spaco Interspaco Ŝaltita - Diversaj Dosierujo de savkopioj Silentaj horoj Vibradoj @@ -474,7 +473,6 @@ Helpo Pri Entajpu etikedonomon - Generas sciigojn Novaĵoj Krei novan taskon Vidigi priskribon diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index df646c108..3c465405c 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -246,7 +246,6 @@ Carpeta de datos adjuntos Carpeta de copias de seguridad Copia de seguridad en Google Drive - Miscelánea Activado Tamaño de letra Espaciado de fila @@ -393,7 +392,6 @@ Visitar página web Llegada a %s Salida de %s - Generando notificaciones Escoger ubicación Seleccionar esta ubicación O elegir una ubicación diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 3326f7009..a553c4893 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -136,7 +136,6 @@ Failide kaust Varukoopia kaust Kopeeri Google Drive-i - Varia Sisse lülitatud Teksti suurus Reavahed diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 6967aa2e4..4c6c25d8a 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -233,7 +233,6 @@ Eranskinen karpeta Babes-kopien karpeta Google Drive babeskopia - Denetarik Gaituta Letraren tamaina Errenkaden banatze espazioa @@ -384,7 +383,6 @@ Bisitatu webgunea %s(e)tan helduta %s(e)tan aterata - Jakinarazpenak sortzen Hautatu kokaleku bat Hautatu kokaleku hau Edo hautatu kokaleku bat diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 29cf7a7a1..9e6f67d68 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -149,7 +149,6 @@ ساعات سکوت پوشه پیوست پشتیبان گیری از پوشه - سایر فعال شده اندازه فونت فاصله سطرها diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index c70e4aab6..a3e0a0ec0 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -222,7 +222,6 @@ Hiljainen aika Liitehakemisto Varmuuskopioiden hakemisto - Sekalaista Sallittu Kirjasimen koko Rivinväli @@ -379,7 +378,6 @@ Tai valitse muu sijainti Valitse tämä sijainti Valitse sijainti - Ilmoitusten muodostaminen Käy verkkosivulla Muistuta lähtiessä Muistuta saapuessa diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index c12f1cf0a..16d17dc36 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -232,7 +232,6 @@ Dossier des pièces jointes Dossier de sauvegarde Sauvegarde « Google Drive » - Divers Activé Taille de police Espacement des lignes @@ -378,7 +377,6 @@ Visiter le site web Arrivé à %s Parti %s - Générer des notifications Choisir un emplacement Sélectionner cet emplacement Ou choisir un emplacement diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 4e89b3d70..5da0e9cb2 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -162,7 +162,6 @@ Horario silencioso Cartafol de anexos Cartafol de copias de seguranza - Miscelánea Activado Tamaño da fonte Espazado de fila @@ -343,7 +342,6 @@ Visitar a páxina web Chegouse ás %s Saiuse ás %s - Xerando as notificacións Escolle a posición Escoller esta posición Ou escolle unha posición diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index f59a4a917..b02a30938 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -284,7 +284,6 @@ Ili odaberi jednu lokaciju Odaberi ovu lokaciju Odaberi jednu lokaciju - Generiranje obavijesti Otišao/la %s Stigao/la %s Dodaj poveznice na web-stranice, adrese i telefonske brojeve @@ -425,7 +424,6 @@ Doprinesi prijevodima Razmak između redaka Razmak - Razno Sigurnosna kopija na Google Drive Mapa sigurnosnih kopija Mapa priloga diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 122956e34..e4410bc8f 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -230,7 +230,6 @@ Csatolmányok mappája Mentési mappa Mentés Google Drive-ba - Egyéb Engedélyezve Karakter méret Sorköz @@ -379,7 +378,6 @@ Weboldal megnyitása Megérkezett ekkor: %s Elindult ekkor: %s - Értesítések létrehozása Hely kiválasztása Hely kiválasztása Vagy másik hely keresése diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml index 7047f7d44..b60f0f4ef 100644 --- a/app/src/main/res/values-id/strings.xml +++ b/app/src/main/res/values-id/strings.xml @@ -167,7 +167,6 @@ Direktori isi lampiran Direktori cadangan Cadangan Google Drive - Lain-lain Subtugas Aktif Ukuran huruf @@ -376,7 +375,6 @@ Sinkronkan tugas Anda dengan aplikasi DAVx⁵ Apa yang Baru Perizinan hilang - Menghasilkan notifikasi Ingatkan pada saat keberangkatan Ingatkan saat kedatangan Tugas Baru diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index bf9a7334a..6f879d632 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -247,7 +247,6 @@ Cartella degli allegati Cartella di backup Backup su Google Drive - Varie Attiva Dimensione carattere Interlinea @@ -394,7 +393,6 @@ Visita il sito web Arrivato alle %s Partito alle %s - Gestione notifiche Scegli una posizione Seleziona questa posizione O scegli una posizione diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index f2eeb1b5b..941d40c97 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -263,7 +263,6 @@ תיקיית קבצים מצורפים תיקיית גיבוי גיבוי Google Drive - שונות אפשר גודל גופן מרווח שורה @@ -413,7 +412,6 @@ מעבר לאתר האינטרנט הגיע ב-%s עזב ב-%s - מייצר התראות בחרו מיקום בחרו את המיקום הזה או בחרו מיקום diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 45b4f805e..0a33def09 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -234,7 +234,6 @@ 添付フォルダー フォルダーをバックアップ Google ドライブにバックアップ - その他 有効 フォントサイズ 行間隔 @@ -382,7 +381,6 @@ Webサイトを参照 %s に到着 %s に出発 - 通知を生成しています 場所を選択 この場所を選択 または場所を選ぶ diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index faa233b1b..3308ea1c1 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -231,7 +231,6 @@ 무음 시간대 첨부파일 위치 백업 위치 - 기타 활성화됨 글자 크기 줄 간격 @@ -489,7 +488,6 @@ 할일 목록 일시 선택상자 닫기 새 기능 - 알림 생성하기 제목 다음 새 할일 숨기기 diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 2d7c6f22a..89efe027e 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -252,7 +252,6 @@ Pridedamo failo aplankas Atsarginių kopijų aplankas Google Drive atsarginė kopija - Įvairūs Įjungta Šrifto dydis Eilutės plotis @@ -400,7 +399,6 @@ Aplankyti svetainę Atvykus į %s Išvykus iš %s - Generuojami pranešimai Pasirinkti vietą Pasirinkti šią vietą Arba pasirinkti vietą diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index f780911b1..f6211a020 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -267,7 +267,6 @@ Vedleggsmappe Sikkerhetskopimappe Google Drive-sikkerhetskopi - Ymse Påskrudd Skriftstørrelse Tilpass redigeringsskjerm @@ -384,7 +383,6 @@ Besøk nettside Ankom %s Forlot %s - Oppretter merknader Velg et sted Velg dette stedet Eller velg et sted diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index fb4bfbbe0..8ed1318f7 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -230,7 +230,6 @@ Rusttijd Map voor bijlagen Backup naar Google Drive - Diversen Actief Lettergrootte Regelafstand @@ -373,7 +372,6 @@ Website bezoeken Aangekomen op %s Vertrokken van %s - Meldingen aanmaken Locatie kiezen Locatie onder naald kiezen Of kies een locatie diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 5ec3e5efa..9c63abf55 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -242,7 +242,6 @@ Katalog załączników Katalog kopii zapasowych Kopia zapasowa na Dysku Google - Różne Włączone Rozmiar czcionki Odstęp wierszy @@ -388,7 +387,6 @@ Wejdź na stronę Przybyto do %s Wyruszono z %s - Generowanie powiadomień Wybierz lokalizację Ustaw wybraną lokalizację Lub wybierz lokalizację diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 64ad079e0..16014cff0 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -243,7 +243,6 @@ Pasta de anexo Pasta de backup Backup no Google Drive - Miscelânea Ativado Tamanho do texto Espaçamento da linha @@ -391,7 +390,6 @@ Visitar website Chegou às %s Saiu às %s - Gerando notificações Escolher uma localização Selecionar essa localização Ou escolha uma localização diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 076aa0c57..2818ac309 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -221,7 +221,6 @@ Horas de silêncio Pasta de anexo Pasta de cópias de segurança - Outras Ativo Tamanho das letras Espaçamento entre linhas @@ -347,7 +346,6 @@ Ou escolha uma localização Selecionar esta localização Escolher uma localização - A gerar notificações Saiu às %s Chegou às %s Visitar site web diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index eca8d8d99..14d9e04fa 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -251,7 +251,6 @@ Dimensiunea fontului Activat Sub-sarcini - Diverse Copie de rezervă Google Drive Dosar de rezervă Fișier atașat @@ -609,7 +608,6 @@ Sau alege o locație Selectează această locație Alege o locație - Generarea de notificări A plecat %s A sosit la %s Vizitează site-ul web diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index a23617d12..a6d95a07c 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -248,7 +248,6 @@ Папка вложений Папка резервных копий Резервное копирование в Google Drive - Прочие настройки Включено Размер шрифта Межстрочный интервал @@ -412,7 +411,6 @@ URL Ошибка: %s ", " - Генерация уведомлений Мой порядок %d подзадача diff --git a/app/src/main/res/values-si/strings.xml b/app/src/main/res/values-si/strings.xml index 9d3990e13..b6f790aeb 100644 --- a/app/src/main/res/values-si/strings.xml +++ b/app/src/main/res/values-si/strings.xml @@ -510,7 +510,6 @@ නැතහොත් ස්ථානයක් තෝරන්න මෙම ස්ථානය තෝරන්න ස්ථානයක් තෝරන්න - දැනුම්දීම් ජනනය කරමින් ඇත %s පිටත් විය වෙබ් අඩවියට පිවිසෙන්න පිටත්වීමේදී මතක් කරන්න @@ -617,7 +616,6 @@ අකුරු ප්‍රමාණය සක්‍රීයයි උප කාර්යයන් - විවිධ ගූගල් ඩ්‍රයිව් උපස්ථය උපස්ථ ෆෝල්ඩරය ඇමුණුම් ෆෝල්ඩරය diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index a07dbbef4..d462ca1ab 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -233,7 +233,6 @@ Priečinok pre prílohy Zalóhovať adresár Kopírovať na Disk Google - Rôzne Povolené Veľkosť písma Šírka riadkov @@ -380,7 +379,6 @@ Navštíviť stránky Príchod o %s Odchod o %s - Všeobecné upozornenia Vybrať polohu Vybrať túto polohu Alebo vybrať polohu ručne diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index d80539f41..e3019cc1e 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -156,7 +156,6 @@ Tyst period Lagringsplats för bilagor Säkerhetskopieringsmapp - Övrigt Aktiverad Teckenstorlek Anpassa redigeringsvyn @@ -382,7 +381,6 @@ Besök hemsida Anlände vid %s Avgick %s - Genererar aviseringar Välj en plats Välj denna plats Eller välj en plats diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index bf97b5a2a..7f9fa9da6 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -221,7 +221,6 @@ அல்லது இருப்பிடத்தைத் தேர்வுசெய்க இந்த இருப்பிடத்தைத் தேர்ந்தெடுக்கவும் இருப்பிடத்தைத் தேர்வுசெய்க - அறிவிப்புகளை உருவாக்குகிறது புறப்பட்டது %s %s இல் வந்து சேர்ந்தது வலைத்தளத்தைப் பார்வையிடவும் @@ -377,7 +376,6 @@ எழுத்துரு அளவு இயக்கப்பட்டது துணை பணிகள் - இதர Google Driveல் நகலெடுக்கவும் காப்பு கோப்புறை இணைப்பு கோப்புறை diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 8ded0d0ce..778afa0d6 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -194,7 +194,6 @@ หรือเลือกตําแหน่งที่ตั้ง เลือกตําแหน่งที่ตั้งนี้ เลือกตําแหน่งที่ตั้ง - การสร้างการแจ้งเตือน ออกเดินทาง %s มาถึง %s เยี่ยมชมเว็บไซต์ @@ -363,7 +362,6 @@ ขนาดแบบอักษร เปิด งานย่อย - เบ็ดเตล็ด การสํารองข้อมูลของ Google ไดรฟ์ โฟลเดอร์สำรอง โฟลเดอร์สิ่งที่แนบมา diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 31742e540..8755aabf5 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -233,7 +233,6 @@ Ek klasörü Yedekleme klasörü Google Drive yedeği - Türlü Etkin Yazı tipi boyutu Satır aralığı @@ -381,7 +380,6 @@ Web siteyi ziyaret et Varıldı: %s Kalkıldı: %s - Bildirimler oluşturuluyor Konum seç Bu konumu seç Ya da konum seç diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 638f8f6b0..d2e754c98 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -248,7 +248,6 @@ Тека з прикріпленими файлами Тека з резервними копіями Резервне копіювання до Google Drive - Різне Увімкнено Розмір шрифту Відстань між рядками @@ -397,7 +396,6 @@ Відвідати сторінку Прибув до %s Відправився з %s - Генерація нагадувань Обрати місце Обрати це місце Або вказати інше місце diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index 247510d43..c13ae20b4 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -288,7 +288,6 @@ فونٹ سائز فعال ذیلی ٹاسکس - متفرقات گوگل ڈرائیو بیک اپ بیک اپ فولڈر اٹیچ منٹ فولڈر @@ -438,7 +437,6 @@ ہر %1$sکو %2$s پر دہراتا ہے، %3$d%4$sپر واقع ہوتا ہے یہ جگہ منتخب کریں جگہ منتخب کریں - نوٹیفیکیشنز بنائیں %s رخصت ہو گیا %s پر پہنچ گیا ویب سائٹس کھولیں diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 11648d688..a1dc991f5 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -247,7 +247,6 @@ Hoặc chọn vị trí Chọn vị trí này Chọn vị trí - Đang tạo các thông báo Đã khởi hành %s Đã đến %s Đi đến trang web @@ -425,7 +424,6 @@ Cỡ chữ Bật Công việc con - Khác Sao lưu Google Drive Thư mục sao lưu Thư mục tệp đính kèm diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 0ce8d5d7d..dff9cd7b3 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -216,7 +216,6 @@ 静音时间 附件文件夹 备份文件夹 - 杂项 启用 字体大小 行间距 @@ -374,7 +373,6 @@ 访问网站 到达 %s 离开 %s - 生成通知 选择一个位置 选中这个位置 或选择一个位置 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index d5e40e38d..460898930 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -131,7 +131,6 @@ 鈴聲 震動 備份資料夾 - 雜項設定 啟用 行距 貢獻翻譯 @@ -379,7 +378,6 @@ 或選擇一個地點 選擇此地點 選擇一個地點 - 產生通知 於 %s 離開 於 %s 到達 訪問網站 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c0992ff0d..86dbe2c43 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -309,7 +309,6 @@ File %1$s contained %2$s.\n\n Attachment folder Backup folder Google Drive backup - Miscellaneous Subtasks Enabled Font size @@ -506,7 +505,6 @@ File %1$s contained %2$s.\n\n Visit website Arrived at %s Departed %s - Generating notifications Choose a location Select this location Or choose a location diff --git a/app/src/test/java/org/tasks/jobs/NotificationQueueTest.kt b/app/src/test/java/org/tasks/jobs/NotificationQueueTest.kt deleted file mode 100644 index a8cc0cf4b..000000000 --- a/app/src/test/java/org/tasks/jobs/NotificationQueueTest.kt +++ /dev/null @@ -1,263 +0,0 @@ -package org.tasks.jobs - -import org.junit.After -import org.junit.Assert.assertEquals -import org.junit.Before -import org.junit.Test -import org.mockito.AdditionalAnswers -import org.mockito.ArgumentMatchers -import org.mockito.Mockito -import org.tasks.Freeze.Companion.freezeAt -import org.tasks.data.Alarm.Companion.TYPE_DATE_TIME -import org.tasks.data.Alarm.Companion.TYPE_SNOOZE -import org.tasks.preferences.Preferences -import org.tasks.time.DateTime -import org.tasks.time.DateTimeUtils -import java.util.concurrent.TimeUnit - -class NotificationQueueTest { - private lateinit var queue: NotificationQueue - private lateinit var workManager: WorkManager - private lateinit var preferences: Preferences - - @Before - fun before() { - preferences = Mockito.mock(Preferences::class.java) - Mockito.`when`(preferences.adjustForQuietHours(ArgumentMatchers.anyLong())) - .then(AdditionalAnswers.returnsFirstArg()) - workManager = Mockito.mock(WorkManager::class.java) - queue = NotificationQueue(preferences, workManager) - } - - @After - fun after() { - Mockito.verifyNoMoreInteractions(workManager) - } - - @Test - fun removeAlarmDoesntAffectOtherAlarm() { - val now = DateTimeUtils.currentTimeMillis() - queue.add(AlarmEntry(1, 1, now, TYPE_DATE_TIME)) - Mockito.verify(workManager).scheduleNotification(now) - queue.add(AlarmEntry(2, 2, now, TYPE_DATE_TIME)) - queue.remove(listOf(AlarmEntry(1, 1, now, TYPE_DATE_TIME))) - freezeAt(now) { - assertEquals( - listOf(AlarmEntry(2, 2, now, TYPE_DATE_TIME)), - queue.overdueJobs - ) - } - } - - @Test - fun removeByTaskDoesntAffectOtherAlarm() { - val now = DateTimeUtils.currentTimeMillis() - queue.add(AlarmEntry(1, 1, now, TYPE_DATE_TIME)) - Mockito.verify(workManager).scheduleNotification(now) - queue.add(AlarmEntry(2, 2, now, TYPE_DATE_TIME)) - queue.cancelForTask(1) - freezeAt(now) { - assertEquals( - listOf(AlarmEntry(2, 2, now, TYPE_DATE_TIME)), - queue.overdueJobs - ) - } - } - - @Test - fun rescheduleForFirstJob() { - queue.add(AlarmEntry(1, 2, 3, TYPE_DATE_TIME)) - Mockito.verify(workManager).scheduleNotification(3) - } - - @Test - fun dontRescheduleForLaterJobs() { - queue.add(AlarmEntry(1, 2, 3, TYPE_DATE_TIME)) - queue.add(AlarmEntry(2, 3, 4, TYPE_DATE_TIME)) - Mockito.verify(workManager).scheduleNotification(3) - } - - @Test - fun rescheduleForNewerJob() { - queue.add(AlarmEntry(1, 1, 2, TYPE_DATE_TIME)) - queue.add(AlarmEntry(1, 1, 1, TYPE_DATE_TIME)) - val order = Mockito.inOrder(workManager) - order.verify(workManager).scheduleNotification(2) - order.verify(workManager).scheduleNotification(1) - } - - @Test - fun rescheduleWhenCancelingOnlyJob() { - queue.add(AlarmEntry(1, 1, 2, TYPE_DATE_TIME)) - queue.cancelForTask(1) - val order = Mockito.inOrder(workManager) - order.verify(workManager).scheduleNotification(2) - order.verify(workManager).cancelNotifications() - } - - @Test - fun rescheduleWhenCancelingFirstJob() { - queue.add(AlarmEntry(1, 1, 1, 0)) - queue.add(AlarmEntry(2, 2, 2, 0)) - queue.cancelForTask(1) - val order = Mockito.inOrder(workManager) - order.verify(workManager).scheduleNotification(1) - order.verify(workManager).scheduleNotification(2) - } - - @Test - fun dontRescheduleWhenCancelingLaterJob() { - queue.add(AlarmEntry(1, 1, 1, 0)) - queue.add(AlarmEntry(2, 2, 2, 0)) - queue.cancelForTask(2) - Mockito.verify(workManager).scheduleNotification(1) - } - - @Test - fun nextScheduledTimeIsZeroWhenQueueIsEmpty() { - Mockito.`when`(preferences.adjustForQuietHours(ArgumentMatchers.anyLong())) - .thenReturn(1234L) - assertEquals(0, queue.nextScheduledTime()) - } - - @Test - fun adjustNextScheduledTimeForQuietHours() { - Mockito.`when`(preferences.adjustForQuietHours(ArgumentMatchers.anyLong())) - .thenReturn(1234L) - queue.add(AlarmEntry(1, 1, 1, TYPE_DATE_TIME)) - Mockito.verify(workManager).scheduleNotification(1234) - } - - @Test - fun overdueJobsAreReturned() { - val now = DateTimeUtils.currentTimeMillis() - queue.add(AlarmEntry(1, 1, now, TYPE_DATE_TIME)) - queue.add(AlarmEntry(2, 1, now + ONE_MINUTE, TYPE_DATE_TIME)) - Mockito.verify(workManager).scheduleNotification(now) - freezeAt(now) { - assertEquals( - listOf(AlarmEntry(1, 1, now, TYPE_DATE_TIME)), queue.overdueJobs - ) - } - } - - @Test - fun twoOverdueJobsAtSameTimeReturned() { - val now = DateTimeUtils.currentTimeMillis() - queue.add(AlarmEntry(1, 1, now, TYPE_DATE_TIME)) - queue.add(AlarmEntry(2, 2, now, TYPE_DATE_TIME)) - Mockito.verify(workManager).scheduleNotification(now) - freezeAt(now) { - assertEquals( - setOf( - AlarmEntry(1, 1, now, TYPE_DATE_TIME), - AlarmEntry(2, 2, now, TYPE_DATE_TIME) - ), - queue.overdueJobs.toSet() - ) - } - } - - @Test - fun twoOverdueJobsAtDifferentTimes() { - val now = DateTimeUtils.currentTimeMillis() - queue.add(AlarmEntry(1, 1, now, TYPE_DATE_TIME)) - queue.add(AlarmEntry(2, 2, now + ONE_MINUTE, TYPE_DATE_TIME)) - Mockito.verify(workManager).scheduleNotification(now) - freezeAt(now + 2 * ONE_MINUTE) { - assertEquals( - listOf( - AlarmEntry(1, 1, now, TYPE_DATE_TIME), - AlarmEntry(2, 2, now + ONE_MINUTE, TYPE_DATE_TIME) - ), - queue.overdueJobs - ) - } - } - - @Test - fun overdueJobsAreRemoved() { - val now = DateTimeUtils.currentTimeMillis() - queue.add(AlarmEntry(1, 1, now, TYPE_DATE_TIME)) - queue.add(AlarmEntry(2, 2, now + ONE_MINUTE, TYPE_DATE_TIME)) - Mockito.verify(workManager).scheduleNotification(now) - freezeAt(now) { - queue.remove(queue.overdueJobs) - } - assertEquals( - listOf(AlarmEntry(2, 2, now + ONE_MINUTE, TYPE_DATE_TIME)), queue.getJobs() - ) - } - - @Test - fun multipleOverduePeriodsLapsed() { - val now = DateTimeUtils.currentTimeMillis() - queue.add(AlarmEntry(1, 1, now, TYPE_DATE_TIME)) - queue.add(AlarmEntry(2, 2, now + ONE_MINUTE, TYPE_DATE_TIME)) - queue.add(AlarmEntry(3, 3, now + 2 * ONE_MINUTE, TYPE_DATE_TIME)) - Mockito.verify(workManager).scheduleNotification(now) - freezeAt(now + ONE_MINUTE) { - queue.remove(queue.overdueJobs) - } - assertEquals( - listOf(AlarmEntry(3, 3, now + 2 * ONE_MINUTE, TYPE_DATE_TIME)), queue.getJobs() - ) - } - - - @Test - fun clearShouldCancelExisting() { - queue.add(AlarmEntry(1, 1, 1, 0)) - queue.clear() - val order = Mockito.inOrder(workManager) - order.verify(workManager).scheduleNotification(1) - order.verify(workManager).cancelNotifications() - assertEquals(0, queue.size()) - } - - @Test - fun ignoreInvalidCancelForByAlarm() { - val now = DateTimeUtils.currentTimeMillis() - queue.add(AlarmEntry(1, 1, now, TYPE_DATE_TIME)) - queue.remove(listOf(AlarmEntry(2, 2, now, TYPE_DATE_TIME))) - Mockito.verify(workManager).scheduleNotification(now) - } - - @Test - fun ignoreInvalidCancelForTask() { - val now = DateTimeUtils.currentTimeMillis() - queue.add(AlarmEntry(1, 1, now, TYPE_DATE_TIME)) - queue.cancelForTask(2) - Mockito.verify(workManager).scheduleNotification(now) - } - - @Test - fun allDuringSameMinuteAreOverdue() { - val now = DateTime(2017, 9, 3, 0, 14, 6, 455) - val due = DateTime(2017, 9, 3, 0, 14, 0, 0) - val snooze = DateTime(2017, 9, 3, 0, 14, 59, 999) - queue.add(AlarmEntry(1, 1, due.millis, TYPE_DATE_TIME)) - queue.add(AlarmEntry(2, 2, snooze.millis, TYPE_SNOOZE)) - queue.add(AlarmEntry(3, 3, due.plusMinutes(1).millis, TYPE_DATE_TIME)) - Mockito.verify(workManager).scheduleNotification(due.millis) - freezeAt(now) { - val overdueJobs = queue.overdueJobs - assertEquals( - listOf( - AlarmEntry(1, 1, due.millis, TYPE_DATE_TIME), - AlarmEntry(2, 2, snooze.millis, TYPE_SNOOZE) - ), - overdueJobs - ) - queue.remove(overdueJobs) - assertEquals( - listOf(AlarmEntry(3, 3, due.plusMinutes(1).millis, TYPE_DATE_TIME)), - queue.getJobs() - ) - } - } - - companion object { - private val ONE_MINUTE = TimeUnit.MINUTES.toMillis(1) - } -} \ No newline at end of file