Add option to notify at start date

pull/1652/head
Alex Baker 3 years ago
parent a0594df42e
commit b13f62a9fb

@ -3,6 +3,7 @@ package com.todoroo.astrid.reminders
import com.natpryce.makeiteasy.MakeItEasy.with import com.natpryce.makeiteasy.MakeItEasy.with
import com.todoroo.andlib.utility.DateUtilities import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.data.Task import com.todoroo.astrid.data.Task
import com.todoroo.astrid.data.Task.Companion.HIDE_UNTIL_DUE
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.UninstallModules import dagger.hilt.android.testing.UninstallModules
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
@ -22,6 +23,7 @@ import org.tasks.makers.TaskMaker.CREATION_TIME
import org.tasks.makers.TaskMaker.DELETION_TIME import org.tasks.makers.TaskMaker.DELETION_TIME
import org.tasks.makers.TaskMaker.DUE_DATE import org.tasks.makers.TaskMaker.DUE_DATE
import org.tasks.makers.TaskMaker.DUE_TIME import org.tasks.makers.TaskMaker.DUE_TIME
import org.tasks.makers.TaskMaker.HIDE_TYPE
import org.tasks.makers.TaskMaker.ID import org.tasks.makers.TaskMaker.ID
import org.tasks.makers.TaskMaker.RANDOM_REMINDER_PERIOD import org.tasks.makers.TaskMaker.RANDOM_REMINDER_PERIOD
import org.tasks.makers.TaskMaker.REMINDERS import org.tasks.makers.TaskMaker.REMINDERS
@ -52,6 +54,19 @@ class ReminderServiceTest : InjectingTestCase() {
service = ReminderService(preferences, jobs, random, taskDao) service = ReminderService(preferences, jobs, random, taskDao)
} }
@Test
fun dontScheduleStartDateReminderWhenFlagNotSet() {
service.scheduleAlarm(
newTask(
with(ID, 1L),
with(HIDE_TYPE, Task.HIDE_UNTIL_DUE),
with(DUE_TIME, DateTimeUtils.newDateTime())
)
)
assertTrue(jobs.isEmpty())
}
@Test @Test
fun dontScheduleDueDateReminderWhenFlagNotSet() { fun dontScheduleDueDateReminderWhenFlagNotSet() {
service.scheduleAlarm(newTask(with(ID, 1L), with(DUE_TIME, DateTimeUtils.newDateTime()))) service.scheduleAlarm(newTask(with(ID, 1L), with(DUE_TIME, DateTimeUtils.newDateTime())))
@ -66,6 +81,34 @@ class ReminderServiceTest : InjectingTestCase() {
assertTrue(jobs.isEmpty()) assertTrue(jobs.isEmpty())
} }
@Test
fun schedulePastStartDate() {
val task = newTask(
with(ID, 1L),
with(DUE_TIME, DateTimeUtils.newDateTime().minusDays(1)),
with(HIDE_TYPE, HIDE_UNTIL_DUE),
with(REMINDERS, Task.NOTIFY_AT_START)
)
service.scheduleAlarm(task)
verify(ReminderEntry(1, task.hideUntil, ReminderService.TYPE_START))
}
@Test
fun scheduleFutureStartDate() {
val task = newTask(
with(ID, 1L),
with(DUE_TIME, DateTimeUtils.newDateTime().plusDays(1)),
with(HIDE_TYPE, HIDE_UNTIL_DUE),
with(REMINDERS, Task.NOTIFY_AT_START)
)
service.scheduleAlarm(task)
verify(ReminderEntry(1, task.hideUntil, ReminderService.TYPE_START))
}
@Test @Test
fun schedulePastDueDate() { fun schedulePastDueDate() {
val task = newTask( val task = newTask(

@ -251,6 +251,9 @@ class Task : Parcelable {
val isNotifyAfterDeadline: Boolean val isNotifyAfterDeadline: Boolean
get() = isReminderFlagSet(NOTIFY_AFTER_DEADLINE) get() = isReminderFlagSet(NOTIFY_AFTER_DEADLINE)
val isNotifyAtStart: Boolean
get() = isReminderFlagSet(NOTIFY_AT_START)
val isNotifyAtDeadline: Boolean val isNotifyAtDeadline: Boolean
get() = isReminderFlagSet(NOTIFY_AT_DEADLINE) get() = isReminderFlagSet(NOTIFY_AT_DEADLINE)
@ -516,6 +519,9 @@ class Task : Parcelable {
/** reminder mode five times (exclusive with non-stop) */ /** reminder mode five times (exclusive with non-stop) */
const val NOTIFY_MODE_FIVE = 1 shl 4 const val NOTIFY_MODE_FIVE = 1 shl 4
const val NOTIFY_AT_START = 1 shl 5
@JvmField val CREATOR: Parcelable.Creator<Task> = object : Parcelable.Creator<Task> { @JvmField val CREATOR: Parcelable.Creator<Task> = object : Parcelable.Creator<Task> {
override fun createFromParcel(source: Parcel): Task? { override fun createFromParcel(source: Parcel): Task? {
return Task(source) return Task(source)

@ -66,6 +66,8 @@ class ReminderService internal constructor(
// random reminders // random reminders
val whenRandom = calculateNextRandomReminder(task) val whenRandom = calculateNextRandomReminder(task)
val whenStartDate = calculateStartDateReminder(task)
// notifications at due date // notifications at due date
val whenDueDate = calculateNextDueDateReminder(task) val whenDueDate = calculateNextDueDateReminder(task)
@ -75,8 +77,14 @@ class ReminderService internal constructor(
// snooze trumps all // snooze trumps all
if (whenSnooze != NO_ALARM) { if (whenSnooze != NO_ALARM) {
return ReminderEntry(taskId, whenSnooze, TYPE_SNOOZE) return ReminderEntry(taskId, whenSnooze, TYPE_SNOOZE)
} else if (whenRandom < whenDueDate && whenRandom < whenOverdue) { } else if (
whenRandom < whenDueDate &&
whenRandom < whenOverdue &&
whenRandom < whenStartDate
) {
return ReminderEntry(taskId, whenRandom, TYPE_RANDOM) return ReminderEntry(taskId, whenRandom, TYPE_RANDOM)
} else if (whenStartDate < whenDueDate) {
return ReminderEntry(taskId, whenStartDate, TYPE_START)
} else if (whenDueDate < whenOverdue) { } else if (whenDueDate < whenOverdue) {
return ReminderEntry(taskId, whenDueDate, TYPE_DUE) return ReminderEntry(taskId, whenDueDate, TYPE_DUE)
} else if (whenOverdue != NO_ALARM) { } else if (whenOverdue != NO_ALARM) {
@ -108,6 +116,21 @@ class ReminderService internal constructor(
return NO_ALARM return NO_ALARM
} }
private fun calculateStartDateReminder(task: Task): Long {
if (task.hasStartDate() && task.isNotifyAtStart) {
val startDate = task.hideUntil
val startDateAlarm = if (task.hasDueTime()) {
startDate
} else {
DateTime(startDate).withMillisOfDay(preferences.defaultDueTime).millis
}
if (task.reminderLast < startDateAlarm) {
return startDateAlarm
}
}
return NO_ALARM
}
/** /**
* Calculate the next alarm time for due date reminders. * Calculate the next alarm time for due date reminders.
* *
@ -122,14 +145,14 @@ class ReminderService internal constructor(
private fun calculateNextDueDateReminder(task: Task): Long { private fun calculateNextDueDateReminder(task: Task): Long {
if (task.hasDueDate() && task.isNotifyAtDeadline) { if (task.hasDueDate() && task.isNotifyAtDeadline) {
val dueDate = task.dueDate val dueDate = task.dueDate
val lastReminder = task.reminderLast val dueDateAlarm = if (task.hasDueTime()) {
val dueDateAlarm: Long
dueDateAlarm = if (task.hasDueTime()) {
dueDate dueDate
} else { } else {
DateTime(dueDate).withMillisOfDay(preferences.defaultDueTime).millis DateTime(dueDate).withMillisOfDay(preferences.defaultDueTime).millis
} }
return if (lastReminder < dueDateAlarm) dueDateAlarm else NO_ALARM if (task.reminderLast < dueDateAlarm) {
return dueDateAlarm
}
} }
return NO_ALARM return NO_ALARM
} }
@ -165,6 +188,7 @@ class ReminderService internal constructor(
const val TYPE_ALARM = 4 const val TYPE_ALARM = 4
const val TYPE_GEOFENCE_ENTER = 5 const val TYPE_GEOFENCE_ENTER = 5
const val TYPE_GEOFENCE_EXIT = 6 const val TYPE_GEOFENCE_EXIT = 6
const val TYPE_START = 7
private const val NO_ALARM = Long.MAX_VALUE private const val NO_ALARM = Long.MAX_VALUE
} }
} }

@ -52,6 +52,9 @@ class ReminderControlSet : TaskEditControlFragment() {
viewModel.ringFiveTimes!! -> setRingMode(1) viewModel.ringFiveTimes!! -> setRingMode(1)
else -> setRingMode(0) else -> setRingMode(0)
} }
if (viewModel.whenStart!!) {
addStart()
}
if (viewModel.whenDue!!) { if (viewModel.whenDue!!) {
addDue() addDue()
} }
@ -97,6 +100,7 @@ class ReminderControlSet : TaskEditControlFragment() {
private fun addAlarm(selected: String) { private fun addAlarm(selected: String) {
when (selected) { when (selected) {
getString(R.string.when_started) -> addStart()
getString(R.string.when_due) -> addDue() getString(R.string.when_due) -> addDue()
getString(R.string.when_overdue) -> addOverdue() getString(R.string.when_overdue) -> addOverdue()
getString(R.string.randomly) -> addRandomReminder(TimeUnit.DAYS.toMillis(14)) getString(R.string.randomly) -> addRandomReminder(TimeUnit.DAYS.toMillis(14))
@ -147,9 +151,9 @@ class ReminderControlSet : TaskEditControlFragment() {
} }
private fun addAlarmRow(timestamp: Long) { private fun addAlarmRow(timestamp: Long) {
addAlarmRow( addAlarmRow(DateUtilities.getLongDateStringWithTime(timestamp, locale.locale)) {
DateUtilities.getLongDateStringWithTime(timestamp, locale.locale), viewModel.selectedAlarms?.remove(timestamp)
View.OnClickListener { viewModel.selectedAlarms?.remove(timestamp) }) }
} }
private fun addNewAlarm() { private fun addNewAlarm() {
@ -180,6 +184,9 @@ class ReminderControlSet : TaskEditControlFragment() {
private val options: List<String> private val options: List<String>
get() { get() {
val options: MutableList<String> = ArrayList() val options: MutableList<String> = ArrayList()
if (viewModel.whenStart != true) {
options.add(getString(R.string.when_started))
}
if (viewModel.whenDue != true) { if (viewModel.whenDue != true) {
options.add(getString(R.string.when_due)) options.add(getString(R.string.when_due))
} }
@ -193,25 +200,32 @@ class ReminderControlSet : TaskEditControlFragment() {
return options return options
} }
private fun addStart() {
viewModel.whenStart = true
addAlarmRow(getString(R.string.when_started)) {
viewModel.whenStart = false
}
}
private fun addDue() { private fun addDue() {
viewModel.whenDue = true viewModel.whenDue = true
addAlarmRow(getString(R.string.when_due), View.OnClickListener { addAlarmRow(getString(R.string.when_due)) {
viewModel.whenDue = false viewModel.whenDue = false
}) }
} }
private fun addOverdue() { private fun addOverdue() {
viewModel.whenOverdue = true viewModel.whenOverdue = true
addAlarmRow(getString(R.string.when_overdue), View.OnClickListener { addAlarmRow(getString(R.string.when_overdue)) {
viewModel.whenOverdue = false viewModel.whenOverdue = false
}) }
} }
private fun addRandomReminder(reminderPeriod: Long) { private fun addRandomReminder(reminderPeriod: Long) {
val alarmRow = addAlarmRow(getString(R.string.randomly_once) + " ", View.OnClickListener { val alarmRow = addAlarmRow(getString(R.string.randomly_once) + " ") {
viewModel.reminderPeriod = 0 viewModel.reminderPeriod = 0
randomControlSet = null randomControlSet = null
}) }
randomControlSet = RandomReminderControlSet(activity, alarmRow, reminderPeriod, viewModel) randomControlSet = RandomReminderControlSet(activity, alarmRow, reminderPeriod, viewModel)
} }

@ -236,6 +236,9 @@ class TaskEditViewModel @Inject constructor(
var selectedAlarms: HashSet<Long>? = null var selectedAlarms: HashSet<Long>? = null
var whenStart: Boolean? = null
get() = field ?: (task?.reminderFlags?.and(Task.NOTIFY_AT_START) ?: 0 > 0)
var whenDue: Boolean? = null var whenDue: Boolean? = null
get() = field ?: (task?.reminderFlags?.and(Task.NOTIFY_AT_DEADLINE) ?: 0 > 0) get() = field ?: (task?.reminderFlags?.and(Task.NOTIFY_AT_DEADLINE) ?: 0 > 0)
@ -416,6 +419,9 @@ class TaskEditViewModel @Inject constructor(
private fun getReminderFlags(): Int { private fun getReminderFlags(): Int {
var value = 0 var value = 0
if (whenStart == true) {
value = value or Task.NOTIFY_AT_START
}
if (whenDue == true) { if (whenDue == true) {
value = value or Task.NOTIFY_AT_DEADLINE value = value or Task.NOTIFY_AT_DEADLINE
} }

@ -340,6 +340,7 @@ File %1$s contained %2$s.\n\n
<string name="pick_a_date_and_time">Pick a date and time</string> <string name="pick_a_date_and_time">Pick a date and time</string>
<string name="when_overdue">When overdue</string> <string name="when_overdue">When overdue</string>
<string name="when_due">When due</string> <string name="when_due">When due</string>
<string name="when_started">When started</string>
<string name="geofence_radius">Radius</string> <string name="geofence_radius">Radius</string>
<string name="location_radius_meters">%s m</string> <string name="location_radius_meters">%s m</string>
<string name="tags">Tags</string> <string name="tags">Tags</string>

Loading…
Cancel
Save