mirror of https://github.com/tasks/tasks
Convert random and snooze reminders to alarms
Display snooze time in edit screenpull/1769/head
parent
67899e6fff
commit
cb834a9818
@ -1,156 +0,0 @@
|
||||
package com.todoroo.astrid.reminders
|
||||
|
||||
import com.natpryce.makeiteasy.MakeItEasy.with
|
||||
import com.todoroo.andlib.utility.DateUtilities
|
||||
import com.todoroo.astrid.data.Task
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import dagger.hilt.android.testing.UninstallModules
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.tasks.Freeze.Companion.freezeClock
|
||||
import org.tasks.data.TaskDao
|
||||
import org.tasks.date.DateTimeUtils.newDateTime
|
||||
import org.tasks.injection.InjectingTestCase
|
||||
import org.tasks.injection.ProductionModule
|
||||
import org.tasks.jobs.NotificationQueue
|
||||
import org.tasks.jobs.ReminderEntry
|
||||
import org.tasks.makers.TaskMaker.CREATION_TIME
|
||||
import org.tasks.makers.TaskMaker.DUE_TIME
|
||||
import org.tasks.makers.TaskMaker.ID
|
||||
import org.tasks.makers.TaskMaker.RANDOM_REMINDER_PERIOD
|
||||
import org.tasks.makers.TaskMaker.REMINDERS
|
||||
import org.tasks.makers.TaskMaker.REMINDER_LAST
|
||||
import org.tasks.makers.TaskMaker.SNOOZE_TIME
|
||||
import org.tasks.makers.TaskMaker.newTask
|
||||
import org.tasks.preferences.Preferences
|
||||
import org.tasks.reminders.Random
|
||||
import org.tasks.time.DateTime
|
||||
import javax.inject.Inject
|
||||
|
||||
@UninstallModules(ProductionModule::class)
|
||||
@HiltAndroidTest
|
||||
class ReminderServiceTest : InjectingTestCase() {
|
||||
@Inject lateinit var preferences: Preferences
|
||||
@Inject lateinit var taskDao: TaskDao
|
||||
@Inject lateinit var jobs: NotificationQueue
|
||||
|
||||
private lateinit var service: ReminderService
|
||||
private lateinit var random: RandomStub
|
||||
|
||||
@Before
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
random = RandomStub()
|
||||
preferences.clear()
|
||||
service = ReminderService(jobs, random, taskDao)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ignoreStaleSnoozeTime() {
|
||||
val task = newTask(
|
||||
with(ID, 1L),
|
||||
with(DUE_TIME, newDateTime()),
|
||||
with(SNOOZE_TIME, newDateTime().minusMinutes(5)),
|
||||
with(REMINDER_LAST, newDateTime().minusMinutes(4))
|
||||
)
|
||||
|
||||
service.scheduleAlarm(task)
|
||||
|
||||
assertTrue(jobs.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dontIgnoreMissedSnoozeTime() {
|
||||
val dueDate = newDateTime()
|
||||
val task = newTask(
|
||||
with(ID, 1L),
|
||||
with(DUE_TIME, dueDate),
|
||||
with(SNOOZE_TIME, dueDate.minusMinutes(4)),
|
||||
with(REMINDER_LAST, dueDate.minusMinutes(5)),
|
||||
with(REMINDERS, Task.NOTIFY_AT_DEADLINE))
|
||||
|
||||
service.scheduleAlarm(task)
|
||||
|
||||
verify(ReminderEntry(1, task.reminderSnooze, ReminderService.TYPE_SNOOZE))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun scheduleInitialRandomReminder() {
|
||||
random.seed = 0.3865f
|
||||
|
||||
freezeClock {
|
||||
val now = newDateTime()
|
||||
val task = newTask(
|
||||
with(ID, 1L),
|
||||
with(REMINDER_LAST, null as DateTime?),
|
||||
with(CREATION_TIME, now.minusDays(1)),
|
||||
with(RANDOM_REMINDER_PERIOD, DateUtilities.ONE_WEEK))
|
||||
|
||||
service.scheduleAlarm(task)
|
||||
|
||||
verify(ReminderEntry(1L, now.minusDays(1).millis + 584206592, ReminderService.TYPE_RANDOM))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun scheduleNextRandomReminder() {
|
||||
random.seed = 0.3865f
|
||||
|
||||
freezeClock {
|
||||
val now = newDateTime()
|
||||
val task = newTask(
|
||||
with(ID, 1L),
|
||||
with(REMINDER_LAST, now.minusDays(1)),
|
||||
with(CREATION_TIME, now.minusDays(30)),
|
||||
with(RANDOM_REMINDER_PERIOD, DateUtilities.ONE_WEEK))
|
||||
|
||||
service.scheduleAlarm(task)
|
||||
|
||||
verify(ReminderEntry(1L, now.minusDays(1).millis + 584206592, ReminderService.TYPE_RANDOM))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun scheduleOverdueRandomReminder() {
|
||||
random.seed = 0.3865f
|
||||
|
||||
freezeClock {
|
||||
val now = newDateTime()
|
||||
val task = newTask(
|
||||
with(ID, 1L),
|
||||
with(REMINDER_LAST, now.minusDays(14)),
|
||||
with(CREATION_TIME, now.minusDays(30)),
|
||||
with(RANDOM_REMINDER_PERIOD, DateUtilities.ONE_WEEK))
|
||||
|
||||
service.scheduleAlarm(task)
|
||||
|
||||
verify(ReminderEntry(1L, now.millis + 10148400, ReminderService.TYPE_RANDOM))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun snoozeOverridesAll() {
|
||||
val now = newDateTime()
|
||||
val task = newTask(
|
||||
with(ID, 1L),
|
||||
with(DUE_TIME, now),
|
||||
with(SNOOZE_TIME, now.plusMonths(12)),
|
||||
with(REMINDERS, Task.NOTIFY_AT_DEADLINE or Task.NOTIFY_AFTER_DEADLINE),
|
||||
with(RANDOM_REMINDER_PERIOD, DateUtilities.ONE_HOUR))
|
||||
|
||||
service.scheduleAlarm(task)
|
||||
|
||||
verify(ReminderEntry(1, now.plusMonths(12).millis, ReminderService.TYPE_SNOOZE))
|
||||
}
|
||||
|
||||
private fun verify(vararg reminders: ReminderEntry) = assertEquals(reminders.toList(), jobs.getJobs())
|
||||
|
||||
internal class RandomStub : Random() {
|
||||
var seed = 1.0f
|
||||
|
||||
override fun nextFloat() = seed
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package org.tasks.makers
|
||||
|
||||
import com.natpryce.makeiteasy.Instantiator
|
||||
import com.natpryce.makeiteasy.Property
|
||||
import com.natpryce.makeiteasy.Property.newProperty
|
||||
import com.natpryce.makeiteasy.PropertyLookup
|
||||
import com.natpryce.makeiteasy.PropertyValue
|
||||
import org.tasks.data.Alarm.Companion.TYPE_DATE_TIME
|
||||
import org.tasks.date.DateTimeUtils.newDateTime
|
||||
import org.tasks.jobs.AlarmEntry
|
||||
import org.tasks.makers.Maker.make
|
||||
import org.tasks.time.DateTime
|
||||
|
||||
object AlarmEntryMaker {
|
||||
val ID: Property<AlarmEntry, Long> = newProperty()
|
||||
val TASK: Property<AlarmEntry, Long> = newProperty()
|
||||
val TIME: Property<AlarmEntry, DateTime> = newProperty()
|
||||
val TYPE: Property<AlarmEntry, Int> = newProperty()
|
||||
|
||||
private val instantiator = Instantiator { lookup: PropertyLookup<AlarmEntry> ->
|
||||
AlarmEntry(
|
||||
lookup.valueOf(ID, 0L),
|
||||
lookup.valueOf(TASK, 0L),
|
||||
lookup.valueOf(TIME, newDateTime()).millis,
|
||||
lookup.valueOf(TYPE, TYPE_DATE_TIME)
|
||||
)
|
||||
}
|
||||
|
||||
fun newAlarmEntry(vararg properties: PropertyValue<in AlarmEntry?, *>): AlarmEntry {
|
||||
return make(instantiator, *properties)
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package com.todoroo.astrid.alarms
|
||||
|
||||
import com.todoroo.andlib.utility.DateUtilities
|
||||
import com.todoroo.astrid.data.Task
|
||||
import org.tasks.data.Alarm
|
||||
import org.tasks.jobs.AlarmEntry
|
||||
import org.tasks.preferences.Preferences
|
||||
import org.tasks.reminders.Random
|
||||
import org.tasks.time.DateTimeUtils.withMillisOfDay
|
||||
import javax.inject.Inject
|
||||
|
||||
class AlarmCalculator(
|
||||
private val random: Random,
|
||||
private val defaultTimeProvider: () -> Int,
|
||||
){
|
||||
@Inject
|
||||
internal constructor(
|
||||
preferences: Preferences
|
||||
) : this(Random(), { preferences.defaultDueTime })
|
||||
|
||||
fun toAlarmEntry(task: Task, alarm: Alarm): AlarmEntry? {
|
||||
val trigger = when (alarm.type) {
|
||||
Alarm.TYPE_SNOOZE,
|
||||
Alarm.TYPE_DATE_TIME ->
|
||||
alarm.time
|
||||
Alarm.TYPE_REL_START ->
|
||||
when {
|
||||
task.hasStartTime() ->
|
||||
task.hideUntil + alarm.time
|
||||
task.hasStartDate() ->
|
||||
task.hideUntil.withMillisOfDay(defaultTimeProvider()) + alarm.time
|
||||
else ->
|
||||
AlarmService.NO_ALARM
|
||||
}
|
||||
Alarm.TYPE_REL_END ->
|
||||
when {
|
||||
task.hasDueTime() ->
|
||||
task.dueDate + alarm.time
|
||||
task.hasDueDate() ->
|
||||
task.dueDate.withMillisOfDay(defaultTimeProvider()) + alarm.time
|
||||
else ->
|
||||
AlarmService.NO_ALARM
|
||||
}
|
||||
Alarm.TYPE_RANDOM ->
|
||||
calculateNextRandomReminder(random, task, alarm.time)
|
||||
else ->
|
||||
AlarmService.NO_ALARM
|
||||
}
|
||||
return when {
|
||||
trigger <= AlarmService.NO_ALARM ->
|
||||
null
|
||||
trigger > task.reminderLast || alarm.type == Alarm.TYPE_SNOOZE ->
|
||||
AlarmEntry(alarm.id, alarm.task, trigger, alarm.type)
|
||||
alarm.repeat > 0 -> {
|
||||
val past = (task.reminderLast - trigger) / alarm.interval
|
||||
val next = trigger + (past + 1) * alarm.interval
|
||||
if (past < alarm.repeat && next > task.reminderLast) {
|
||||
AlarmEntry(alarm.id, alarm.task, next, alarm.type)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
else ->
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/** Schedules alarms for a single task */
|
||||
/**
|
||||
* Calculate the next alarm time for random reminders.
|
||||
*
|
||||
*
|
||||
* We take the last reminder time and add approximately the reminder period. If it's still in
|
||||
* the past, we set it to some time in the near future.
|
||||
*/
|
||||
private fun calculateNextRandomReminder(random: Random, task: Task, reminderPeriod: Long): Long {
|
||||
if (reminderPeriod > 0) {
|
||||
var `when` = task.reminderLast
|
||||
if (`when` == 0L) {
|
||||
`when` = task.creationDate
|
||||
}
|
||||
`when` += (reminderPeriod * (0.85f + 0.3f * random.nextFloat())).toLong()
|
||||
if (`when` < DateUtilities.now()) {
|
||||
`when` =
|
||||
DateUtilities.now() + ((0.5f + 6 * random.nextFloat()) * DateUtilities.ONE_HOUR).toLong()
|
||||
}
|
||||
return `when`
|
||||
}
|
||||
return AlarmService.NO_ALARM
|
||||
}
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Todoroo Inc
|
||||
*
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.astrid.reminders
|
||||
|
||||
import com.todoroo.andlib.utility.DateUtilities
|
||||
import com.todoroo.astrid.data.Task
|
||||
import org.tasks.data.TaskDao
|
||||
import org.tasks.jobs.NotificationQueue
|
||||
import org.tasks.jobs.ReminderEntry
|
||||
import org.tasks.reminders.Random
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class ReminderService internal constructor(
|
||||
private val jobs: NotificationQueue,
|
||||
private val random: Random,
|
||||
private val taskDao: TaskDao,
|
||||
) {
|
||||
|
||||
@Inject
|
||||
internal constructor(
|
||||
notificationQueue: NotificationQueue,
|
||||
taskDao: TaskDao
|
||||
) : this(notificationQueue, Random(), taskDao)
|
||||
|
||||
suspend fun scheduleAlarm(id: Long) = scheduleAllAlarms(listOf(id))
|
||||
|
||||
suspend fun scheduleAllAlarms(taskIds: List<Long>) = scheduleAlarms(taskDao.fetch(taskIds))
|
||||
|
||||
suspend fun scheduleAllAlarms() = scheduleAlarms(taskDao.getTasksWithReminders())
|
||||
|
||||
fun scheduleAlarm(task: Task) = scheduleAlarms(listOf(task))
|
||||
|
||||
private fun scheduleAlarms(tasks: List<Task>) =
|
||||
tasks
|
||||
.mapNotNull { getReminderEntry(it) }
|
||||
.let { jobs.add(it) }
|
||||
|
||||
fun cancelReminder(taskId: Long) {
|
||||
jobs.cancelReminder(taskId)
|
||||
}
|
||||
|
||||
private fun getReminderEntry(task: Task?): ReminderEntry? {
|
||||
if (task == null || !task.isSaved) {
|
||||
return null
|
||||
}
|
||||
val taskId = task.id
|
||||
|
||||
// Make sure no alarms are scheduled other than the next one. When that one is shown, it
|
||||
// will schedule the next one after it, and so on and so forth.
|
||||
cancelReminder(taskId)
|
||||
if (task.isCompleted || task.isDeleted) {
|
||||
return null
|
||||
}
|
||||
|
||||
// snooze reminder
|
||||
val whenSnooze = calculateNextSnoozeReminder(task)
|
||||
|
||||
// random reminders
|
||||
val whenRandom = calculateNextRandomReminder(task)
|
||||
|
||||
// snooze trumps all
|
||||
return when {
|
||||
whenSnooze != NO_ALARM -> ReminderEntry(taskId, whenSnooze, TYPE_SNOOZE)
|
||||
whenRandom != NO_ALARM -> ReminderEntry(taskId, whenRandom, TYPE_RANDOM)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateNextSnoozeReminder(task: Task): Long {
|
||||
return if (task.reminderSnooze > task.reminderLast) {
|
||||
task.reminderSnooze
|
||||
} else NO_ALARM
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the next alarm time for random reminders.
|
||||
*
|
||||
*
|
||||
* We take the last reminder time and add approximately the reminder period. If it's still in
|
||||
* the past, we set it to some time in the near future.
|
||||
*/
|
||||
private fun calculateNextRandomReminder(task: Task): Long {
|
||||
val reminderPeriod = task.reminderPeriod
|
||||
if (reminderPeriod > 0) {
|
||||
var `when` = task.reminderLast
|
||||
if (`when` == 0L) {
|
||||
`when` = task.creationDate
|
||||
}
|
||||
`when` += (reminderPeriod * (0.85f + 0.3f * random.nextFloat())).toLong()
|
||||
if (`when` < DateUtilities.now()) {
|
||||
`when` = DateUtilities.now() + ((0.5f + 6 * random.nextFloat()) * DateUtilities.ONE_HOUR).toLong()
|
||||
}
|
||||
return `when`
|
||||
}
|
||||
return NO_ALARM
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TYPE_DUE = 0
|
||||
const val TYPE_OVERDUE = 1
|
||||
const val TYPE_RANDOM = 2
|
||||
const val TYPE_SNOOZE = 3
|
||||
const val TYPE_ALARM = 4
|
||||
const val TYPE_GEOFENCE_ENTER = 5
|
||||
const val TYPE_GEOFENCE_EXIT = 6
|
||||
const val TYPE_START = 7
|
||||
private const val NO_ALARM = Long.MAX_VALUE
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package org.tasks.jobs;
|
||||
|
||||
import org.tasks.notifications.Notification;
|
||||
|
||||
public interface NotificationQueueEntry {
|
||||
|
||||
long getId();
|
||||
|
||||
long getTime();
|
||||
|
||||
Notification toNotification();
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
package org.tasks.jobs;
|
||||
|
||||
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
|
||||
import static org.tasks.time.DateTimeUtils.printTimestamp;
|
||||
|
||||
import org.tasks.notifications.Notification;
|
||||
|
||||
public class ReminderEntry implements NotificationQueueEntry {
|
||||
|
||||
private final long taskId;
|
||||
private final long time;
|
||||
private final int type;
|
||||
|
||||
public ReminderEntry(long taskId, long time, int type) {
|
||||
this.taskId = taskId;
|
||||
this.time = time;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return taskId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Notification toNotification() {
|
||||
Notification notification = new Notification();
|
||||
notification.setTaskId(taskId);
|
||||
notification.setType(type);
|
||||
notification.setTimestamp(currentTimeMillis());
|
||||
return notification;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ReminderEntry reminderEntry = (ReminderEntry) o;
|
||||
|
||||
if (taskId != reminderEntry.taskId) {
|
||||
return false;
|
||||
}
|
||||
if (time != reminderEntry.time) {
|
||||
return false;
|
||||
}
|
||||
return type == reminderEntry.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = (int) (taskId ^ (taskId >>> 32));
|
||||
result = 31 * result + (int) (time ^ (time >>> 32));
|
||||
result = 31 * result + type;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ReminderEntry{" + "taskId=" + taskId + ", time=" + printTimestamp(time) + ", type=" + type + '}';
|
||||
}
|
||||
}
|
@ -0,0 +1,364 @@
|
||||
package com.todoroo.astrid.alarms
|
||||
|
||||
import com.natpryce.makeiteasy.MakeItEasy.with
|
||||
import com.todoroo.andlib.utility.DateUtilities.ONE_WEEK
|
||||
import com.todoroo.astrid.data.Task
|
||||
import com.todoroo.astrid.data.Task.Companion.HIDE_UNTIL_DUE
|
||||
import com.todoroo.astrid.data.Task.Companion.HIDE_UNTIL_DUE_TIME
|
||||
import com.todoroo.astrid.data.Task.Companion.URGENCY_SPECIFIC_DAY_TIME
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.tasks.Freeze.Companion.freezeAt
|
||||
import org.tasks.data.Alarm
|
||||
import org.tasks.data.Alarm.Companion.TYPE_DATE_TIME
|
||||
import org.tasks.data.Alarm.Companion.TYPE_RANDOM
|
||||
import org.tasks.data.Alarm.Companion.TYPE_REL_END
|
||||
import org.tasks.data.Alarm.Companion.TYPE_REL_START
|
||||
import org.tasks.data.Alarm.Companion.TYPE_SNOOZE
|
||||
import org.tasks.data.Alarm.Companion.whenDue
|
||||
import org.tasks.data.Alarm.Companion.whenOverdue
|
||||
import org.tasks.data.Alarm.Companion.whenStarted
|
||||
import org.tasks.date.DateTimeUtils.newDateTime
|
||||
import org.tasks.date.DateTimeUtils.toDateTime
|
||||
import org.tasks.makers.AlarmEntryMaker.TIME
|
||||
import org.tasks.makers.AlarmEntryMaker.TYPE
|
||||
import org.tasks.makers.AlarmEntryMaker.newAlarmEntry
|
||||
import org.tasks.makers.TaskMaker.CREATION_TIME
|
||||
import org.tasks.makers.TaskMaker.DUE_DATE
|
||||
import org.tasks.makers.TaskMaker.DUE_TIME
|
||||
import org.tasks.makers.TaskMaker.HIDE_TYPE
|
||||
import org.tasks.makers.TaskMaker.REMINDER_LAST
|
||||
import org.tasks.makers.TaskMaker.newTask
|
||||
import org.tasks.reminders.Random
|
||||
import org.tasks.time.DateTime
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.TimeUnit.DAYS
|
||||
import java.util.concurrent.TimeUnit.MINUTES
|
||||
|
||||
class AlarmCalculatorTest {
|
||||
private lateinit var random: RandomStub
|
||||
private lateinit var alarmCalculator: AlarmCalculator
|
||||
private val now = newDateTime()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
random = RandomStub()
|
||||
alarmCalculator = AlarmCalculator(random) {
|
||||
TimeUnit.HOURS.toMillis(13).toInt()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ignoreOldReminder() {
|
||||
assertNull(
|
||||
alarmCalculator.toAlarmEntry(
|
||||
newTask(with(REMINDER_LAST, now)),
|
||||
Alarm(0L, now.millis, TYPE_DATE_TIME)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dateTimeReminder() {
|
||||
val alarm = alarmCalculator.toAlarmEntry(
|
||||
newTask(with(REMINDER_LAST, now)),
|
||||
Alarm(0L, now.millis + 1, TYPE_DATE_TIME)
|
||||
)
|
||||
|
||||
assertEquals(newAlarmEntry(with(TIME, now.plusMillis(1)), with(TYPE, TYPE_DATE_TIME)), alarm)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dontIgnoreOldSnooze() {
|
||||
val alarm = alarmCalculator.toAlarmEntry(
|
||||
newTask(with(REMINDER_LAST, now)),
|
||||
Alarm(0L, now.millis, TYPE_SNOOZE)
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
newAlarmEntry(with(TIME, now), with(TYPE, TYPE_SNOOZE)),
|
||||
alarm
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun scheduleReminderAtDefaultDue() {
|
||||
val alarm = alarmCalculator.toAlarmEntry(newTask(with(DUE_DATE, now)), whenDue(0L))
|
||||
|
||||
assertEquals(
|
||||
newAlarmEntry(
|
||||
with(TIME, now.startOfDay().withHourOfDay(13)),
|
||||
with(TYPE, TYPE_REL_END)
|
||||
),
|
||||
alarm
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun scheduleReminderAtDefaultDueTime() = runBlocking {
|
||||
val alarm = alarmCalculator.toAlarmEntry(newTask(with(DUE_TIME, now)), whenDue(0L))
|
||||
|
||||
assertEquals(
|
||||
newAlarmEntry(
|
||||
with(TIME, now.startOfMinute().plusMillis(1000)), with(TYPE, TYPE_REL_END)
|
||||
),
|
||||
alarm
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun scheduleReminderAtDefaultStart() = runBlocking {
|
||||
val alarm = alarmCalculator.toAlarmEntry(
|
||||
newTask(with(DUE_DATE, now), with(HIDE_TYPE, HIDE_UNTIL_DUE)),
|
||||
whenStarted(0L)
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
newAlarmEntry(
|
||||
with(TIME, now.startOfDay().withHourOfDay(13)),
|
||||
with(TYPE, TYPE_REL_START)
|
||||
),
|
||||
alarm
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun scheduleReminerAtDefaultStartTime() = runBlocking {
|
||||
val alarm = alarmCalculator.toAlarmEntry(
|
||||
newTask(with(DUE_TIME, now), with(HIDE_TYPE, HIDE_UNTIL_DUE_TIME)),
|
||||
whenStarted(0L)
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
newAlarmEntry(
|
||||
with(TIME, now.startOfMinute().plusMillis(1000)),
|
||||
with(TYPE, TYPE_REL_START)
|
||||
),
|
||||
alarm
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun scheduleRelativeAfterDue() {
|
||||
val alarm = alarmCalculator.toAlarmEntry(
|
||||
newTask(with(DUE_DATE, now)),
|
||||
Alarm(0L, DAYS.toMillis(1), TYPE_REL_END)
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
newAlarmEntry(
|
||||
with(TIME, now.plusDays(1).startOfDay().withHourOfDay(13)),
|
||||
with(TYPE, TYPE_REL_END)
|
||||
),
|
||||
alarm
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun scheduleRelativeAfterDueTime() = runBlocking {
|
||||
val alarm = alarmCalculator.toAlarmEntry(
|
||||
newTask(with(DUE_TIME, now)),
|
||||
Alarm(0, DAYS.toMillis(1), TYPE_REL_END)
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
newAlarmEntry(
|
||||
with(TIME, now.plusDays(1).startOfMinute().plusMillis(1000)),
|
||||
with(TYPE, TYPE_REL_END)
|
||||
),
|
||||
alarm
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun scheduleRelativeAfterStart() = runBlocking {
|
||||
val alarm = alarmCalculator.toAlarmEntry(
|
||||
newTask(with(DUE_DATE, now), with(HIDE_TYPE, HIDE_UNTIL_DUE)),
|
||||
Alarm(0, DAYS.toMillis(1), TYPE_REL_START)
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
newAlarmEntry(
|
||||
with(TIME, now.plusDays(1).startOfDay().withHourOfDay(13)),
|
||||
with(TYPE, TYPE_REL_START)
|
||||
),
|
||||
alarm
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun scheduleRelativeAfterStartTime() = runBlocking {
|
||||
val alarm = alarmCalculator.toAlarmEntry(
|
||||
newTask(with(DUE_TIME, now), with(HIDE_TYPE, HIDE_UNTIL_DUE_TIME)),
|
||||
Alarm(0, DAYS.toMillis(1), TYPE_REL_START)
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
newAlarmEntry(
|
||||
with(TIME, now.plusDays(1).startOfMinute().plusMillis(1000)),
|
||||
with(TYPE, TYPE_REL_START)
|
||||
),
|
||||
alarm
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun scheduleFirstRepeatReminder() = runBlocking {
|
||||
val alarm = alarmCalculator.toAlarmEntry(
|
||||
newTask(with(DUE_TIME, now), with(REMINDER_LAST, now.plusMinutes(4))),
|
||||
Alarm(0, 0, TYPE_REL_END, 1, MINUTES.toMillis(5))
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
newAlarmEntry(
|
||||
with(TIME, now.plusMinutes(5).startOfMinute().plusMillis(1000)),
|
||||
with(TYPE, TYPE_REL_END)
|
||||
),
|
||||
alarm
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun scheduleSecondRepeatReminder() = runBlocking {
|
||||
val alarm = alarmCalculator.toAlarmEntry(
|
||||
newTask(with(DUE_TIME, now), with(REMINDER_LAST, now.plusMinutes(6))),
|
||||
Alarm(0, 0, TYPE_REL_END, 2, MINUTES.toMillis(5))
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
newAlarmEntry(
|
||||
with(TIME, now.plusMinutes(10).startOfMinute().plusMillis(1000)),
|
||||
with(TYPE, TYPE_REL_END)
|
||||
),
|
||||
alarm
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun terminateRepeatReminder() = runBlocking {
|
||||
val alarm = alarmCalculator.toAlarmEntry(
|
||||
newTask(with(DUE_TIME, now), with(REMINDER_LAST, now.plusMinutes(10))),
|
||||
Alarm(0L, 0, TYPE_REL_END, 2, MINUTES.toMillis(5))
|
||||
)
|
||||
|
||||
assertNull(alarm)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dontScheduleRelativeEndWithNoEnd() = runBlocking {
|
||||
assertNull(alarmCalculator.toAlarmEntry(newTask(), whenDue(0L)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dontScheduleRelativeStartWithNoStart() = runBlocking {
|
||||
assertNull(
|
||||
alarmCalculator.toAlarmEntry(
|
||||
newTask(with(DUE_DATE, newDateTime())),
|
||||
whenStarted(0L)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun reminderOverdueEveryDay() = runBlocking {
|
||||
val dueDate =
|
||||
Task.createDueDate(URGENCY_SPECIFIC_DAY_TIME, DateTime(2022, 1, 30, 13, 30).millis)
|
||||
.toDateTime()
|
||||
val alarm = alarmCalculator.toAlarmEntry(
|
||||
newTask(with(DUE_TIME, dueDate), with(REMINDER_LAST, dueDate.plusDays(6))),
|
||||
whenOverdue(0L)
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
newAlarmEntry(with(TIME, dueDate.plusDays(7)), with(TYPE, TYPE_REL_END)),
|
||||
alarm
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun endDailyOverdueReminder() = runBlocking {
|
||||
val dueDate =
|
||||
Task.createDueDate(URGENCY_SPECIFIC_DAY_TIME, DateTime(2022, 1, 30, 13, 30).millis)
|
||||
.toDateTime()
|
||||
val alarm = alarmCalculator.toAlarmEntry(
|
||||
newTask(with(DUE_TIME, dueDate), with(REMINDER_LAST, dueDate.plusDays(7))),
|
||||
whenOverdue(0L)
|
||||
)
|
||||
|
||||
assertNull(alarm)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun scheduleOverdueRandomReminder() {
|
||||
random.seed = 0.3865f
|
||||
freezeAt(now) {
|
||||
val alarm = alarmCalculator.toAlarmEntry(
|
||||
newTask(
|
||||
with(REMINDER_LAST, now.minusDays(14)),
|
||||
with(CREATION_TIME, now.minusDays(30)),
|
||||
),
|
||||
Alarm(0L, ONE_WEEK, TYPE_RANDOM)
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
newAlarmEntry(with(TIME, now.plusMillis(10148400)), with(TYPE, TYPE_RANDOM)),
|
||||
alarm
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun scheduleInitialRandomReminder() {
|
||||
random.seed = 0.3865f
|
||||
|
||||
freezeAt(now) {
|
||||
val alarm = alarmCalculator.toAlarmEntry(
|
||||
newTask(
|
||||
with(REMINDER_LAST, null as DateTime?),
|
||||
with(CREATION_TIME, now.minusDays(1)),
|
||||
),
|
||||
Alarm(0L, ONE_WEEK, TYPE_RANDOM)
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
newAlarmEntry(
|
||||
with(TIME, now.minusDays(1).plusMillis(584206592)),
|
||||
with(TYPE, TYPE_RANDOM)
|
||||
),
|
||||
alarm
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun scheduleNextRandomReminder() {
|
||||
random.seed = 0.3865f
|
||||
|
||||
freezeAt(now) {
|
||||
val alarm = alarmCalculator.toAlarmEntry(
|
||||
newTask(
|
||||
with(REMINDER_LAST, now.minusDays(1)),
|
||||
with(CREATION_TIME, now.minusDays(30)),
|
||||
),
|
||||
Alarm(0L, ONE_WEEK, TYPE_RANDOM)
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
newAlarmEntry(
|
||||
with(TIME, now.minusDays(1).plusMillis(584206592)),
|
||||
with(TYPE, TYPE_RANDOM)
|
||||
),
|
||||
alarm
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal class RandomStub : Random() {
|
||||
var seed = 1.0f
|
||||
|
||||
override fun nextFloat() = seed
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue